我有一个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实体监听器?
答案 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 }