首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Symfony vich上传程序和原理可记录的扩展问题?

Symfony vich上传程序和原理可记录的扩展问题?
EN

Stack Overflow用户
提问于 2019-06-28 22:08:04
回答 1查看 728关注 0票数 3

我使用这两个库来创建一个实体,该实体使用vich/uploader-bundle创建图片,并使用stof/doctrine-extensions-bundle提供的loggable原理扩展来记录实体更改历史,stof/doctrine-extensions-bundle提供了来自atlantic18/doctrineextensions的扩展。

所以这里有一个问题:我有一个实体,它有一个Vich可上传的图片字段,并且它正在使用picture的Gedmo loggable扩展和注释。

代码语言:javascript
运行
复制
/**
 * @var VersionedFile
 *
 * @ORM\Embedded(class="App\Entity\Embedded\VersionedFile")
 *
 * @Gedmo\Versioned()
 */
private $picture;

/**
 * @var File
 *
 * @Vich\UploadableField(
 *     mapping="user_picture",
 *     fileNameProperty="picture.name",
 *     size="picture.size",
 *     mimeType="picture.mimeType",
 *     originalName="picture.originalName",
 *     dimensions="picture.dimensions
 * )
 */
private $pictureFile;

/**
 * @var DateTimeInterface
 *
 * @ORM\Column(type="datetime", nullable=true)
 *
 * @Gedmo\Versioned()
 */
private $pictureUpdatedAt;

嵌入的实体类App\Entity\Embedded\VersionedFile具有所有需要的注释,以便使用loggable原理扩展正确地进行版本控制。

代码语言:javascript
运行
复制
// Not the whole code but just to get the idea for property versioning

/**
 * @ORM\Column(name="name", nullable=true)
 *
 * @Gedmo\Versioned()
 */
protected $name;

现在问题来了。当我上传文件并持久化实体时,会发生以下事情。实体管理器持久化实体,并调用Gedmo可记录侦听器(Gedmo\Loggable\LoggableListener)的onFlush方法。此监听程序检查更改并计划插入日志条目。

