如何在Symfony中以编程方式设置Doctrine实体监听器?

时间:2018-01-31 19:41:13

标签: php symfony doctrine-orm doctrine

我有一个Symfony 3.4应用程序和一个带有EntityChangeListener的Composer包来记录实体属性更改。该包还包含EntityListenerPass(编译器传递),它在构建服务容器时迭代app 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实体监听器?

1 个答案:

答案 0 :(得分:2)

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

使用这种方法,您可以在Doctrine第一次加载classmetada时(通过使用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 }