Как упростить конфигурацию нескольких пакетов

Как упростить конфигурацию нескольких пакетов

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

Существует возможность удалить недостаток подхода нескольких пакетов, включив одно расширение, которое добавляло бы вначале настроек любого пакета некоторую информацию. Оно может использовать настройки, определённые в app/config/config.yml, чтобы добавлять к настройкам информацию так же, как если бы они были ясно написаны пользователем в конфигурации приложения.

Например, это можеть быть использовано для конфигурации имени менеджера сущностей для использования в нескольких пакетах. Или для включения необязательной функции, зависящей от другого загружаемого пакета.

Для того, чтобы дать расширению возможность делать это, оно должно реализовывать PrependExtensionInterface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;

use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class AcmeHelloExtension extends Extension implements PrependExtensionInterface
{
    // ...

    public function prepend(ContainerBuilder $container)
    {
        // ...
    }
}

Внутри метода prepend(), разработчики имеют полный доступ к экземпляру ContainerBuilder прямо перед вызовом метода load() в каждом из зарегистрированных расширений пакета. Для того, чтобы добавить вначале настройки к пакету, разработчики расширений могут использовать метод prependExtensionConfig() в экземпляре ContainerBuilder. Так как этот метод добавляет только настройки,любые другие настройки, ясно указанные в app/config/config.yml, будут переопределять их.

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

 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
30
31
32
33
34
35
36
37
38
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
public function prepend(ContainerBuilder $container)
{
    // получить все пакеты
    $bundles = $container->getParameter('kernel.bundles');
    // определить, зарегистрирован ли AcmeGoodbyeBundle
    if (!isset($bundles['AcmeGoodbyeBundle'])) {
        // отключить AcmeGoodbyeBundle в пакетах
        $config = array('use_acme_goodbye' => false);
        foreach ($container->getExtensions() as $name => $extension) {
            switch ($name) {
                case 'acme_something':
                case 'acme_other':
                    // установить use_acme_goodbye как false в конфигурации
                    // acme_something и acme_other
                    //
                    // отметьте, что если пользователь вручную сконфигурировал
                    // use_acme_goodbye как true в app/config/config.yml,
                    // то настройка в итоге будет true, а не false
                    $container->prependExtensionConfig($name, $config);
                    break;
            }
        }
    }

    // обработайте кофнигурацию AcmeHelloExtension
    $configs = $container->getExtensionConfig($this->getAlias());
    // используйте класс конфигурации, чтобы сгенерировать массив конфигурации с
    // настройками "acme_hello"
    $config = $this->processConfiguration(new Configuration(), $configs);

    // проверьте, установлен ли entity_manager_name в конфигурации "acme_hello"
    if (isset($config['entity_manager_name'])) {
        // добавьте в начале настроек acme_something entity_manager_name
        $config = array('entity_manager_name' => $config['entity_manager_name']);
        $container->prependExtensionConfig('acme_something', $config);
    }
}

Вышенаписанное будет эквивалентно написанию следующего в app/config/config.yml в случае, если AcmeGoodbyeBundle не зарегистрирован, а настройка entity_manager_name для acme_hello установлена, как non_default:

  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # app/config/config.yml
    acme_something:
        # ...
        use_acme_goodbye: false
        entity_manager_name: non_default
    
    acme_other:
        # ...
        use_acme_goodbye: false
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <!-- app/config/config.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:acme-something="http://example.org/schema/dic/acme_something"
        xmlns:acme-other="http://example.org/schema/dic/acme_other"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <acme-something:config use-acme-goodbye="false">
            <acme-something:entity-manager-name>non_default</acme-something:entity-manager-name>
        </acme-something:config>
    
        <acme-other:config use-acme-goodbye="false" />
    
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    // app/config/config.php
    $container->loadFromExtension('acme_something', array(
        // ...
        'use_acme_goodbye' => false,
        'entity_manager_name' => 'non_default',
    ));
    $container->loadFromExtension('acme_other', array(
        // ...
        'use_acme_goodbye' => false,
    ));
    

Больше о пакетах и использованием PrependExtensionInterface

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

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.