ashleydawson/doctrine-flysystem-bundle

为 Symfony 2 中的 Doctrine 实体添加 flysystem 存储行为(特性)

0.8.7 2016-10-26 09:39 UTC

This package is auto-updated.

Last update: 2024-09-20 22:17:58 UTC


README

Build Status

knpbundles.com

在 Symfony 2 中为 Doctrine 实体添加 flysystem 存储行为

要求

 >= PHP 5.4
 >= Symfony Framework 2.3

Doctrine 支持

  • Doctrine ORM 支持 - 完整
  • Doctrine ODM 支持 - 不完整

简介

我创建了这个包来扩展 Flysystem 文件系统抽象。实际上,这个库扩展了为 Symfony 2 定制的 FlysystemBundle

此包在 Doctrine 实体上实现了一个“上传文件”处理程序,允许 Flysystem 将文件作为 Doctrine 实体生命周期的一部分进行存储。

该包的主要类是一个 特性,它可以应用于任何 Doctrine 实体,使 Flysystem 存储处理程序能够持久化文件细节与实体一起。

安装

您可以通过 Composer 安装 Doctrine Flysystem Bundle。为此,只需在您的 composer.json 文件中添加该包,如下所示

{
    "require": {
        "ashleydawson/doctrine-flysystem-bundle": "0.8.*"
    }
}

运行 composer update 以安装包。然后您需要在您的 app/AppKernel.php 文件中注册该包。下面的第一个示例使用 MultiBundle 库来注册包及其依赖项。有关更多信息,请参阅 MultiBundle 文档

// app/AppKernel.php

$bundles = array(
    // ...,
);

// Do this after the production bundles are set
\AshleyDawson\DoctrineFlysystemBundle\AshleyDawsonDoctrineFlysystemBundle::registerInto($bundles);

// ...

或者您可以这样做(传统方式)

// app/AppKernel.php

// ...

$bundles = array(
    // ...
    new Oneup\FlysystemBundle\OneupFlysystemBundle(), // Doctrine Flysystem Bundle depends on this
    new AshleyDawson\DoctrineFlysystemBundle\AshleyDawsonDoctrineFlysystemBundle(),
);

// ...

配置

接下来,您需要配置至少一个文件系统来存储文件。以下是一个示例,但更好的示例可以在 FlysystemBundle 文档 中找到。

# app/config/config.yml
oneup_flysystem:
    adapters:
        my_adapter:
            local:
                directory: %kernel.root_dir%/cache

    filesystems:
        my_filesystem:
            adapter: my_adapter
            mount: my_filesystem_mount_name

注意:此行 mount: my_filesystem_mount_name 很重要,因为此包使用在此定义的挂载前缀来引用文件系统

用法

为了使用此包,您必须将给定的特性应用于您希望存储上传文件的实体。

<?php

namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use AshleyDawson\DoctrineFlysystemBundle\ORM\StorableTrait;

/**
 * Post
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Post
{
    /**
     * Use the storable file trait
     */
    use StorableTrait;

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set title
     *
     * @param string $title
     * @return Post
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Get the Flysystem filesystem mount prefix as
     * configured in https://github.com/1up-lab/OneupFlysystemBundle/blob/master/Resources/doc/filesystem_create.md#use-the-mount-manager
     *
     * <code>
     * // A single filesystem...
     *
     * public function getFilesystemMountPrefix()
     * {
     *     return 'example_filesystem_mount_prefix';
     * }
     *
     * // Or a list of filesystems...
     *
     * public function getFilesystemMountPrefix()
     * {
     *     return [
     *         'example_filesystem_mount_prefix_01',
     *         'example_filesystem_mount_prefix_02',
     *     ];
     * }
     * </code>
     *
     * @return string|array
     */
    public function getFilesystemMountPrefix()
    {
        return 'my_filesystem_mount_name'; // This is the mount prefix configured in app/config/config.yml
    }
}

getFilesystemMountPrefix() 方法定义了 Flysystem 挂载前缀,其中您希望将与此实体关联的文件存储,如 app/config/config.yml 中定义。

注意:如果 getFilesystemMountPrefix() 返回挂载前缀数组,则文件将存储在每个文件系统中

特性将为实体添加四个属性

  • fileName : string
    • 客户端上传文件的原始名称
    • 列名:file_name
    • 例如:foobar.gif
  • fileStoragePath : string
    • 文件的存储路径。默认为文件名(如上所述)
    • 列名:file_storage_path
    • 例如:/path/to/foobar.gif
  • fileMimeType : string
    • 客户端上传文件的解析 MIME 类型
    • 列名:file_mime_type
    • 例如:image/gif
  • fileSize : integer
    • 以字节为单位的文件大小
    • 列名:file_size
    • 例如:2324

在使用此实体之前,您需要更新您的模式。

app/console doctrine:schema:update [--dump-sql | --force]

表单类型

使用实体和表单类型的示例

<?php

namespace Acme\DemoBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * Class PostType
 *
 * @package Acme\DemoBundle\Form
 */
