У меня есть служба, которая требует, чтобы водитель выполнял фактическую работу. Сам драйвер в контексте Symfony 2 - это просто еще одна услуга.

Чтобы проиллюстрировать упрощенную версию:

services:
  # The driver services.
  my_scope.mailer_driver_smtp:
    class: \My\Scope\Service\Driver\SmtpDriver

  my_scope.mailer_driver_mock:
    class: \My\Scope\Service\Driver\MockDriver

  # The actual service.
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@my_scope.mailer_driver_smtp]]

Как показано выше, я могу внедрить любую из двух служб драйверов в службу Mailer. Проблема, конечно, в том, что внедряемая служба драйвера жестко запрограммирована. Итак, я хочу параметризовать @my_scope.mailer_driver_smtp.

Я делаю это, добавляя запись в свой parameters.yml

my_scope_mailer_driver: my_scope.mailer_driver_smtp

Затем я могу использовать это в своем config.yml и назначить параметр семантической открытой конфигурации [1]:

my_scope:
  mailer:
    driver: %my_scope_mailer_driver%

В конце концов, в классе Configuration моего пакета я установил параметр для контейнера:

$container->setParameter('my_scope.mailer.driver', $config['mailer']['driver'] );

Значение параметра контейнера my_scope.mailer.driver теперь равно my_scope.mailer_driver_smtp, которое я установил в parameters.yml, который, насколько я понимаю, является просто строкой.

Если я теперь использую имя параметра из контейнера, я получаю сообщение об ошибке с жалобой на отсутствие такой службы. Например:

services:
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@my_scope.mailer.driver]]

Вышеуказанное приведет к ошибке:

[Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException]                                          
The service "my_scope.mailer" has a dependency on a non-existent service "my_scope.mailer.driver"

Теперь вопрос в том, каков правильный синтаксис для внедрения этой службы на основе параметров контейнера?

[1] http://symfony.com/doc/current/cookbook/bundles/ extension.html

5
Luke 13 Мар 2014 в 04:57
1
Вместо этого вам нужно использовать $container->setAlias
 – 
Touki
13 Мар 2014 в 11:56
Интересно. Вы указываете на принятый ответ на вопрос, который я задавал ранее. Как вы думаете, вы можете добавить эту ссылку выше в ответ, чтобы я мог принять ее как правильный ответ? Спасибо.
 – 
Luke
17 Мар 2014 в 02:37

4 ответа

Лучший ответ

На этот вопрос есть аналогичный ответ здесь

Я думаю, что лучший способ использовать такое определение - использовать псевдоним службы.
Это может выглядеть так

<▪Acme\FooBundle\DependencyInjection\AcmeFooExtension

public function load(array $configs, ContainerBuilder $container)
{
    $configuration = new Configuration;
    $config = $this->processConfiguration($configuration, $configs);

    $loader = new Loader\YamlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../Resources/config')
    );
    $loader->load('services.yml');

    $alias = $config['mailer']['driver'];
    $container->setAlias('my_scope.mailer_driver', $alias);
}

Это приведет к наложению псевдонима на службу, которую вы определили в my_scope.mailer.driver, на my_scope.mailer_driver, которую вы можете использовать как любую другую службу.

< sizesservices.yml

services:
    my_scope.mailer_driver:
        alias: my_scope.mailer_driver_smtp # Fallback

    my_scope.mailer_driver_smtp:
        class: My\Scope\Driver\Smtp

    my_scope.mailer_driver_mock:
        class: My\Scope\Driver\Mock

    my_scope.mailer:
        class: My\Scope\Mailer
        arguments:
            - @my_scope.mailer_driver

При таком дизайне служба будет меняться всякий раз, когда вы изменяете параметр my_scope.mailer_driver в своем config.yml .
Обратите внимание, что расширение вызовет исключение, если служба не существует.

5
Community 23 Май 2017 в 15:00

С помощью языка выражения контейнера службы у вас есть доступ к следующим двум функциям в файлах конфигурации :

  • service - возвращает данную услугу (см. пример ниже);
  • параметр - возвращает конкретное значение параметра (синтаксис аналогичен сервису)

Итак, чтобы преобразовать имя параметра в ссылку на сервис, вам понадобится что-то вроде этого:

parameters:
  my_scope_mailer_driver: my_scope.mailer_driver_smtp

services:
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@=service(parameter('my_scope_mailer_driver'))]]
5
Alexei Tenitski 25 Июл 2017 в 00:20
Я только что заметил это. Это действительно хороший способ сделать это. Попробую в следующий раз, когда мне нужно будет сделать что-то подобное.
 – 
Luke
24 Июл 2017 в 07:55

Сначала я подумал, что это просто вопрос правильной передачи символа @. Но я попробовал разные комбинации и пришел к выводу, что вы не можете передать реальную услугу в качестве параметра. Может быть, кто-нибудь еще вмешается и покажет, как это сделать.

Тогда я решил, что это просто вопрос использования определения сервиса и передачи ему ссылки. Сначала я попробовал это в обычном расширении, но контейнер еще не содержит всех определений сервисов.

Поэтому я использовал проход компилятора: http://symfony.com/doc/current/ cookbook / service_container / compiler_passes.html

Класс Pass выглядит так:

namespace Cerad\Bundle\AppCeradBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class Pass1 implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        // Set in the Extension: my_scope.mailer_driver_smtp
        $mailerDriverId = $container->getParameter('my_scope.mailer.driver');

        $def = $container->getDefinition('my_scope.mailer');

        $def->addMethodCall('setDriver', array(new Reference($mailerDriverId)));
    }
}

Выньте раздел звонков из служебного файла и он должен заработать. Я подозреваю, что есть способ попроще, но, может быть, и нет.

3
Cerad 13 Мар 2014 в 21:23

@my_scope.mailer.driver должен быть услугой, но не определен как услуга. Чтобы получить строковый параметр с именем my_scope.mailer.driver, вам необходимо заключить его в %: %my_scope.mailer.driver%.

Поэтому вам нужно передать @%my_scope.mailer.driver% в качестве параметра службе. Парсер Yml заменит %my_scope.mailer.driver% на соответствующее значение параметра, и только тогда он будет вызван как служба.

0
Michael Sivolobov 13 Мар 2014 в 08:46
Спасибо за это. Я пробовал это, но это не работает. Я получаю очень похожую ошибку: "Служба "my_scope.mailer" зависит от несуществующей службы "%my_scope.mailer.driver%"" (обратите внимание на символы процента)
 – 
Luke
13 Мар 2014 в 10:08
Удалите кавычки вокруг знаков %
 – 
Igor Pantović
13 Мар 2014 в 10:57
Вокруг него нет кавычек, кавычки взяты из сообщения об исключении. Я использую это: [setDriver, [@%my_scope.mailer.driver%]], что дает ошибку, показанную в моем комментарии.
 – 
Luke
13 Мар 2014 в 13:39
Синтаксис @%my_scope.mailer.driver% в настоящее время не работает. Также alias: %my_scope.mailer.driver% тоже не работает. (Проверено на Symfony 2.4.2).
 – 
Konstantin Pelepelin
20 Мар 2014 в 14:46