Как управлять общими зависимостями с родительскими сервисами¶
По мере добавление функционала в ваше приложение, вы также можете заиметь
родственные классы, которые имеют некоторые общие зависимости. Например,
вы можете иметь несколько классов хранилища, которым нужен сервис
doctrine.entity_manager
и необязательный сервис``logger``:
// src/AppBundle/Repository/BaseDoctrineRepository.php
namespace AppBundle\Repository;
// ...
abstract class BaseDoctrineRepository
{
protected $entityManager;
protected $logger;
public function __construct(EntityManagerInterface $manager)
{
$this->entityManager = $manager;
}
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
// ...
}
Так же, как вы используете наследование PHP, чтобы избежать дубликатов в вашем PHP-коде, сервис-контейнер позволяет вам расширить родительские сервисы, чтобы избежать дубликатов определений сервиса:
- YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
services: AppBundle\Repository\BaseDoctrineRepository: abstract: true arguments: ['@doctrine.entity_manager'] calls: - [setLogger, ['@logger']] AppBundle\Repository\DoctrineUserRepository: # расширить сервис AppBundle\Repository\BaseDoctrineRepository parent: AppBundle\Repository\BaseDoctrineRepository AppBundle\Repository\DoctrinePostRepository: parent: AppBundle\Repository\BaseDoctrineRepository # ...
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
<?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="AppBundle\Repository\BaseDoctrineRepository" abstract="true"> <argument type="service" id="doctrine.entity_manager" /> <call method="setLogger"> <argument type="service" id="logger" /> </call> </service> <!-- extends the AppBundle\Repository\BaseDoctrineRepository service --> <service id="AppBundle\Repository\DoctrineUserRepository" parent="AppBundle\Repository\BaseDoctrineRepository" /> <service id="AppBundle\Repository\DoctrinePostRepository" parent="AppBundle\Repository\BaseDoctrineRepository" /> <!-- ... --> </services> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use AppBundle\Repository\DoctrineUserRepository; use AppBundle\Repository\DoctrinePostRepository; use AppBundle\Repository\BaseDoctrineRepository; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Reference; $container->register(BaseDoctrineRepository::class) ->setAbstract(true) ->addArgument(new Reference('doctrine.entity_manager')) ->addMethodCall('setLogger', array(new Reference('logger'))) ; // расширить сервис AppBundle\Repository\BaseDoctrineRepository $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrineUserRepository::class); $container->setDefinition(DoctrineUserRepository::class, $definition); $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrinePostRepository::class); $container->setDefinition(DoctrinePostRepository::class, $definition); // ...
В этом контексте, наличие сервиса parent
предполагает, что аргументы и
вызовы метода родительского сервиса должны быть использованы для дочерних
сервисов. В особенности, EntityManager
будет внедрён, а setLogger()
будет вызван, когда будет инстанциирован AppBundle\Repository\DoctrineUserRepository
.
Все атрибуты родительского сервиса являются общими с дочерним, кроме
shared
, abstract
и tags
. Они не наследуются от родителя.
Note
Если у вас есть раздел _defaults
в вашем файле, все дочерние сервисы
должны ясно переопределять эти значения, чтобы избежать двусмысленности.
Вы увидите чёткое сообщение ошибки об этом.
Tip
В показанных примерах, класс, которые имеют общую конфигурацию, ткакже расширяются из одного родительского класса в PHP. Это вовсе не обязательно. Вы можете просто извлечь общие части схожих определений сервиса в родительский сервис, не расширяя также родительский класс в PHP.
Переопределение родительских зависимостей¶
Могут быть времена, когда вы захотите переопределить то, какой сервис внедряется только для одного из дочерних сервисов. Вы можете пеоепределить болошиниство настроек просто указав их в дочернем классе:
- YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
services: # ... AppBundle\Repository\DoctrineUserRepository: parent: AppBundle\Repository\BaseDoctrineRepository # переопределяет публичную настройку родительского сервиса public: false # добавляет аргумент '@app.username_checker' к родительскому # списку аргументов arguments: ['@app.username_checker'] AppBundle\Repository\DoctrinePostRepository: parent: AppBundle\Repository\BaseDoctrineRepository # переопределяет первый аргумент (используя специальный ключ index_N) arguments: index_0: '@doctrine.custom_entity_manager'
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
<?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <!-- ... --> <!-- переопределяет публичную настройку родительского сервисаe --> <service id="AppBundle\Repository\DoctrineUserRepository" parent="AppBundle\Repository\BaseDoctrineRepository" public="false" > <!-- добавляет аргумент '@app.username_checker' к родительскому списку аргументов --> <argument type="service" id="app.username_checker" /> </service> <service id="AppBundle\Repository\DoctrinePostRepository" parent="AppBundle\Repository\BaseDoctrineRepository" > <!-- переопределяет первый аргумент (используя атрибут index) --> <argument index="0" type="service" id="doctrine.custom_entity_manager" /> </service> <!-- ... --> </services> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
use AppBundle\Repository\DoctrineUserRepository; use AppBundle\Repository\DoctrinePostRepository; use AppBundle\Repository\BaseDoctrineRepository; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Reference; // ... $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrineUserRepository::class); // переопределяет публичную настройку родительского сервиса $definition->setPublic(false); // добавляет аргумент '@app.username_checker' к родительскому списку аргументов $definition->addArgument(new Reference('app.username_checker')); $container->setDefinition(DoctrineUserRepository::class, $definition); $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrinePostRepository::class); // переопределяет первый аргумент $definition->replaceArgument(0, new Reference('doctrine.custom_entity_manager')); $container->setDefinition(DoctrinePostRepository::class, $definition);
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.