Як завантажувати конфігурацію сервісу всередині пакета

Дата оновлення перекладу 2022-12-11

Як завантажувати конфігурацію сервісу всередині пакета

Сервіси, створені пакетами, не визначаються в основному файлі config/services.yaml, який використовується додатком, а визначаються у самих пакетах. Ця стаття пояснює, як створювати та завантажувати файли сервісу, використовуючи структуру каталогів пакета.

Створення класу розширення

Для того, щоб завантажувати конфігурацію сервісів, вам потрібно створити Розширення впровадження залежності (ВЗ) для вашого пакета. За замовчуванням, клас Розширення має слідувати таким угодам (але пізніше ви дізнаєтеся, як їх пропускати, якщо це необхідно):

  • Воно має жити у просторі імен DependencyInjection пакета;
  • Воно має реалізовувати ExtensionInterface, що зазвичай досягається шляхом розширення класу Extension ;
  • Імʼя дорівнює імені пакета з суфіксом Bundle, зміненим на Extension (наприклад клас розширення AcmeBundle буде називатися AcmeExtension, а AcmeHelloBundle - AcmeHelloExtension, а для AcmeHelloBundle - AcmeHelloExtension ).

Ось як має виглядати розширення AcmeHelloBundle:

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

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

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(), щоб він повертав правильне додаткове імʼя ВЗ. Додаткове імʼя ВЗ - це імʼя, що використовується для посилання на пакет у контейнері (наприклад, у файлах config/packages/). За замовчуванням, це робиться шляхом видалення суфікса 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\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

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

Інші доступні завантажувачі - YamlFileLoader та PhpFileLoader.

Використання конфігурації для зміни сервісів

Розширення такаож є класом, який займається конфігурацією для цього конкретного пакета (наприклад, конфігурація у config/packages/<bundle_alias>.yaml). Щоб прочитати більше про це, див. статтю "Як створити дружню конфігурацію для пакета".

Завантаження сервісів напряму у ваш клас Пакета

6.1

Клас AbstractBundle було представлено в Symfony 6.1.

Як варіант, ви можете визначати та завантажувати конфігурацію сервісів напряму у клас пакета замість створення спеціального класу Extension. Ви можете зробити це, розширивши з AbstractBundle та визначивши метод loadExtension():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ...
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeHelloBundle extends AbstractBundle
{
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        // завантажити файл XML, PHP або Yaml
        $container->import('../config/services.xml');

        // ви також можете додати або замінити параметри та сервіси
        $container->parameters()
            ->set('acme_hello.phrase', $config['phrase'])
        ;

        if ($config['scream']) {
            $container->services()
                ->get('acme_hello.printer')
                    ->class(ScreamingPrinter::class)
            ;
        }
    }
}

Цей метод працює схоже на метод Extension::load(), але використовує новий API, щоб визначати та імпортувати конфігурацію сервісу.

Note

На відміну від параметра $configs в Extension::load(), параметр $config вже злито та оброблено AbstractBundle.

Note

loadExtension() викликається тільки під час компіляції.

Додавання класів для компіляції

Пакети можуть натякати Symfony про те, які з їх класів містять анотації, щоб вони були скомпільовані при генеруванні кешу додатка для покращення загальної ефективності. Визначте список класів з анотаціями для компіляції у методі addAnnotatedClassesToCompile():

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

    $this->addAnnotatedClassesToCompile([
        // ви можете визначити повністю кваліфіковані імена класів...
        'App\\Controller\\DefaultController',
        // ... але також підтримуються глобальні патерни:
        '**Bundle\\Controller\\',

        // ...
    ]);
}

Note

Якщо якийсь клас розширюється з інших класів, всі його батьки автоматично додаються у список класів для компіляції.

Схеми трансформуються у справжні простори імен класів, викоирстовуючи карту класів, згенеровану Composer. Отже, до використання цих схем, ви маєте згенерувати повну карту класів, що виконує команду Composer dump-autoload.

Caution

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