Symfony 3.4实体监听器如何指定监听/排除字段?
如何在Symfony 3.4的Doctrine实体监听器中指定监听/排除字段?
嘿,我正好碰到过类似的需求,在Symfony 3.4里Doctrine确实没有原生注解直接支持指定监听器要监听或排除的字段,但我们可以用两种优雅的方式实现,不用手动逐个校验$changeArray的键名:
方案一:自定义实体注解(与实体强绑定,更直观)
这种方式给User实体的字段添加自定义注解,标注哪些字段需要监听/排除,然后在监听器里通过反射读取注解过滤变更字段。
1. 创建自定义注解类
在AppBundle/Annotation目录下新建注解类(没有目录就手动创建):
<?php namespace AppBundle\Annotation; use Doctrine\Common\Annotations\Annotation; /** * @Annotation * @Target("PROPERTY") */ class ListenField extends Annotation { // 仅作为标记使用,无需额外属性 } // 如果需要排除字段,可创建对应注解 /** * @Annotation * @Target("PROPERTY") */ class ExcludeField extends Annotation { }
2. 在User实体上使用注解
比如只想监听firstname和email字段,就给这两个字段加上@ListenField注解:
<?php namespace AppBundle\Entity; use AppBundle\Annotation\ListenField; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity */ class User { /** * @ORM\Column(type="string") * @ListenField */ private $firstname; /** * @ORM\Column(type="string") * @ListenField */ private $email; /** * @ORM\Column(type="string") */ private $password; // 该字段不会被监听 // ... 其他字段、getter和setter方法 }
3. 修改实体监听器,读取注解过滤字段
更新EntityListener,通过反射获取实体字段的注解,过滤出需要处理的变更:
<?php namespace AppBundle\EventListener; use AppBundle\Annotation\ListenField; use AppBundle\Entity\User; use Doctrine\Common\Annotations\Reader; use Doctrine\ORM\Event\PreUpdateEventArgs; use ReflectionClass; class EntityListener { private $annotationReader; // 通过依赖注入注入注解读取器 public function __construct(Reader $annotationReader) { $this->annotationReader = $annotationReader; } public function preUpdate(PreUpdateEventArgs $eventArgs) { $entity = $eventArgs->getEntity(); if (!$entity instanceof User) { return; } $changeArray = $eventArgs->getEntityChangeSet(); $reflectionClass = new ReflectionClass($entity); $listenFields = []; // 遍历实体属性,收集带有@ListenField注解的字段名 foreach ($reflectionClass->getProperties() as $property) { $annotation = $this->annotationReader->getPropertyAnnotation( $property, ListenField::class ); if ($annotation instanceof ListenField) { $listenFields[] = $property->getName(); } } // 过滤出仅包含监听字段的变更数组 $filteredChanges = array_intersect_key($changeArray, array_flip($listenFields)); // 如果用排除字段逻辑,替换为: // $filteredChanges = array_diff_key($changeArray, array_flip($excludeFields)); var_dump($filteredChanges); // 现在只有指定字段的变更会输出 } }
4. 更新services.yml,注入注解读取器
记得给监听器注入doctrine.annotations.reader服务:
AppBundle\EventListener\EntityListener: arguments: - '@doctrine.annotations.reader' tags: - { name: doctrine.event_listener, event: preUpdate }
方案二:在监听器中配置字段列表(快速灵活,无需修改实体)
如果不想改动实体代码,直接在监听器里定义监听/排除字段列表,甚至可以通过配置文件注入,调整起来更方便。
1. 修改监听器,添加字段配置
<?php namespace AppBundle\EventListener; use AppBundle\Entity\User; use Doctrine\ORM\Event\PreUpdateEventArgs; class EntityListener { // 定义要监听的字段列表 private $listenFields = ['firstname', 'email']; // 或者定义排除字段列表 // private $excludeFields = ['password', 'createdAt']; public function preUpdate(PreUpdateEventArgs $eventArgs) { $entity = $eventArgs->getEntity(); if (!$entity instanceof User) { return; } $changeArray = $eventArgs->getEntityChangeSet(); // 过滤监听字段 $filteredChanges = array_intersect_key($changeArray, array_flip($this->listenFields)); // 如果用排除字段逻辑,替换为: // $filteredChanges = array_diff_key($changeArray, array_flip($this->excludeFields)); var_dump($filteredChanges); } }
2. (可选)通过services.yml注入字段列表
如果想把字段配置放到配置文件里,方便后续修改,可调整services.yml:
AppBundle\EventListener\EntityListener: arguments: $listenFields: ['firstname', 'email'] # 对应监听器构造函数的参数 tags: - { name: doctrine.event_listener, event: preUpdate }
然后修改监听器的构造函数接收参数:
public function __construct(array $listenFields) { $this->listenFields = $listenFields; }
这两种方案都能帮你摆脱手动校验字段名的麻烦:方案一更适合需要和实体字段强绑定的场景,方案二更适合快速调整或不想修改实体的情况,你可以根据自己的需求选择~
内容的提问来源于stack exchange,提问作者Clément Andraud
相关产品推荐
相关产品推荐

