Як управляти спільними залежностями з батьківськими сервісами

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

Як управляти спільними залежностями з батьківськими сервісами

За мірою додавання функціоналу у ваш додаток, ви також можете стати власником споріднених класів, які мають деякі спільні залежності. Наприклад, ви можете мати декілька класів сховища, яким потрібен сервіс doctrine.orm.entity_manager та необовʼязковий сервіс logger:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Repository/BaseDoctrineRepository.php
namespace App\Repository;

use Doctrine\ORM\EntityManager;
use Psr\Log\LoggerInterface;

// ...
abstract class BaseDoctrineRepository
{
    protected LoggerInterface $logger;

    public function __construct(
        protected EntityManager $entityManager,
    ) {
    }

    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    // ...
}

Ваші класи дочірнього сервісу можуть виглядати так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Repository/DoctrineUserRepository.php
namespace App\Repository;

use App\Repository\BaseDoctrineRepository;

// ...
class DoctrineUserRepository extends BaseDoctrineRepository
{
    // ...
}

// src/Repository/DoctrinePostRepository.php
namespace App\Repository;

use App\Repository\BaseDoctrineRepository;

// ...
class DoctrinePostRepository extends BaseDoctrineRepository
{
    // ...
}

Сервіс-контейнер дозволяє вам розширрювати батьківські сервіси для того, щоб уникнути дубльованих визначень сервісів:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# config/services.yaml
services:
    App\Repository\BaseDoctrineRepository:
        abstract:  true
        arguments: ['@doctrine.orm.entity_manager']
        calls:
            - setLogger: ['@logger']

    App\Repository\DoctrineUserRepository:
        # розширити сервіс App\Repository\BaseDoctrineRepository
        parent: App\Repository\BaseDoctrineRepository

    App\Repository\DoctrinePostRepository:
        parent: App\Repository\BaseDoctrineRepository

    # ...

У цьому контексті, наявність сервісу parent передбачає, що аргументи і виклики методу батьківського сервісу повинні бути використані для дочірніх сервісів. А точніше, EntityManager буде впроваджений, а setLogger() буде викликаний, коли буде інстанційовано App\Repository\DoctrineUserRepository.

Всі атрибути батьківського сервісу є спільними з дочірніми, окрім shared, abstract і tags. Вони не успадковуються від батька.

Tip

У продемонстрованих прикладах, класи, які мають спільну конфігурацію, також розширюються з одного батьківського класу у PHP. Це зовсім не обовʼязково. Ви можете просто вилучити спільні частини схожих визначень сервісу у батьківський сервіс, не розширюючи також батьківський клас у PHP.

Перевизначення батьківських залежностей

Можуть бути випадки, коли ви захочете перевизначити те, який сервіс впроваджується тільки для одного з дочірніх сервісів. Ви можете перевизначити більшість налаштувань, просто вказавши їх у дочірньому класі:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# config/services.yaml
services:
    # ...

    App\Repository\DoctrineUserRepository:
        parent: App\Repository\BaseDoctrineRepository

        # перевизначає приватне налаштування батьківського сервісу
        public: true

        # додає аргумент '@app.username_checker' до батьківського
        # списку аргументів
        arguments: ['@app.username_checker']

    App\Repository\DoctrinePostRepository:
        parent: App\Repository\BaseDoctrineRepository

        # перевизначає перший аргумент (використовуючи спеціальний ключ index_N)
        arguments:
            index_0: '@doctrine.custom_entity_manager'