【问题标题】:How to programmatically set up Doctrine entity listener in Symfony?如何在 Symfony 中以编程方式设置 Doctrine 实体监听器?
【发布时间】:2018-01-31 19:41:12
【问题描述】:

我有一个 Symfony 3.4 应用程序和一个带有 EntityChangeListener 的 Composer 包来记录实体属性更改。该包还包含一个EntityListenerPass(编译器通行证),它在构建服务容器时迭代应用程序config.yml 中定义的类名列表。它以编程方式标记这样的实体类,以在 preUpdate 事件上通知侦听器:

$listener = $container->getDefinition('entity_history.listener.entity_change');
$entities = $container->getExtensionConfig('entity_history')[0]['entities'];

foreach ($entities as $className) {
    $listener->addTag('doctrine.orm.entity_listener', ['entity' => $className, 'event' => 'preUpdate']);
}

添加这些标签会导致很多看似无关的错误。例如,实体状态的 Doctrine UnitOfWork 内部未定义索引错误。 Doctrine 也将突然从数据库加载的相关实体识别为 new。甚至switch 语句中的对象比较也开始失败:

致命错误:嵌套级别太深 - 递归依赖?

但如果没有这些侦听器,一切正常,所有测试都通过。是否有替代/更好的方式以编程方式设置 Doctrine 实体侦听器?

【问题讨论】:

  • 你能给EntityChangeListener的链接吗?
  • 对不起,我不明白你到底是什么链接。
  • EntityChangeListener 这是一些捆绑包?因为我在 git 中没有找到这个
  • 啊,那个监听器来自我自己正在开发的包。
  • 非常酷。有没有打算在这个基础上做一个bandle

标签: php symfony doctrine-orm doctrine


【解决方案1】:

是的,您可以通过直接作用于类元数据来附加实体侦听器。在我的应用程序(Symfony 2.8)中,我通过添加一个对loadClassMetadata 事件作出反应的侦听器来为我的配置中标记的一些实体执行此操作。

使用这种方法,您可以在 Doctrine 首次加载类元数据时挂钩您的实体侦听器(通过使用 addEntityListener)。因此,您只需挂钩当前上下文所需的实体侦听器,仅此而已。

这是我用来反映在您的特定情况下的外观的监听器的修改版本:

namespace AppBundle\Listener;

use Doctrine\ORM\Event\LoadClassMetadataEventArgs;

class MappingListener
{
    private $listenerClassname;
    private $entities;

    public function __construct($listenerClassname, array $entities)
    {
        $this->entities = $entities;
        $this->listenerClassname = $listenerClassname;
    }

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $classMetadata = $eventArgs->getClassMetadata();

        if(!in_array($classMetadata->name, $this->entities))
        {
            return;
        }

        // Hook the entity listener in the class metadata
        // $classMetadata->addEntityListener( string $eventName, string $class, string $method )
        $classMetadata->addEntityListener('preUpdate', $this->listenerClassName, 'preUpdate');
    }
}

然后在您的services.yml 中,类似这样的内容:

mapping.listener:
    class: AppBundle\Listener\MappingListener
    arguments: [ "%your_listener_classname%", "%your_entities_array%" ]
    tags:
        - { name: doctrine.event_listener, event: loadClassMetadata, lazy: true }

【讨论】:

  • 谢谢。因为我的实际侦听器需要注入服务,所以我更改了编译器传递以获取 doctrine.orm.default_entity_listener_resolver 的定义并添加一个方法调用以将我的 EntityChangeListener 注册为服务。否则ContainerAwareEntityListenerResolver 会明显且错误地实例化侦听器。
猜你喜欢
  • 2019-04-29
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
  • 2018-08-02
  • 2015-09-02
  • 1970-01-01
  • 2018-03-27
  • 1970-01-01
相关资源
最近更新 更多