Как управлять общими зависимостями с родительскими сервисами

Дата обновления перевода 2024-07-27

Как управлять общими зависимостями с родительскими сервисами

По мере добавление функционала в ваше приложение, вы также можете заиметь родственные классы, которые имеют некоторые общие зависимости. Например, вы можете иметь несколько классов хранилища, которым нужен сервис doctrine.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() будет вызван, когда будет инстанциирован AppBundle\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: false

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

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

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