我有一个Symfony 3.4应用程序和一个Composer软件包,其中包含记录实体属性更改的EntityChangeListener
。该包还包含一个 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识别为新实体。甚至 switch
语句中的对象比较也开始失败,并显示:
致命错误:嵌套级别太深 - 递归依赖?
但是没有这些听众,一切正常,所有测试都通过了。是否有另一种/更好的方法来以编程方式设置 Doctrine 实体侦听器?
是的,您可以通过直接对类元数据执行操作来附加实体侦听器。在我的应用程序(Symfony 2.8(中,我通过添加对loadClassMetadata
事件做出反应的侦听器,为配置中标记的某些实体执行此操作。
使用这种方法,您可以在 Doctrine 首次加载类元时挂接实体侦听器(通过使用 addEntityListener
(。因此,您只钩住当前上下文所需的实体侦听器,仅此而已。
这是我用来镜像它在特定情况下的外观的侦听器的修改版本:
namespace AppBundleListener;
use DoctrineORMEventLoadClassMetadataEventArgs;
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: AppBundleListenerMappingListener
arguments: [ "%your_listener_classname%", "%your_entities_array%" ]
tags:
- { name: doctrine.event_listener, event: loadClassMetadata, lazy: true }