Як створити користувацький завантажувач маршрутів

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

Як створити користувацький завантажувач маршрутів

Базові додатки можуть визначати всі свої маршрути в одному файлі конфігурації - зазвичай config/routes.yaml (див. ). Однак, у більшості додатків, розповсюджено імпортувати визначення маршршутів з різних джерел: PHP-атрирбутів у файлах контролерів, YAML, XML або PHP-файлів, збережених у якомусь каталозі та ін.

Вбудовані завантажувачі маршруту

Symfony надає декілька завантажувачів маршршуту для більшості розповсюджених потреб:

  • YAML
  • XML
  • PHP
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
# config/routes.yaml
app_file:
    # завантажує маршрути із заданого файлу маршрутизації, збережного в якомусь пакеті
    resource: '@AcmeBundle/Resources/config/routing.yaml'

app_psr4:
    # завантажує маршрути з PHP-атрибутів контролерів, знайдених у заданому корені простору імен PSR-4
    resource:
        path: '../src/Controller/'
        namespace: App\Controller
    type: attribute

app_attributes:
    # завантажує маршрути з PHP-атрибутів контролерів, знайдених у цьому каталозі
    resource: '../src/Controller/'
    type:     attribute

app_class_attributes:
    # завантажує маршрути з PHP-атрибутів заданого класу
    resource: App\Controller\MyController
    type:     attribute

app_directory:
    # завантажує маршрути з файлів YAML, XML або PHP, знайденних у цьому каталозі
    resource: '../legacy/routing/'
    type:     directory

app_bundle:
    # завантажує маршрути з файлів YAML, XML або PHP, знайденних у якомусь каталозі пакету
    resource: '@AcmeOtherBundle/Resources/config/routing/'
    type:     directory

6.1

Значення attribute другого аргументу import() було представлене в Symfony 6.1.

6.2

Функція імпорту маршрутів з кореня простору імен PSR-4 namespace була представлена в Symfony 6.2.

Note

При імпорті джерел, ключ (наприклад, app_file) є імʼям колекції. Просто переконайтеся, що він унікальний для кожного файлу, щоб жодні інші рядки його не перевизначали.

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

Що таке користувацький завантажувач маршрутів

Користувацький завантажувач маршрутів дозволяє вам генерувати маршрути, засновані на якихось угодах, патернах або інтеграціях. Приклад використання цього - бібліотека OpenAPI-Symfony-Routing, де маршрути генеруються, засновуючись на анотаціях OpenAPI/Swagger. Інший приклад - SonataAdminBundle, який створює маршрути, засновані на угодах CRUD.

Завантаження маршрутів

Маршрути у додатку Symfony завантажуються за допомогою DelegatingLoader. Цей завантажувач використовує декілька інших завантажувачів (делегатів) для завантаження ресурсів різних типів, наприклад, YAML-файлів або анотацій #[Route] у файлах контролера. Спеціалізовані завантажувачі впроваджують LoaderInterface, і таким чином мають два важливих методи: supports() і load().

Візьмемо ці рядки з routes.yaml:

  • YAML
  • XML
  • PHP
1
2
3
4
# config/routes.yaml
controllers:
    resource: ../src/Controller/
    type: attribute

Коли головний завантажувач парсує це, він звертається до всіх зареєстрованих завантажувачів-делегатів і викликає їх метод supports() із заданим ресурсом (../src/Controller/) і типом (attribute) в якості аргументів. Коли один з звантажувачів повератає true, буде викликано його метод load(), який повинен повернути RouteCollection, який містить обʼєкти Route.

Note

Маршрути, завантажені цим способом, будуть кешовані маршрутизатором так само, як і коли вони визначені в одному з форматів за замовчуванням (наприклад, XML, YAML, PHP файлах).

Завантаження маршрутів з користувацьким сервісом

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

Щоб зробити це, визначте type: service в якості типу завантажуваного джерела маршрутизації, і сконфігуруйте сервіс і метод, щоб вони викликали:

  • YAML
  • XML
  • PHP
1
2
3
4
# config/routes.yaml
admin_routes:
    resource: 'admin_route_loader::loadRoutes'
    type: service

У цьому прикладі, маршрути завантажуються, викликаючи метод loadRoutes() сервісу, ID якого - admin_route_loader. Ваш сервіс не повинен розширювати або реалізовувати ніякий особливий клас, але викликаний метод повинен повертати обʼєкт RouteCollection.

Якщо ви використовуєте автоконфігурацію , ваш клас має реалізовувати інтерфейс RouteLoaderInterface, щоб бути тегованим автоматично. Якщо ви не використовуєте автоконфігурацію, тегуйте його вручну з routing.route_loader.

