hostnet / entity-tracker-component
当被追踪的实体发生变化时提供事件
Requires
- php: ^7.3|^8.0
- doctrine/annotations: 1.*,>=1.2.0
- doctrine/common: ^2.13.3|^3.0.0
- doctrine/orm: ^2.7.5
- psr/log: ^1.0.0
Requires (Dev)
- hostnet/database-test-lib: ^2.0.2
- hostnet/phpcs-tool: ^9.1.0
- phpspec/prophecy-phpunit: ^2.0
- phpunit/phpunit: ^9.4
- symfony/cache: ^5.3
README
文档
什么是实体追踪器?
实体追踪器组件是一个库,用于在EntityManager刷新期间跟踪实体内的变化。这使得在preFlush
事件期间自动执行各种事情成为可能。
当您实现@Tracked
注解或@Tracked
的子类时,实体开始被追踪。您可以完全控制接下来发生什么以及您将使用哪些事件来监听entityChanged
事件。
假设每次您刷新用户时,您都希望设置其更新时间。默认情况下,您必须手动调用$user->setUpdatedAt()
或创建一个自定义监听器在preFlush上设置更新时间戳。这两者都是很多额外的工作,并且您必须编写额外的代码来确定变化。监听preFlush将始终触发您的监听器,并且您不希望编写一个巨大的if语句或为每个实体创建一个监听器。
要求
追踪组件需要至少php 5.4,并在Doctrine2上运行。有关具体要求,请查看composer.json
安装
安装很简单,此包在packagist上可用。您可以将包锁定为主版本,因为我们遵循语义版本化2.0.0。
示例
"require" : { "hostnet/entity-tracker-component" : "^2.0.1" }
注意:如果您想使用最新的更改,可以使用dev-master,但这不建议用于生产代码!
文档
它是如何工作的?
它通过在您的实体上放置注解并在我们的事件上注册您的监听器来实现,假设您已经将我们的事件注册到doctrine中。这就是您需要做的全部工作来开始跟踪实体,以便它在entityChanged
事件中可用。
设置
注册事件
以下是一个非常基本的设置的示例。如果您使用具有依赖注入容器的框架,设置这会更容易。
注意:如果您使用Symfony,可以查看hostnet/entity-tracker-bundle。此包旨在为您配置服务。
use Acme\Component\Listener\ChangedAtListener; use Hostnet\Component\EntityTracker\Listener\EntityChangedListener; use Hostnet\Component\EntityTracker\Provider\EntityAnnotationMetadataProvider; use Hostnet\Component\EntityTracker\Provider\EntityMutationMetadataProvider; /* @var $em \Doctrine\ORM\EntityManager */ $event_manager = $em->getEventManager(); // default doctrine annotation reader $annotation_reader = new AnnotationReader(); // setup required providers $mutation_metadata_provider = new EntityMutationMetadataProvider($annotation_reader); $annotation_metadata_provider = new EntityAnnotationMetadataProvider($annotation_reader); // pre flush event listener that uses the @Tracked annotation $entity_changed_listener = new EntityChangedListener( $mutation_metadata_provider, $annotation_metadata_provider ); // our example listener $listener = new ChangedAtListener(new DateTime()); // register the events $event_manager->addEventListener('preFlush', $entity_changed_listener); $event_manager->addEventListener('prePersist', $entity_changed_listener); $event_manager->addEventListener('entityChanged', $listener);
创建监听器
监听器需要一个具有与事件名称相同名称的方法。该方法将有一个参数,即EntityChangedEvent $event
。事件包含使用的EntityManager、当前实体、原始(旧)实体以及一个已更改或变异的字段数组。
注意:Doctrine2事件管理器使用事件名称作为方法名称,因此您应按照以下列表实现entityChanged方法。
namespace Acme\Component\Listener; use Hostnet\Component\EntityTracker\Event\EntityChangedEvent; class ChangedAtListener { private $now; public function __construct(\DateTime $now) { $this->now = $now; } public function entityChanged(EntityChangedEvent $event) { if (!($entity = $event->getCurrentEntity()) instanceof UpdatableInterface) { // uses the tracked but might not have our method return; } $entity->setUpdatedAt($this->now); } }
为实体创建接口
除了@Tracked
注解之外,我们还想确定我们是否可以在我们的实体中设置一个updated_at字段。这可以通过为我们实体创建以下接口来完成。
namespace Acme\Component\Listener; interface UpdatableInterface { public function setUpdatedAt(\DateTime $now); }
在实体上注册注解
现在我们只需要在实体上放置@Tracked
注解和接口,并实现所需的方法
use Acme\Component\Listener\UpdatableInterface; use Doctrine\ORM\Mapping as ORM; use Hostnet\Component\EntityTracker\Annotation\Tracked; /** * @ORM\Entity * @Tracked */ class MyEntity implements UpdatableInterface { /** * @ORM\... */ private $changed_at; public function setUpdatedAt(\DateTime $now) { $this->changed_at = $now; } }
接下来是什么?
更改字段的值并刷新实体。这将触发预刷新(preFlush),进而触发我们的监听器,然后触发实体变更(entityChanged)事件。
$entity->setName('henk'); $em->flush(); // Voila, your changed_at is filled in
扩展追踪注解
你可能需要扩展@Tracker
注解。这允许你在监听器中添加选项和额外的检查。
示例注解
在下面的示例中,你将看到如何创建自定义注解。
- 你必须添加
@Annotation
- 你必须添加
@Target({"CLASS"})
- 它必须扩展
Hostnet\Component\EntityTracker\Annotation\Tracked
使用这个注解,我们将能够访问监听器内的特定选项。现在我们可以在监听器中尝试获取这个注解,并调用getIgnoredFields()
。这个示例将忽略使用注解的实体中的一些字段。
use Hostnet\Component\EntityTracker\Annotation\Tracked; /** * @Annotation * @Target({"CLASS"}) */ class Changed extends Tracked { public $ignore_fields = []; public function getIgnoredFields() { if (empty($this->ignore_fields)) { return ['id']; } return $ignore_fields; } }
自定义注解解析器
为了获取注解,我们实现了解析器。下面的示例展示了如何自己实现它。
use Doctrine\ORM\EntityManagerInterface; use Hostnet\Component\EntityTracker\Provider\EntityAnnotationMetadataProvider; class ChangedResolver { private $annotation = 'Changed'; private $provider; public function __construct(EntityAnnotationMetadataProvider $provider) { $this->provider = $provider; } public function getChangedAnnotation(EntityManagerInterface $em, $entity) { return $this->provider->getAnnotationFromEntity($em, $entity, $this->annotation); } }
自定义entityChanged监听器
监听器现在可以使用解析器获取注解,因此可以在特定字段集更改时执行一些额外操作。
use Hostnet\Component\EntityTracker\Event\EntityChangedEvent; class ChangedListener { private $resolver; public function __construct(ChangedResolver $resolver) { $this->resolver = $resolver; } public function entityChanged(EntityChangedEvent $event) { $em = $event->getEntityManager(); $entity = $event->getCurrentEntity(); if (null === ($annotation = $this->resolver->getChangedAnnotation($em, $entity))) { return; } $preferred_changes = array_diff($annotation->getIgnoredFields(), $event->getMutatedFields()); // do something with them } }