События Doctrine
Дата обновления перевода 2024-07-19
События Doctrine
Doctrine, набор PHP библиотек используемый в Symfony для работы с базами данных, предоставляет легковесную систему событий для обновления сущностей во время выполнения приложения. Эти события называются lifecycle events и дают возможность выполнять задачи вроде "обновить свойство createdAt автоматически прямо перед persist сущности данного типа".
Doctrine запускает события до/после выполнения наиболее частых операций с сущностью
(например, prePersist/postPersist
, preUpdate/postUpdate
) а также
при других частых задачах (e.g. loadClassMetadata
, onClear
).
Есть несколько способов слушать эти события Doctrine:
- Обратные вызовы жизненного цикла, они определяются как публичные методы в классах сущностей Они не могут использовать сервисы, поэтому предназначены для очень простой логики, связанной с с одной сущностью;
- Слушатели сущностей, они определяются как классы с методами обратного вызова для событий, на которые вы хотите реагировать. Они могут использовать сервисы, но вызываются только для сущностей определенного класса, поэтому они идеально подходят для сложной логики событий, связанной с одной сущностью;
- Слушатели жизненного цикла, они похожи на слушателей сущностей, но их методы событий вызываются для всех сущностей, а не только для тех, которые относятся к определенному типу. Они идеально подходят для обмена логикой событий между сущностями.
Производительность каждого типа слушателей зависит от того, к какому количеству сущностей они применяются: обратные вызовы жизненного цикла быстрее, чем слушатели сущностей, которые, в свою очередь, быстрее чем слушатели жизненного цикла.
Эта статья объясняет только основы того, как события Doctrine используются в приложениях Symfony. Прочитайте официальную документацию о событиях Doctrine чтобы узнать о них детальнее.
See also
Эта статья покрывает listeners и subscribers для Doctrine ORM. Если вы используете ODM для MongoDB, прочитайте документацию к DoctrineMongoDBBundle.
Обратные вызовы жизненного цикла Doctrine
Lifecycle callbacks определяются как методы внутри сущности, которую вы хотите изменить.
Например, предположим, что вы хотите установить колонку с датой createdAt
в текущую
дату, то только когда к сущности применяется первый раз persist (то есть, добавление новой записи). Чтобы сделать это,
определите callback для события Doctrine prePersist
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Entity/Product.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
// При использовании аннотаций не забудьте добавить @ORM\HasLifecycleCallbacks()
// к классу сущности там, где вы определяете обратный вызов
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
class Product
{
// ...
#[ORM\PrePersist]
public function setCreatedAtValue(): void
{
$this->createdAt = new \DateTimeImmutable();
}
}
Note
Некоторые обратные вызовы жизненного цикла получают аргумент, который предоставляет доступ к
полезной информации, такой как текущий entity manager (например,preUpdate
callback получает аргумент PreUpdateEventArgs $event
).
Слушатели сущностей Doctrine
Слушатели сущностей определяются как PHP-классы, которые слушают одно событие Doctrine
для однго класса entity. Например, предположим, что вы хотите отправить несколько
уведомлений, когда entity User
изменяется в DB. Для этого
определите listener для Doctrine события postUpdate
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/EventListener/UserChangedNotifier.php
namespace App\EventListener;
use App\Entity\User;
use Doctrine\ORM\Event\PostUpdateEventArgs;
class UserChangedNotifier
{
// методы слушателя сущности получают два аргумента:
// экземпляр сущности и событие жизненного цикла
public function postUpdate(User $user, PostUpdateEventArgs $event): void
{
// ... сделайте что-то, чтобы уведомить об изменениях
}
}
Затем, добавьте атрибут #[AsEntityListener]
к классу, чтобы включить его в качестве
слушателя сущностей Doctrine в вашем приложении:
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/EventListener/UserChangedNotifier.php
namespace App\EventListener;
// ...
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Events;
#[AsEntityListener(event: Events::postUpdate, method: 'postUpdate', entity: User::class)]
class UserChangedNotifier
{
// ...
}
Как вариант, если вы предпочитаете не использовать PHP-атрибуты, вы должны
сконфигурировать сервис для слушателя сущностей и добавить тег
doctrine.orm.entity_listener
следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# config/services.yaml
services:
# ...
App\EventListener\UserChangedNotifier:
tags:
-
# это опции, необходимые для определения слушателя сущности
name: 'doctrine.orm.entity_listener'
event: 'postUpdate'
entity: 'App\Entity\User'
# это другие опции, которые вы можете определить при необходимости
# установите опцию 'lazy' как TRUE, чтобы инстанциировать слушателей только при использовании
# lazy: true
# установите опцию 'entity_manager', если слушатель не ассоциирован с менеджером по умолчанию
# entity_manager: 'custom'
# по умолчанию, Symfony ищет метод, вызываемый после события (например, postUpdate())
# если он не существует, она пытается выполнить метод '__invoke()', но вы можете
# сконфигурировать пользовательское имя метода с опцией 'method'
# method: 'checkUserChanges'
Слушатели жизненного цикла Doctrine
Слушатели жизненного цикла определяются как PHP-классы, которые слушают одно событие Doctrine
для всех сущностей приложения. Например, предположим, что вы хотите
обновить поисковый индекс, каждый раз, когда новая entity добавляется (persist) в DB. Чтобы
сделать это, объявите listener для события Doctrine postPersist
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// src/EventListener/SearchIndexer.php
namespace App\EventListener;
use App\Entity\Product;
use Doctrine\ORM\Event\PostPersistEventArgs;
class SearchIndexer
{
// методы слушателя получают аргумент, который дает вам доступ и к
// сущности объекта события, и к сущности самого менеджера
public function postPersist(PostPersistEventArgs $args): void
{
$entity = $args->getObject();
// если этот слушатель применяется только к определенным типам сущностей,
// добавьте код для проверки сущности как можно раньше
if (!$entity instanceof Product) {
return;
}
$entityManager = $args->getObjectManager();
// ... сделать что-то с сущностью Product
}
}
Note
В предыдущих версиях Doctrine, вместо PostPersistEventArgs
вам нужно было
использовать LifecycleEventArgs
, что устарело в Doctrine ORM 2.14.
Then, add the #[AsDoctrineListener]
attribute to the class to enable it as
a Doctrine listener in your application:
1 2 3 4 5 6 7 8 9 10 11
// src/EventListener/SearchIndexer.php
namespace App\EventListener;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Events;
#[AsDoctrineListener(event: Events::postPersist, priority: 500, connection: 'default')]
class SearchIndexer
{
// ...
}
Как вариант, если вы не хотите использовать атрибуты PHP, вы должны включить слушателя
в приложении Symfony, создав для него новый сервис и
добавив тег doctrine.event_listener
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/App/EventListener/SearchIndexer.php
namespace App\EventListener;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Event\PostPersistEventArgs;
#[AsDoctrineListener('postPersist'/*, 500, 'default'*/)]
class SearchIndexer
{
public function postPersist(PostPersistEventArgs $event): void
{
// ...
}
}
2.7.2
Атрибут AsDoctrineListener был представлен в DoctrineBundle 2.7.2.
Tip
Значение опции connection
также может быть
параметром конфигурации .