Події Doctrine

Дата оновлення перекладу 2024-05-27

Події Doctrine

Doctrine - набір PHP бібліотек, що використовується в Symfony для роботи з базами даних, надає легковажну систему подій для оновлення сутностей під час виконання додатку. Ці події називаються подіями життєвого циклу та надають можливість виконувати задачі на кшталт "оновити властивість createdAt автоматично прямо перед збереженням сутності даного типу".

Doctrine оголошує події до/після виконання найчастіших операцій з сутністю (наприклад, prePersist/postPersist, preUpdate/postUpdate), а також при інших частих задачах (наприклад, loadClassMetadata, onClear).

Є декілька способів слухати ці події Doctrine:

  • Зворотні виклики життєвого циклу, вони визначаються як загальнодоступні методи у класах сутностей. Вони не можуть використовувати сервіси, тому призначені для дуже простої логіки, пов'язаної з однією сутністю;
Слухачі сутностей, вони визначаються як класи з методами зворотного виклику для
подій, на які ви хочете відреагувати. Вони можуть використовувати сервіси, але викликаються лише для сутностей певного класу, тому вони ідеально підходять для складної логіки подій, пов'язаної з одним об'єктом;
Слухачі життєвого циклу, вони схожі на слухачів сутностей, але їхні методи обробки подій
викликаються для всіх сутностей, а не тільки для сутностей певного типу. Вони
ідеально підходять для того, щоб розподіляти логіку подій між сутностями.

Продуктивність кожного типу слухача залежить від того, до скількох сутностей він застосовується: зворотні виклики життєвого циклу працюють швидше, ніж слухачі сутностей, які, в свою чергу, працюють швидше, ніж слухачі життєвого циклу.

Ця стаття пояснює лише основи того, як події Doctrine використовуються у додатках Symfony. Прочитайте офіційну документацію про події Doctrine, щоб дізнатися про них детальніше.

See also

Ця стаття охоплює слухачів та підписників для Doctrine ORM. Якщо ви використовуєте ODM для MongoDB, прочитайте документацію DoctrineMongoDBBundle.

Зворотні виклики життєвого циклу Doctrine

Зворотні виклики життєвого циклю визначаються як методи сутності, яку ви хочете змінити. Наприклад, припустимо, що ви хочете встановити колонку з датою createdAt у поточну дату, тільки тоді, коли до сутності буде застосовано збереження в перший раз (тобто, додавання нового запису). Щоб зробити це, визначте зворотній виклик для події 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

Деякі зворотні виклики життєвого циклу отримують аргумент, який надає доступ до корисної інформації, такої як поточний менеджер сутностей (наприклад, зворотній виклик preUpdate отримує аргумент PreUpdateEventArgs $event).

Слухачі сутностей Doctrine

Слухачі сутностей визначаються як PHP-класи, які слухають одну подію Doctrine для одного класу сутності. Наприклад, припустимо, що ви хочете відправити декілька повідомлень, коли сутність User змінюється в базі даних.

Спочатку визначте слухача для події 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 для всіх сутностей додатку. Наприклад, припустимо, що ви хочете оновлювати пошуковий індекс, кожний раз, коли додається нова сутність в базу даних. Щоб зробити це, оголосіть слухача для події 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.

Далі додайте атрибут #[AsDoctrineListener] до класу, щоб включити його як слухача Doctrine у вашому додатку:

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/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 також може бути параметром конфігурації .