Подключение служб Symfony2 в DIC с использованием параметров конфигурации - PullRequest
2 голосов
/ 21 ноября 2011

Я бы хотел иметь возможность подключать сервисы из параметров конфигурации, чтобы я мог использовать фиктивную реализацию клиента веб-сервиса в среде dev и использовать подходящий клиент в prod env.

Вот фрагмент из моих services.xml:

<parameters>
    ...
    <parameter key="api_client.api_service.id">ic_domain_security.api_mock_service</parameter>
    ...
</parameters>

<services>
...
    <service id="ic_domain_security.api_client"
                 class="%api_client.class%"
                 public="true">
            <tag name="monolog.logger" channel="domainApiClient"/>
            <argument type="service" id="%api_client.api_service.id%"/>
            <call method="setLogger">
                <argument type="service" id="logger" on-invalid="null"/>
            </call>
        </service>

        <service id="ic_domain_security.api_restful_webservice"
                 class="IC\DomainSecurityBundle\ApiClient\ApiRestfulWebService"
                 public="false">
            <tag name="monolog.logger" channel="domainApiRestfulWebService"/>
            <argument>%ic_domain_security.service_url%</argument>
            <argument type="service" id="logger" on-invalid="null"/>
        </service>

        <service id="ic_domain_security.api_mock_service"
                 class="IC\DomainSecurityBundle\Tests\Mock\ApiMockService"
                 public="false">
            <call method="setLogger">
                <argument type="service" id="logger" on-invalid="null"/>
            </call>
        </service>
...
</services>

Строка <argument type="service" id="%api_client.api_service.id%"/> пытается определить службу для передачи на основе параметра. В итоге я пытаюсь вставить параметр api_client.api_service.id в файл (ы) конфигурации.

К сожалению, невозможно определить идентификаторы сервисов с помощью параметров, и я получаю следующую ошибку:

ServiceNotFoundException: The service "ic_domain_security.api_client" has a dependency on a non-existent service "%api_client.api_service.id%".

Есть ли способ сделать то, что я объяснил выше, чтобы сервисные идентификаторы можно было передавать с использованием параметров конфигурации?

Ответы [ 3 ]

3 голосов
/ 21 ноября 2011

Лучший способ сделать то, что вы пытаетесь сделать, это определить семантическую конфигурацию для вашего пакета.В любом случае, я не думаю, что вы можете сделать это без написания некоторого PHP-кода.

Это означает, что вы должны написать класс конфигурации и класс расширения внедрения зависимости.Это довольно простой процесс, хотя он не исчерпывающе документирован в документации по Symfony2.Мой совет - взять несколько простых пакетов (например, я очень много узнал о FOSUserBundle) и посмотреть, как они определяют свою конфигурацию.

Здесь вы можете найти введение в систему расширения DIC.

2 голосов
/ 23 августа 2012

Чтобы разрешить определение класса службы в конфигурации, вы должны использовать семантическую конфигурацию (как упомянуто выше).Ниже приведен пример:

Вот простая служба (служба может быть любого класса php):

<?php

namespace Sample\Bundle\ServiceBundle\Service;

class Awesomeness {
  public function doSomething()
  {
    return 'I did something';
  }
}

Существует несколько способов подключить службу к контейнеру службы Symfony2.Для этого примера мы создадим файл services.xml в ServiceBundle / Resources / config (это описано во всех документах Symfony2):

    <?xml version="1.0" ?>

    <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="sample_service_bundle.awesomess" class="%sample_service_bundle.awesomeness.class%">
                </service>
        </services>
</container>

Обратите внимание, что я не добавил стандартный раздел в службы.xml.Это намеренно.Мы будем модифицировать класс расширения пакета и класс семантической конфигурации для добавления параметра из config.yml.

Чтобы разрешить определение класса обслуживания в конфигурации, мы сначала добавим конфигурацию в DependencyInjection / Configuration.php.:

class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('brain_glove_site');

        $rootNode
                ->children()
                        ->scalarNode('awesomeness_class')->cannnotBeEmpty()->defaultValue('Sample\\Bundle\\ServiceBundle\\Service\\Awesomeness')->end()
                ->end()
        ;

        return $treeBuilder;
    }
}

Здесь мы создали новый параметр конфигурации с именем 'awesomeness_class', который можно установить в config.yml, и предоставили ему значение по умолчанию.

Однако нам все еще нужно создатьфактический параметр, который служба может использовать.Это делается в классе расширения, который будет использовать ваш пакет.Если ваш пакет называется SampleServiceBundle, то класс, который вы редактируете, будет DependencyInjection \ SampleServiceExtension:

class SampleServiceExtension extends Extension
{
    /**
     * {@inheritDoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

        $loader->load('services.xml');

        $container->setParameter('sample_service_bundle.awesomeness.class', $config['awesomeness_class']);
    }
}

Вызов метода $ container-> setParameter () вводит параметр в ваш контейнер, который затем будет использоваться вашимопределение службы.

На этом этапе вам больше ничего не нужно делать, чтобы использовать службу как обычно.

Если вы хотите изменить класс, который создается службой в конфигурации, вы можетеedit config_dev.yml:

...
sample_service:
    awesomeness_class: SomeOtherBundle/SomeOtherClass

И теперь ваше приложение будет использовать другой класс для этой службы.

0 голосов
/ 26 августа 2012

Я никогда не понимал, как определять динамические аргументы (с параметром в качестве идентификатора службы). Единственный способ найти эту работу - использовать фабричную И семантическую конфигурацию.

В основном вам нужно сделать следующее:

  • Определить параметр с помощью семантической конфигурации
  • Создать фабрику, которая может возвращать правильный сервис на основе этого параметра (вашей фабрике для этого потребуется сам экземпляр ServiceContainer)
  • Определите сервис, который использует фабрику для создания экземпляра (это даст вам идентификатор сервиса, который ссылается на динамически создаваемый объект)
  • Используйте этот сервис, называйте зависимость других сервисов.

Информацию о DIC и фабриках вы можете найти здесь http://symfony.com/doc/current/components/dependency_injection/factories.html

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

Здесь вы можете найти небольшой пример того, что я сделал: https://gist.github.com/32378f4000df482a8b9b

Это не вся кодовая база, но вы можете увидеть службу "vich_uploader.storage", которая использует фабричную службу для создания экземпляра фактической службы. StorageFactory вернет правильный сервис на основе параметра DIC 'vich_uploader.storage_service', который определяется семантической конфигурацией пакета.

Все остальные сервисы будут использовать "vich_uploader.storage" в качестве аргумента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...