class PostType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', 'text')
            ->add('uploaded_file', 'file', [
                'required' => false,
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver
            ->setDefaults([
                'data_class' => 'Acme\DemoBundle\Entity\Post',
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'post';
    }
}

注意:名为 "uploaded_file" 的字段映射到 AshleyDawson\DoctrineFlysystemBundle\ORM\StorableTrait 中的参数。如果您想更改此设置,只需为您的实体添加一个访问器作为代理

<?php

namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use AshleyDawson\DoctrineFlysystemBundle\ORM\StorableTrait;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/**
 * Post
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Post
{
    /**
     * Use the storable file trait
     */
    use StorableTrait;

    // ...

    /**
     * Set my file
     *
     * @param \Symfony\Component\HttpFoundation\File\UploadedFile $file
     * @return $this
     */
    public function setMyFile(UploadedFile $file = null)
    {
        $this->setUploadedFile($file);

        return $this;
    }
}

然后您可以将新名称添加到表单类型中,如下所示

    // ...

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', 'text')
            ->add('my_file', 'file', [
                'required' => false,
            ])
        ;
    }

    // ...

事件

存储处理程序,它是 Doctrine 实体生命周期的组成部分,在文件存储活动的边缘触发多个事件。这些事件包括

  • ashleydawson.doctrine_flysystem_bundle.pre_store
    • 在文件写入文件系统之前触发
  • ashleydawson.doctrine_flysystem_bundle.post_store
    • 在文件写入文件系统之后触发
  • ashleydawson.doctrine_flysystem_bundle.pre_delete
    • 在文件从文件系统中删除之前触发
  • ashleydawson.doctrine_flysystem_bundle.post_delete
    • 在文件从文件系统中删除之后触发

这些事件可以在命名空间 AshleyDawson\DoctrineFlysystemBundle\Event\StorageEvents 中找到。

这些事件的良好用例是在写入之前更改表单的任何详细信息,例如(在 Symfony 控制器中)

// Replace the file storage path with a random md5 hash directory structure, name and file extension
$this->get('event_dispatcher')->addListener(StorageEvents::PRE_STORE, function (StoreEvent $event) {

    // Build a directory structure like "af/9e"
    $fileStoragePath = implode('/', str_split(substr(md5(mt_rand()), 0, 4), 2));
    $event->setFileStoragePath(sprintf('/%s/%s.%s', $fileStoragePath, md5(mt_rand()), $event->getUploadedFile()->getClientOriginalExtension()));

});

当然,这是一个简单的示例 - 但它确实展示了如何更改文件(或文件的元信息)。在上面的示例中,我正在构建存储路径的哈希目录结构。类似于以下内容

/af/9e/2997f54d953111d222c00a0b6ed94a50.gif

注意:请不要将上面的示例作为生产解决方案使用,因为存在文件名冲突的可能性。

还可能是一个好主意,而不是像我上面那样使用基于闭包的实现,而是安装一个订阅者。您应该始终努力提供促进单一职责原则的系统!

可选配置

以下方式定义了可选配置参数

# app/config/config.yml
ashley_dawson_doctrine_flysystem:
    delete_old_file_on_update: true # Default is true

delete_old_file_on_update 设置为 false 将意味着在实体使用新文件更新时,将与实体关联的旧文件删除。

覆盖字段映射

当实体使用 StorableTrait 时,映射了多个用于存储文件元数据的字段。如果您需要更改这些映射,可以通过实现 AshleyDawson\DoctrineFlysystemBundle\ORM\Mapping\StorableFieldMapperInterface 并覆盖此包中提供的映射来做到这一点。这将允许您为每个字段定义自己的映射策略。例如

<?php

namespace Acme\DemoBundle\ORM\Flysystem\Mapping;

use AshleyDawson\DoctrineFlysystemBundle\ORM\Mapping\StorableFieldMapperInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;

/**
 * Class MyStorableFieldMapper
 *
 * @package Acme\DemoBundle\ORM\Flysystem\Mapping
 */
class MyStorableFieldMapper implements StorableFieldMapperInterface
{
    /**
     * {@inheritdoc}
     */
    public function mapFields(ClassMetadataInfo $classMetadata)
    {
        $classMetadata
            ->mapField([
                'fieldName' => 'fileName',
                'columnName' => 'file_name',
                'type' => 'string',
                'length' => 255,
                'nullable' => true,
            ])
        ;

        $classMetadata
            ->mapField([
                'fieldName' => 'fileStoragePath',
                'columnName' => 'file_storage_path',
                'type' => 'string',
                'length' => 255,
                'nullable' => true,
            ])
        ;

        $classMetadata
            ->mapField([
                'fieldName' => 'fileSize',
                'columnName' => 'file_size',
                'type' => 'integer',
                'nullable' => true,
            ])
        ;

        $classMetadata
            ->mapField([
                'fieldName' => 'fileMimeType',
                'columnName' => 'file_mime_type',
                'type' => 'string',
                'length' => 60,
                'nullable' => true,
            ])
        ;
    }
}

然后只需配置服务容器以使用您的映射器

# app/config/parameters.yml
ashley_dawson.doctrine_flysystem.storable_field_mapper.class: Acme\DemoBundle\ORM\Flysystem\Mapping\MyStorableFieldMapper