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

Дата оновлення перекладу 2025-01-10

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

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

Існує два різних способи зробити це:

  1. Завантажити ваші сервіси в основний клас пакету : рекомендовано для нових пакетів та для пакетів, що слідують рекомендованій структурі каталогу ;
  2. Створити клас розширення для завантаження файлів конфігурації сервісу : це був традиційний спосіб, але наразі він рекомендований лише для пакетів, що слідують успадкованій структурі каталогів .

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

У пакетах, що розширюють клас 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() викликається лише під час компіляції.

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

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

Розширення впровадження залежності визначається як клас, який відповідає таким умовностям (пізніше ви дізнаєтеся, як їх можна пропустити, якщо це необхідно):

  • Воно має жити у просторі імен DependencyInjection пакета;
  • Воно має реалізовувати ExtensionInterface, що зазвичай досягається шляхом розширення класу Extension ;
  • Імʼя дорівнює імені пакета з суфіксом Bundle, зміненим на Extension (наприклад клас розширення AcmeBundle буде називатися AcmeExtension, а для 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): void
    {
        // ... пізніше ви будете завантажувати тут файли
    }
}

Реєстрація класу розширення вручну

Якщо не слідувати угодам, вам потрібно буде вручну реєструвати ваше розширення. Щоб зробити це, вам потрібно перевизначити метод Bundle::getContainerExtension(), щоб він повертав екземпляр розширення:

1
2
3
4
5
6
7
8
9
10
11
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;

class AcmeHelloBundle extends Bundle
{
    public function getContainerExtension(): ?ExtensionInterface
    {
        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): void
{
    $loader = new XmlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../../config')
    );
    $loader->load('services.xml');
}

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

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

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

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

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

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

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

        // ...
    ]);
}

Note

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

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

Caution

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