У меня есть служба, которая требует, чтобы водитель выполнял фактическую работу. Сам драйвер в контексте 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
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 .
Обратите внимание, что расширение вызовет исключение, если служба не существует.
С помощью языка выражения контейнера службы у вас есть доступ к следующим двум функциям в файлах конфигурации :
- 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'))]]
Сначала я подумал, что это просто вопрос правильной передачи символа @. Но я попробовал разные комбинации и пришел к выводу, что вы не можете передать реальную услугу в качестве параметра. Может быть, кто-нибудь еще вмешается и покажет, как это сделать.
Тогда я решил, что это просто вопрос использования определения сервиса и передачи ему ссылки. Сначала я попробовал это в обычном расширении, но контейнер еще не содержит всех определений сервисов.
Поэтому я использовал проход компилятора: 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)));
}
}
Выньте раздел звонков из служебного файла и он должен заработать. Я подозреваю, что есть способ попроще, но, может быть, и нет.
@my_scope.mailer.driver
должен быть услугой, но не определен как услуга. Чтобы получить строковый параметр с именем my_scope.mailer.driver
, вам необходимо заключить его в %
: %my_scope.mailer.driver%
.
Поэтому вам нужно передать @%my_scope.mailer.driver%
в качестве параметра службе. Парсер Yml заменит %my_scope.mailer.driver%
на соответствующее значение параметра, и только тогда он будет вызван как служба.
[setDriver, [@%my_scope.mailer.driver%]]
, что дает ошибку, показанную в моем комментарии.
@%my_scope.mailer.driver%
в настоящее время не работает. Также alias: %my_scope.mailer.driver%
тоже не работает. (Проверено на Symfony 2.4.2).
Похожие вопросы
Связанные вопросы
Новые вопросы
symfony
Symfony относится как к фреймворку PHP для создания веб-приложений, так и к набору компонентов, на которых строится фреймворк. Этот тег относится к поддерживаемым в настоящее время основным версиям 3.x, 4.x, 5.x и 6.x. Кроме того, вы можете указать точную версию, используя соответствующий тег. Этот тег не следует использовать для вопросов о Symfony 1.x. Вместо этого используйте тег Symfony1.
$container->setAlias