ashleydawson/doctrine-gaufrette-storable-bundle

Symfony2 扩展包,为 doctrine 实体添加存储行为,以便于抽象化文件存储

0.8.1 2015-05-06 09:13 UTC

This package is auto-updated.

Last update: 2024-09-14 03:58:40 UTC


README

Build Status

knpbundles.com

需求

 >= PHP 5.4
 >= Symfony Framework 2.3

Doctrine 支持

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

简介

我构建了这个扩展包来扩展优秀的文件系统抽象层,Knp Lab 的 Gaufrette。实际上,这个库扩展了 KnpGaufretteBundle

这个扩展包在 Doctrine 实体上实现了一个“上传文件”处理器,允许 Gaufrette 将文件作为 Doctrine 实体生命周期的一部分进行存储。

这个扩展包的主要类是应用于任何 Doctrine 实体的一个 特质,使得 Gaufrette 处理器能够与实体一起持久化文件详情。

安装

您可以通过 Composer 安装 Doctrine Gaufrette Storable Bundle。为此,只需在您的 composer.json 文件中像这样要求包

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

运行 composer update 以安装包。然后您需要将包注册到您的 app/AppKernel.php 中

$bundles = array(
    // ...
    new Knp\Bundle\GaufretteBundle\KnpGaufretteBundle(), // KnpGaufretteBundle is a dependency of this bundle
    new AshleyDawson\DoctrineGaufretteStorableBundle\AshleyDawsonDoctrineGaufretteStorableBundle(),
);

配置

接下来,您需要配置至少一个文件系统来存储文件。下面我将提供一个示例,然而,更好的示例可以在 Gaufrette Bundle 文档 中找到。

# app/config/config.yml
knp_gaufrette:
    adapters:
        local_adapter:
            local:
                directory: /tmp/sandbox
    filesystems:
            test_local_filesystem:
                adapter: local_adapter

用法

为了使用此扩展包,您必须将给定的特质应用于希望携带上传文件的实体。

<?php

namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use AshleyDawson\DoctrineGaufretteStorableBundle\Model\UploadedFileTrait;

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

    /**
     * @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 Gaufrette filesystem map id as
     * configured in https://github.com/KnpLabs/KnpGaufretteBundle#configuring-the-filesystems
     *
     * @return string
     */
    public function getFilesystemMapId()
    {
        return 'test_local_filesystem';
    }
}

特质将为实体添加四个字段

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

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

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

抽象方法 getFilesystemMapId() 定义了 Gaufrette 文件系统 ID,其中您希望与该实体关联的文件被存储(在 knp_gaufrette 配置中定义)。

表单类型

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

<?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\DoctrineGaufretteStorableBundle\Model\UploadedFileTrait 中的参数。如果您想更改此,只需添加一个访问器到您的实体作为代理即可

<?php

namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use AshleyDawson\DoctrineGaufretteStorableBundle\Model\UploadedFileTrait;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/**
 * Post
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Post
{
    /**
     * Use the uploaded file trait
     */
    use UploadedFileTrait;   
   
    // ...

    /**
     * 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 实体生命周期的一部分)在文件存储活动的边缘触发几个事件。这些事件包括

  • ad_doctrine_gaufrette_storable.pre_write
    • 在文件写入文件系统之前触发
  • ad_doctrine_gaufrette_storable.post_write
    • 在文件写入文件系统之后触发
  • ad_doctrine_gaufrette_storable.pre_delete
    • 在文件从文件系统中删除之前触发
  • ad_doctrine_gaufrette_storable.post_delete
    • 在文件从文件系统中删除之后触发

这些事件可以在 AshleyDawson\DoctrineGaufretteStorableBundle\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_WRITE, function (WriteUploadedFileEvent $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->getFileExtension()));
});

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

/af/9e/2997f54d953111d222c00a0b6ed94a50.gif

注意:请不要将上述示例用作生产解决方案,因为存在文件名冲突的风险。

也可能是一个好主意,使用订阅者而不是像我上面那样使用基于闭包的实现。你应该始终致力于创建一个促进单一责任原则的系统!