Note

Маршрути, визначені з використанням сервісів завантажувачів маршрутів будуть автоматично кешовані фреймворком. Тому кожен раз, коли ваш сервіс має завантажити нові маршрути, не забудьте очистити кеш.

Tip

Якщо ваш сервіс викличний, вам не потрібно вказувати метод для використання.

Створення користувацького завантажувача

Для того, щоб завантажувати маршрути з користувацького джерела (тобто, з будь-чого, окрім анотацій та файлів YAML або XML), вам потрібно створити користувацький завантажувач маршрутів. Цей завантажувач повинен впроваджувати LoaderInterface.

У більшості випадків, його легше розширити з
Loader замість того, щоб вропваджувати LoaderInterface самостійно.

Приклад завантажувача нижче підтримує заванатаження ресурсів маршрутизації з типом extra. Імʼя типу не повинно перетинатися з іншими завантажувачами, які можуть підтримувати такий же тип ресурсу. Просто вигадайте імʼя, що конкретно відповідає тому, що ви робите. Імʼя ресурсу саме по собі насправді не використовується у цьому прикладі:

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
39
40
41
42
43
// src/Routing/ExtraLoader.php
namespace App\Routing;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class ExtraLoader extends Loader
{
    private $isLoaded = false;

    public function load($resource, string $type = null)
    {
        if (true === $this->isLoaded) {
            throw new \RuntimeException('Do not add the "extra" loader twice');
        }

        $routes = new RouteCollection();

        // підготувати новий маршрут
        $path = '/extra/{parameter}';
        $defaults = [
            '_controller' => 'App\Controller\ExtraController::extra',
        ];
        $requirements = [
            'parameter' => '\d+',
        ];
        $route = new Route($path, $defaults, $requirements);

        // додати новий маршрут до колекції маршрутів
        $routeName = 'extraRoute';
        $routes->add($routeName, $route);

        $this->isLoaded = true;

        return $routes;
    }

    public function supports($resource, string $type = null)
    {
        return 'extra' === $type;
    }
}

Переконайтеся в тому, що контролер, який ви вказуєте, дійсно існує. У цьому впадку, вам потрібно створити метод extraAction() в ExtraController:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/Controller/ExtraController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ExtraController extends AbstractController
{
    public function extra($parameter)
    {
        return new Response($parameter);
    }
}

Тепер, визначіть сервіс для ExtraLoader:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
# config/services.yaml
services:
    # ...

    App\Routing\ExtraLoader:
        tags: [routing.loader]

Відьмітьте тег routing.loader. Всі сервіси з цим тегом будуть відмічені, як потеційні завантажувачі маршрутів, і додані в якості спеціалізованих завантажувачів маршрутів у сервіс routing.loader, який є екземпляром DelegatingLoader.

Використання користувацького завантажувача

Якщо ви більше нічого не робили, то ваш користувацький завантажувач маршрутів не буде викликаний. Що вам залишилося зробити, так це додати декілька рядків у конфігурацію маршрутизації:

  • YAML
  • XML
  • PHP
1
2
3
4
# config/routes.yaml
app_extra:
    resource: .
    type: extra

Важливою частиною тут є ключ type. Його значення повинно бути extra, так як це той тип, який підтримує ExtraLoader, і таким чином, його метод load() точно буде викликаний. Ключ resource не важливий для ExtraLoader, тому він встановлений у значенні . (одна крапка).

Note

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

Більш просунуті завантажувачі

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

Авжеж, вам все ще потрібно впровадити supports() і load(). Коли вам захочеться завантажити інший ресурс - наприклад, YAML-файл конфігурації маршрутизації - ви можете викликати метод import():

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
// src/Routing/AdvancedLoader.php
namespace App\Routing;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;

class AdvancedLoader extends Loader
{
    public function load($resource, string $type = null)
    {
        $routes = new RouteCollection();

        $resource = '@ThirdPartyBundle/Resources/config/routes.yaml';
        $type = 'yaml';

        $importedRoutes = $this->import($resource, $type);

        $routes->addCollection($importedRoutes);

        return $routes;
    }

    public function supports($resource, string $type = null)
    {
        return 'advanced_extra' === $type;
    }
}

Note

Імʼя ресурсу і тип імпортованої конфігурації маршрутизації можуть бути будь-якими, якщо вони будуть підтримуватися завантажувачем конфігурації маршрутизації (YAML, XML, PHP, анотація і т.д.).

Note

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