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

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

В Symfony вы обнаружите, что вы используете много сервисов. Эти сервисы могут быть зарегистрированы в каталоге app/config/ вашего приложения. Но когда вы хотите отделить пакет для использования в других проектах, вы захотите включить конфигурацию сервиса в сам пакет. Эта статья научит вас, как это делать.

Создание класса расширения

Для того, чтобы загружать конфигурацию сервисов, вам нужно создать Расширение внедрения зависимости (ВЗ) для вашего пакета. Этот класс имеет некоторые соглашения для того, чтобы быть авто-определяемым. Но возже вы увидите, как вы можете изменить его в соответствии с вашими предпочтениями. По умолчанию, расширение должно соответствовать следующим соглашениям:

  • Оно должно жить в пространстве имён DependencyInjection пакета;
  • Имя равняется имени пакета с суффиксом Bundle, заменённым на Extension (например класс расширения AppBundle будет называться AppExtension, а AcmeHelloBundle - AcmeHelloExtension).

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

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

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

class AcmeHelloExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        // ... позднее вы будете загружать здесь файлы
    }
}

Ручная регистрация класса расширения

Если не следовать соглашениям, вам нужно будет вручную регистрировать ваше расширение. Чтобы сделать это, вам нужно переопределить метод Bundle::getContainerExtension(), чтобы он возвращал экземпляр расширения:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;

class AcmeHelloBundle extends Bundle
{
    public function getContainerExtension()
    {
        return new UnconventionalExtensionClass();
    }
}

Так как новое имя класса расширения не следует соглашениям именования, вам нужно также переопределить Extension::getAlias(), чтобы он возвращал правильное дополнительное имя ВЗ. Дополнительное имя ВЗ - это имя, используемое для ссылки на пакет в контейнере (например, в файле app/config/config.yml). По умолчанию, это делается путём удаления суффикса Extension и конвертации имени класса на нижнее подчёркивание (например, дополнительное имя ВЗ AcmeHelloExtension - это acme_hello).

Использование метода load()

В методе load(), все сервисы и параметры относятся к загружаемому расширению. Этот метод получает не настоящий экземпляр контейнера, а копию. Контейнер имеет только параметры из настоящего контейнера. После загрузки сервисов и параметром, копия будет соединена с настоящим контейнером, чтобы убедитьс, что все сервисы и параметры также добавлены в него.

В методе load(), вы можете использовать PHP-код для регистрации определений сервиса, но чаще вы будете помещать эти определения в файл конфигурации (используя форматы Yaml, XML или PHP). К счастью, вы можете использовать загрузчики файлов в расширениях!

Например, предположите, что у вас есть файл по имени services.xml в каталоге Resources/config вашего пакета, ваш метод load() выглядит так:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;

// ...
public function load(array $configs, ContainerBuilder $container)
{
    $loader = new XmlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../Resources/config')
    );
    $loader->load('services.xml');
}

Другие доступные загрузчики - YamlFileLoader, PhpFileLoader и IniFileLoader.

Note

IniFileLoader может быть использован только для загрузки параметров и загружать их только в виде строк.

Caution

Если вы удалили файл по умолчанию с определенями сервиса (т.е. app/config/services.yml), убедитесь, что вы также удалили его из ключа imports в app/config/config.yml.

Использование конфигурации для изменения сервисов

Расширение также является классом, которое занимается конфигурацией для этого конкретного пакета (например, конфигурация в app/config/config.yml). Чтобы прочитать больше об этом, см. статью "How to Create Friendly Configuration for a Bundle" article.

Добавление классов для компиляции

Note

Метод addClassesToCompile() был осуждён в Symfony 3.3, и будет удалён в Symfony 4.0. Если вы хотите использвать этот метод и иметь совместимость с Symfony 4.0, проверьте, существует ли метод перед тем, как его вызывать.

Symfony создаёт большой файл classes.php в каталоге кеша для агрегации содержимого PHP-классов, которые используются в каждом запросе. Это сокращает операции ввода/вывода и усиливает производительность приложения.

New in version 3.2: Метод addAnnotatedClassesToCompile() был добавлен в Symfony 3.2.

Ваши пакеты также могут добавлять собственные класс в файлы благодаря методам addClassesToCompile() и addAnnotatedClassesToCompile() (оба работают одним способом, но второй - для классов, содержащих PHP-аннотации). Определите классы, чтобы скомпилировать их в виде массива полностью определённых имён класса:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
use AppBundle\Manager\UserManager;
use AppBundle\Utils\Slugger;

// ...
public function load(array $configs, ContainerBuilder $container)
{
    // ...

    // этот метод не может компилировать классы, содержащие PHP-аннотации
    $this->addClassesToCompile(array(
        UserManager::class,
        Slugger::class,
        // ...
    ));

    // добавляйте здесь только классы, содержащие PHP-аннотации
    $this->addAnnotatedClassesToCompile(array(
        'AppBundle\\Controller\\DefaultController',
        // ...
    ));
}

Note

Если какой-то класс расширяется из других классов, все его родители автоматически включаются в список классов для компиляции.

New in version 3.2: Опция добавления классов для компиляции используя схемы была представлена в Symfony 3.2.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// ...
public function load(array $configs, ContainerBuilder $container)
{
    // ...

    $this->addClassesToCompile(array(
        '**Bundle\\Manager\\',
        // ...
    ));

    $this->addAnnotatedClassesToCompile(array(
        '**Bundle\\Controller\\',
        // ...
    ));
}

Схемы трансформируются в настоящие пространства имён классов, используя карту классов, сегенерированную Composer. Следовательно, до использования этих схем, вы должны сгенерировать полную карту классов, выполняющую команду Composer dump-autoload.

Caution

Эта техника не может быть использована, когда классы для компиляции используют константы __DIR__ или __FILE__, так как их значения будут изменяться при загрузке этих классов из файла classes.php.

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