问题是VichUploaders upload listener (Vich\UploaderBundle\EventListener\Doctrine\UploadListener) is called after the loggable listener and then the file is uploaded which changes the properties name, size, etc. The computed changes about name, size, etc. are not available in theLoggableListener`是先被调用的,所以它不知道它们应该被插入。

是我错过了一些配置,还是我做错了什么。这个想法是记录对图片所做的更改。现在,在数据库中,日志条目只包含$pictureUpdatedAt字段。

我调试了这个问题,我能看到的只有order,而且在LoggableListener中,getObjectChangeSetData方法只返回已更改的$pictureUpdatedAt字段。我不认为这与嵌入式实体有什么共同之处,因为我认为监听器的调用顺序是问题所在。我的第一个想法是更改监听器的优先级,但即使我这样做了,调用的顺序也不会更改,主要是因为当onFlush被调用时,它会触发preUpdate方法,该方法会触发上载程序包的UploadListener

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-08-30 01:54:29

您是对的,问题的根源在于UploadListener监听prePersistpreUpdate,而LoggableListener监听onFlush。由于onFlush是在preUpdate之前触发的,因此永远不会记录文件更改。这个问题只需几个步骤就可以解决。

1.创建新的UploadListener

首先,您可以编写自己的UploadListener来监听onFlush

代码语言:javascript
运行
复制
// src/EventListener/VichUploadListener.php using Flex
// src/AppBundle/EventListener/VichUploadListener.php otherwise
namespace App\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Vich\UploaderBundle\EventListener\Doctrine\UploadListener;

class VichUploadListener extends UploadListener
{
    public function onFlush(OnFlushEventArgs $args): void
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            $this->preUpdate(new LifecycleEventArgs($entity, $em));
        }

        // Required if using property namer on sluggable field. Otherwise, you
        // can also subscribe to "prePersist" and remove this foreach.
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            // We use "preUpdate" here so the changeset is recomputed.
            $this->preUpdate(new LifecycleEventArgs($entity, $em));
        }
    }

    public function getSubscribedEvents(): array
    {
        return [Events::onFlush];
    }
}

在本例中,我重用了原始的UploadListener以使事情变得更简单。因为我们正在监听onFlush,所以在上传文件后重新计算实体变更集是很重要的,这就是为什么我使用"preUpdate“方法进行计划更新和插入。

在改变像这样的事件时,你必须要小心。如果您有另一个侦听器希望设置(或取消设置)某个文件字段的值,则这可能会更改预期行为。如果您使用第二个foreach来处理新的上传,这一点尤其正确。prePersistonFlush之前触发,所以这会使新的上传设置得比以前更晚。

2.创建新的CleanListener

接下来,我们现在必须创建一个新的CleanListener。如果delete_on_update设置为true,则当我们更新file字段时,此侦听器将删除旧文件。由于它监听preUpdate,因此我们必须将其更改为onFlush,以便正确删除旧文件。

代码语言:javascript
运行
复制
// src/EventListener/VichCleanListener.php on Flex
// src/AppBundle/EventListener/VichCleanListener.php otherwise
namespace App\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Vich\UploaderBundle\EventListener\Doctrine\CleanListener;

class VichCleanListener extends CleanListener
{
    public function onFlush(OnFlushEventArgs $args): void
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            $this->preUpdate(new LifecycleEventArgs($entity, $em));
        }
    }

    public function getSubscribedEvents(): array
    {
        return [Events::onFlush];
    }
}

3.配置新的监听器

现在,我们需要用刚刚编写的监听器覆盖配置中的默认监听器。

代码语言:javascript
运行
复制
# config/services.yaml on Flex
# app/config/services.yml otherwise
services:
    # ...

    vich_uploader.listener.upload.orm:
        class: 'App\EventListener\VichUploadListener'
        parent: 'vich_uploader.listener.doctrine.base'
        autowire: false
        autoconfigure: false
        public: false
    vich_uploader.listener.clean.orm:
        class: 'App\EventListener\VichCleanListener'
        parent: 'vich_uploader.listener.doctrine.base'
        autowire: false
        autoconfigure: false
        public: false

4.更改Gedmo扩展优先级

如果所有这些还不够,现在出现了您提出的另一个问题:监听器优先级。至少,我们需要确保在我们的upload/clean监听器之后触发LoggableListener。如果你正在使用任何其他的Gedmo扩展,你需要确保它们按照你需要的顺序加载。defaults set by VichUploaderExtensionCleanListener设置为50,将UploadListener设置为0。您可以在StofDoctrineExtensionsExtension中看到Gedmo Listener defaults

对我来说,我有一个依赖于可延迟字段的属性名称,所以我想确保在UploadListener之前调用SluggableListener。我也使用softdeleteable,并且希望软删除被记录为"remove",所以我想确保LoggableListenerSoftDeleteableListener之前注册。您可以通过覆盖配置中的服务来更改这些优先级。

代码语言:javascript
运行
复制
# config/services.yaml on Flex
# app/config/services.yml otherwise
services:
    # ...

    stof_doctrine_extensions.listener.sluggable:
        class: '%stof_doctrine_extensions.listener.sluggable.class%'
        autowire: false
        autoconfigure: false
        public: false
        calls:
            - { method: 'setAnnotationReader', arguments: ['@annotation_reader'] }
        tags:
            - { name: 'doctrine.event_subscriber', connection: 'default', priority: 5 }

    stof_doctrine_extensions.listener.loggable:
        class: '%stof_doctrine_extensions.listener.loggable.class%'
        autowire: false
        autoconfigure: false
        public: false
        calls:
            - { method: 'setAnnotationReader', arguments: ['@annotation_reader'] }
        tags:
            - { name: 'doctrine.event_subscriber', connection: 'default', priority: -1 }

    stof_doctrine_extensions.listener.softdeleteable:
        class: '%stof_doctrine_extensions.listener.softdeleteable.class%'
        autowire: false
        autoconfigure: false
        public: false
        calls:
            - { method: 'setAnnotationReader', arguments: ['@annotation_reader'] }
        tags:
            - { name: 'doctrine.event_subscriber', connection: 'default', priority: -2 }

或者,您可以创建一个编译器通道,仅更改这些服务的doctrine.event_subscriber标记的优先级。

代码语言:javascript
运行
复制
// src/DependencyInjection/Compiler/DoctrineExtensionsCompilerPass.php on Flex
// src/AppBundle/DependencyInjection/Compiler/DoctrineExtensionsCompilerPass.php otherwise
namespace App\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class DoctrineExtensionsCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $listenerPriorities = [
            'sluggable' => 5,
            'loggable' => -1,
            'softdeleteable' => -2,
        ];

        foreach ($listenerPriorities as $ext => $priority) {
            $id = sprintf('stof_doctrine_extensions.listener.%s', $ext);

            if (!$container->hasDefinition($id)) {
                continue;
            }

            $definition = $container->getDefinition($id);
            $tags = $definition->getTag('doctrine.event_subscriber');
            $definition->clearTag('doctrine.event_subscriber');

            foreach ($tags as $tag) {
                $tag['priority'] = $priority;
                $definition->addTag('doctrine.event_subscriber', $tag);
            }
        }
    }
}

如果使用此方法,请确保使用更高的优先级(高于0)注册编译器通道,以确保它在RegisterEventListenersAndSubscribersPass之前运行。

代码语言:javascript
运行
复制
// src/Kernel.php on Flex
// src/AppBundle/AppBundle.php otherwsie

// ...

use App\DependencyInjection\Compiler\DoctrineExtensionsCompilerPass;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;

// ...

protected function build(ContainerBuilder $container)
{
    $container->addCompilerPass(new DoctrineExtensionsCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 5);
}

现在,只需确保清除缓存即可。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56808473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档