<? phpukraine ДОКУМЕНТАЦІЯ
Пошук по платформі
Документація українською

Переклад силами спільноти. Кожен розділ показує стан готовності — недоперекладене відкрито помічене, а не приховане.

ВЕРСІЯ
АКТУАЛЬНА Актуальний реліз. Переклад наздоганяє оригінал — розділи з низьким відсотком позначені у змісті.
ОСНОВИ · ЧАСТКОВО частковий переклад, оновлено 4 вересня 2026

Маршрутизація — Symfony

ПЕРЕКЛАД НЕПОВНИЙ

Частину підрозділів ще не перекладено — вони показані англійською нижче в тексті або лишились в оригіналі. Готові фрагменти вже перевірені редактором.

Коли ваш застосунок отримує запит, він викликає дію контролера, щоб згенерувати відповідь. Конфігурація маршрутизації визначає, яку дію запустити для кожного вхідного URL. Вона також надає інші корисні можливості, як-от генерацію SEO-дружніх URL (наприклад, /read/intro-to-symfony замість index.php?article_id=57).

Створення маршрутів

Маршрути можна налаштувати в YAML, PHP або за допомогою атрибутів. Усі формати надають однакові можливості та продуктивність, тож обирайте той, що вам до вподоби. Symfony рекомендує атрибути, бо зручно тримати маршрут і контролер в одному місці.

Створення маршрутів через атрибути

PHP-атрибути дозволяють визначати маршрути поруч із кодом контролерів, повʼязаних із цими маршрутами. Атрибути ввімкнені за замовчуванням у застосунках Symfony, які використовують Symfony Flex, тож ви можете починати користуватися ними одразу.

Примітка. Якщо ваш проєкт не використовує Symfony Flex, ви маєте увімкнути маршрутизацію через атрибути вручну, створивши такий конфігураційний файл:

# config/routes.yaml
controllers:
    resource: routing.controllers

Це вказує Symfony шукати атрибути #[Route] по всьому застосунку й реєструвати як маршрути, так і повʼязані з ними контролери.

Припустімо, ви хочете визначити маршрут для URL /blog у своєму застосунку. Для цього створіть клас контролера на кшталт такого:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog', name: 'blog_list')]
    public function list(): Response
    {
        // ...
    }
}

Ця конфігурація визначає маршрут із назвою blog_list, який спрацьовує, коли користувач запитує URL /blog. Коли збіг стається, застосунок виконує метод list() класу BlogController.

Примітка. Рядок запиту (query string) URL не враховується під час зіставлення маршрутів. У цьому прикладі URL на кшталт /blog?foo=bar та /blog?foo=bar&bar=foo теж збігатимуться з маршрутом blog_list.

Увага! Якщо ви визначаєте кілька PHP-класів в одному файлі, Symfony завантажує лише маршрути першого класу, ігноруючи всі інші маршрути. Атрибут маршруту завжди має перевагу над маршрутами, визначеними у YAML- або PHP-файлах, і Symfony завжди завантажить атрибут маршруту.

Назва маршруту (blog_list) поки що не важлива, але вона стане суттєвою пізніше, коли ви генеруватимете URL. Вам треба лише памʼятати, що кожна назва маршруту має бути унікальною в застосунку.

Створення маршрутів у YAML- або PHP-файлах

Замість визначення маршрутів у класах контролерів ви можете визначити їх в окремому YAML- або PHP-файлі. Головна перевага в тому, що вони не потребують жодної додаткової залежності. Головний недолік — доводиться працювати з кількома файлами, коли перевіряєш маршрутизацію якоїсь дії контролера.

Наступний приклад показує, як визначити у YAML або PHP маршрут із назвою blog_list, що повʼязує URL /blog із дією list() контролера BlogController:

# config/routes.yaml
blog_list:
    path: /blog
    # значення controller має формат 'controller_class::method_name'
    controller: App\Controller\BlogController::list

    # якщо дію реалізовано як метод __invoke() класу контролера,
    # частину '::method_name' можна пропустити:
    # controller: App\Controller\BlogController
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_list' => [
        'path' => '/blog',
        // значення controller має формат [controller_class, method_name]
        'controller' => [BlogController::class, 'list'],

        // якщо дію реалізовано як метод __invoke() класу контролера,
        // частину 'method_name' можна пропустити:
        // 'controller' => BlogController::class,
    ],
]);

Зіставлення HTTP-методів

За замовчуванням маршрути збігаються з будь-яким HTTP-дієсловом (GET, POST, PUT тощо). Використовуйте опцію methods, щоб обмежити дієслова, на які має відповідати кожен маршрут:

// src/Controller/BlogApiController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogApiController extends AbstractController
{
    #[Route('/api/posts/{id}', methods: ['GET', 'HEAD'])]
    public function show(int $id): Response
    {
        // ... повертає JSON-відповідь із постом
    }

    #[Route('/api/posts/{id}', methods: ['PUT'])]
    public function edit(int $id): Response
    {
        // ... редагує пост
    }
}
# config/routes.yaml
api_post_show:
    path:       /api/posts/{id}
    controller: App\Controller\BlogApiController::show
    methods:    GET|HEAD

api_post_edit:
    path:       /api/posts/{id}
    controller: App\Controller\BlogApiController::edit
    methods:    PUT
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogApiController;

return Routes::config([
    'api_post_show' => [
        'path' => '/api/posts/{id}',
        'controller' => [BlogApiController::class, 'show'],
        'methods' => ['GET', 'HEAD'],
    ],
    'api_post_edit' => [
        'path' => '/api/posts/{id}',
        'controller' => [BlogApiController::class, 'edit'],
        'methods' => ['PUT'],
    ],
]);

Порада. HTML-форми підтримують лише методи GET і POST. Якщо ви викликаєте маршрут з іншим методом із HTML-форми, додайте приховане поле з назвою _method і потрібним методом (наприклад, <input type="hidden" name="_method" value="PUT">). Якщо ви створюєте форми за допомогою Symfony Forms, це робиться автоматично, коли опція framework.http_method_override має значення true.

З міркувань безпеки ви можете обмежити, які HTTP-методи можна перевизначати, за допомогою опції framework.allowed_http_method_override.

Зіставлення оточень

Використовуйте опцію env, щоб зареєструвати маршрут лише тоді, коли поточне оточення конфігурації (configuration environment) відповідає заданому значенню:

// src/Controller/DefaultController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class DefaultController extends AbstractController
{
    #[Route('/tools', name: 'tools', env: 'dev')]
    public function developerTools(): Response
    {
        // ...
    }

    // Ви також можете передати масив оточень
    #[Route('/tools', name: 'tools', env: ['dev', 'test'])]
    public function developerTools(): Response
    {
        // ...
    }
}
# config/routes.yaml
when@dev:
    tools:
        path: /tools
        controller: App\Controller\DefaultController::developerTools
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\DefaultController;

return Routes::config([
    'when@dev' => [
        'tools' => [
            'path' => '/tools',
            'controller' => [DefaultController::class, 'developerTools'],
        ],
    ],
]);

Зіставлення за виразами

Використовуйте опцію condition, якщо вам потрібно, щоб якийсь маршрут збігався на основі довільної логіки зіставлення:

// src/Controller/DefaultController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class DefaultController extends AbstractController
{
    #[Route(
        '/contact',
        name: 'contact',
        condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'",
        // вирази також можуть містити параметри конфігурації:
        // condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
    )]
    public function contact(): Response
    {
        // ...
    }

    #[Route(
        '/posts/{id}',
        name: 'post_show',
        // вирази можуть отримувати значення параметрів маршруту через змінну "params"
        condition: "params['id'] < 1000"
    )]
    public function showPost(int $id): Response
    {
        // ... повертає JSON-відповідь із постом
    }
}
# config/routes.yaml
contact:
    path:       /contact
    controller: App\Controller\DefaultController::contact
    condition:  "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'"
    # вирази також можуть містити параметри конфігурації:
    # condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
    # вирази можуть навіть використовувати змінні оточення:
    # condition: "context.getHost() == env('APP_MAIN_HOST')"

post_show:
    path:       /posts/{id}
    controller: App\Controller\DefaultController::showPost
    # вирази можуть отримувати значення параметрів маршруту через змінну "params"
    condition:  "params['id'] < 1000"
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\DefaultController;

return Routes::config([
    'contact' => [
        'path' => '/contact',
        'controller' => [DefaultController::class, 'contact'],
        'condition' => 'context.getMethod() in ["GET", "HEAD"] and request.headers.get("User-Agent") matches "/firefox/i"',
        // вирази також можуть містити параметри конфігурації:
        // 'condition' => 'request.headers.get("User-Agent") matches "%app.allowed_browsers%"',
        // вирази можуть навіть використовувати змінні оточення:
        // 'condition' => 'context.getHost() == env("APP_MAIN_HOST")',
    ],
    'post_show' => [
        'path' => '/posts/{id}',
        'controller' => [DefaultController::class, 'showPost'],
        // вирази можуть отримувати значення параметрів маршруту через змінну "params"
        'condition' => 'params["id"] < 1000',
    ],
]);

Значення опції condition — це вираз, що використовує будь-який коректний синтаксис мови виразів (expression language), і може використовувати будь-яку з цих змінних, створених Symfony:

context : Екземпляр Symfony\Component\Routing\RequestContext, який містить найбазовішу інформацію про маршрут, що зіставляється.

request : Обʼєкт Symfony Request, який представляє поточний запит.

params : Масив зіставлених параметрів маршруту для поточного маршруту.

Ви також можете використовувати такі функції:

env(string $name) : Повертає значення змінної за допомогою обробників змінних оточення

service(string $alias) : Повертає сервіс умови маршрутизації (routing condition service).

Спершу додайте атрибут #[AsRoutingConditionService] або тег routing.condition_service до сервісів, які ви хочете використовувати в умовах маршрутів:

use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
use Symfony\Component\HttpFoundation\Request;

#[AsRoutingConditionService(alias: 'route_checker')]
class RouteChecker
{
    public function check(Request $request): bool
    {
        // ...
    }
}

Далі використовуйте функцію service(), щоб послатися на цей сервіс усередині умов:

// Контролер (з використанням аліаса):
#[Route(condition: "service('route_checker').check(request)")]
// Або без аліаса:
#[Route(condition: "service('App\\\Service\\\RouteChecker').check(request)")]

Усередині вирази компілюються у чистий PHP. Через це використання ключа condition не створює жодних додаткових накладних витрат, окрім часу, потрібного на виконання відповідного PHP.

Увага! Умови не враховуються під час генерації URL (це пояснюється далі в цій статті).

Налагодження маршрутів

Коли ваш застосунок росте, у вас зрештою буде багато маршрутів. Symfony містить кілька команд, які допомагають налагоджувати проблеми з маршрутизацією. Спершу команда debug:router виводить усі маршрути вашого застосунку в тому самому порядку, в якому Symfony їх обчислює:

$ php bin/console debug:router

----------------  -------  --------------------------------------------
Name              Method   Path
----------------  -------  --------------------------------------------
homepage          ANY      /
contact           GET      /contact
contact_process   POST     /contact
article_show      ANY      /articles/{_locale}/{year}/{title}.{_format}
blog              ANY      /blog/{page}
blog_show         ANY      /blog/{slug}
----------------  -------  --------------------------------------------

# передайте цю опцію, щоб також показати всі визначені аліаси маршрутів
$ php bin/console debug:router --show-aliases

# передайте цю опцію, щоб також показати контролери, повʼязані з маршрутами
$ php bin/console debug:router --show-controllers

# передайте цю опцію, щоб показати лише маршрути, які відповідають заданому HTTP-методу
# (можна використати спеціальне значення ANY, щоб побачити маршрути, які відповідають будь-якому методу)
$ php bin/console debug:router --method=GET
$ php bin/console debug:router --method=ANY

# передайте цю опцію, щоб відсортувати список маршрутів за заданим стовпцем
$ php bin/console debug:router --sort=path
$ php bin/console debug:router --sort=name

Нове у версії 8.1. Опцію --sort для debug:router було введено в Symfony 8.1.

Передайте цьому аргументу назву (або частину назви) якогось маршруту, щоб вивести подробиці маршруту:

$ php bin/console debug:router app_lucky_number

+-------------+---------------------------------------------------------+
| Property    | Value                                                   |
+-------------+---------------------------------------------------------+
| Route Name  | app_lucky_number                                        |
| Path        | /lucky/number/{max}                                     |
| ...         | ...                                                     |
| Options     | compiler_class: Symfony\Component\Routing\RouteCompiler |
|             | utf8: true                                              |
+-------------+---------------------------------------------------------+

Інша команда називається router:match, і вона показує, який маршрут збігатиметься із заданим URL. Вона корисна, щоб зʼясувати, чому якийсь URL не виконує ту дію контролера, на яку ви очікуєте:

$ php bin/console router:match /lucky/number/8

  [OK] Route "app_lucky_number" matches

Параметри маршруту

У попередніх прикладах визначалися маршрути, де URL ніколи не змінюється (наприклад, /blog). Однак часто визначають маршрути, де деякі частини є змінними. Наприклад, URL для показу якогось допису в блозі, ймовірно, включатиме заголовок або слаг (наприклад, /blog/my-first-post чи /blog/all-about-symfony).

У маршрутах Symfony змінні частини загортаються у { }. Наприклад, маршрут для показу вмісту допису в блозі визначається як /blog/{slug}:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    // ...

    #[Route('/blog/{slug}', name: 'blog_show')]
    public function show(string $slug): Response
    {
        // $slug дорівнюватиме динамічній частині URL
        // напр., за адресою /blog/yay-routing буде $slug='yay-routing'

        // ...
    }
}
# config/routes.yaml
blog_show:
    path:       /blog/{slug}
    controller: App\Controller\BlogController::show
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_show' => [
        'path' => '/blog/{slug}',
        'controller' => [BlogController::class, 'show'],
    ],
]);

Назва змінної частини ({slug} у цьому прикладі) використовується для створення PHP-змінної, у якій зберігається вміст цієї частини маршруту й передається до контролера. Якщо користувач відвідує URL /blog/my-first-post, Symfony виконує метод show() класу BlogController і передає методу show() аргумент $slug = 'my-first-post'.

Маршрути можуть визначати будь-яку кількість параметрів, але кожен із них можна використати лише один раз у кожному маршруті (наприклад, /blog/posts-about-{category}/page/{pageNumber}).

Валідація параметрів

Уявіть, що у вашому застосунку є маршрут blog_show (URL: /blog/{slug}) і маршрут blog_list (URL: /blog/{page}). Оскільки параметри маршруту приймають будь-яке значення, немає способу відрізнити ці два маршрути.

Якщо користувач запитує /blog/my-first-post, збігатимуться обидва маршрути, і Symfony використає той, який було визначено першим. Щоб це виправити, додайте якусь валідацію до параметра {page} за допомогою опції requirements:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog/{page}', name: 'blog_list', requirements: ['page' => '[0-9]+'])]
    public function list(int $page): Response
    {
        // ...
    }

    #[Route('/blog/{slug}', name: 'blog_show')]
    public function show(string $slug): Response
    {
        // ...
    }
}
# config/routes.yaml
blog_list:
    path:       /blog/{page}
    controller: App\Controller\BlogController::list
    requirements:
        page: '[0-9]+'

blog_show:
    path:       /blog/{slug}
    controller: App\Controller\BlogController::show
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_list' => [
        'path' => '/blog/{page}',
        'controller' => [BlogController::class, 'list'],
        'requirements' => ['page' => '[0-9]+'],
    ],
    'blog_show' => [
        'path' => '/blog/{slug}',
        'controller' => [BlogController::class, 'show'],
    ],
]);

Опція requirements визначає регулярні вирази PHP, яким мають відповідати параметри маршруту, щоб увесь маршрут збігся. У цьому прикладі [0-9]+ — це регулярний вираз, який відповідає цифрам будь-якої довжини. Тепер:

URL Маршрут Параметри
/blog/2 blog_list $page = 2
/blog/my-first-post blog_show $slug = my-first-post

Порада. Перелік (enum) Symfony\Component\Routing\Requirement\Requirement містить набір поширених констант із регулярними виразами, як-от цифри, дати та UUID, які можна використовувати як вимоги до параметрів маршруту.

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Requirement\Requirement;

class BlogController extends AbstractController
{
    #[Route('/blog/{page}', name: 'blog_list', requirements: ['page' => Requirement::DIGITS])]
    public function list(int $page): Response
    {
        // ...
    }
}
# config/routes.yaml
blog_list:
    path:       /blog/{page}
    controller: App\Controller\BlogController::list
    requirements:
        page: !php/const Symfony\Component\Routing\Requirement\Requirement::DIGITS
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;
use Symfony\Component\Routing\Requirement\Requirement;

return Routes::config([
    'blog_list' => [
        'path' => '/blog/{page}',
        'controller' => [BlogController::class, 'list'],
        'requirements' => ['page' => Requirement::DIGITS],
    ],
]);

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

Порада. Параметри також підтримують властивості Unicode PCRE (PCRE Unicode properties) — escape-послідовності, що відповідають узагальненим типам символів. Наприклад, \p{Lu} відповідає будь-якому символу верхнього регістру будь-якою мовою, \p{Greek} відповідає будь-яким грецьким символам тощо.

Якщо вам так більше до вподоби, вимоги можна вписувати всередині кожного параметра за синтаксисом {parameter_name<requirements>}. Ця можливість робить конфігурацію лаконічнішою, але може погіршити читабельність маршруту, коли вимоги складні:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog/{page<[0-9]+>}', name: 'blog_list')]
    public function list(int $page): Response
    {
        // ...
    }
}
# config/routes.yaml
blog_list:
    path:       /blog/{page<[0-9]+>}
    controller: App\Controller\BlogController::list
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_list' => [
        'path' => '/blog/{page<[0-9]+>}',
        'controller' => [BlogController::class, 'list'],
    ],
]);

Необовʼязкові параметри

У попередньому прикладі URL маршруту blog_list — це /blog/{page}. Якщо користувачі відвідують /blog/1, буде збіг. Але якщо вони відвідують /blog, збігу не буде. Щойно ви додаєте параметр до маршруту, він мусить мати значення.

Ви можете зробити так, щоб blog_list знову збігався, коли користувач відвідує /blog, додавши значення за замовчуванням для параметра {page}. Коли використовуються атрибути, значення за замовчуванням визначаються в аргументах дії контролера. В інших форматах конфігурації вони визначаються опцією defaults:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog/{page}', name: 'blog_list', requirements: ['page' => '[0-9]+'])]
    public function list(int $page = 1): Response
    {
        // ...
    }
}
# config/routes.yaml
blog_list:
    path:       /blog/{page}
    controller: App\Controller\BlogController::list
    defaults:
        page: 1
    requirements:
        page: '[0-9]+'

blog_show:
    # ...
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_list' => [
        'path' => '/blog/{page}',
        'controller' => [BlogController::class, 'list'],
        'defaults' => ['page' => 1],
        'requirements' => ['page' => '[0-9]+'],
    ],
    'blog_show' => [
        // ...
    ],
]);

Тепер, коли користувач відвідує /blog, збігатиметься маршрут blog_list, а $page за замовчуванням матиме значення 1.

Порада. Значенню за замовчуванням дозволено не відповідати вимозі.

Увага! Ви можете мати більше одного необовʼязкового параметра (наприклад, /blog/{slug}/{page}), але все, що йде після необовʼязкового параметра, теж має бути необовʼязковим. Наприклад, /{page}/blog — коректний шлях, але page завжди буде обовʼязковим (тобто /blog не збігатиметься з цим маршрутом).

Якщо ви хочете завжди включати якесь значення за замовчуванням у згенерований URL (наприклад, щоб примусово генерувати /blog/1 замість /blog у попередньому прикладі), додайте символ ! перед назвою параметра: /blog/{!page}

Як і у випадку з вимогами, значення за замовчуванням теж можна вписувати всередині кожного параметра за синтаксисом {parameter_name?default_value}. Ця можливість сумісна з вписаними вимогами, тож ви можете вписати обидва в один параметр:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog/{page<[0-9]+>?1}', name: 'blog_list')]
    public function list(int $page): Response
    {
        // ...
    }
}
# config/routes.yaml
blog_list:
    path:       /blog/{page<[0-9]+>?1}
    controller: App\Controller\BlogController::list
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_list' => [
        'path' => '/blog/{page<[0-9]+>?1}',
        'controller' => [BlogController::class, 'list'],
    ],
]);

Порада. Щоб надати параметру значення за замовчуванням null, не додавайте нічого після символу ? (наприклад, /blog/{page?}). Якщо ви так робите, не забудьте оновити типи відповідних аргументів контролера, щоб дозволити передавання значень null (наприклад, замініть int $page на ?int $page).

Параметр priority

Symfony обчислює маршрути в порядку їх визначення. Якщо шлях маршруту відповідає багатьом різним патернам, це може завадити зіставленню інших маршрутів. У YAML- або PHP-файлах конфігурації ви можете переміщувати визначення маршрутів вище або нижче у файлі конфігурації, щоб керувати їхнім пріоритетом. У маршрутах, визначених як PHP-атрибути, це зробити значно важче, тож ви можете задати необовʼязковий параметр priority у таких маршрутах, щоб керувати їхнім пріоритетом:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    /**
     * Цей маршрут має «жадібний» патерн і визначений першим.
     */
    #[Route('/blog/{slug}', name: 'blog_show')]
    public function show(string $slug): Response
    {
        // ...
    }

    /**
     * Цей маршрут не міг би збігтися без визначення пріоритету, вищого за 0.
     */
    #[Route('/blog/list', name: 'blog_list', priority: 2)]
    public function list(): Response
    {
        // ...
    }
}

Параметр priority очікує ціле значення. Маршрути з вищим пріоритетом сортуються раніше за маршрути з нижчим пріоритетом. Значення за замовчуванням, коли його не визначено, — 0.

Перетворення параметрів

Поширена потреба в маршрутизації — перетворити значення, збережене в якомусь параметрі (наприклад, ціле число, що є ID користувача), на інше значення (наприклад, обʼєкт, що представляє користувача). Ця можливість називається «param converter».

Тепер збережіть попередню конфігурацію маршруту, але змініть аргументи дії контролера. Замість string $slug додайте BlogPost $post:

// src/Controller/BlogController.php
namespace App\Controller;

use App\Entity\BlogPost;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    // ...

    #[Route('/blog/{slug:post}', name: 'blog_show')]
    public function show(BlogPost $post): Response
    {
        // $post — це обʼєкт, слаг якого збігається з параметром маршрутизації

        // ...
    }
}

Якщо аргументи вашого контролера містять type-hint для обʼєктів (BlogPost у цьому випадку), «param converter» робить запит до бази даних, щоб знайти обʼєкт за параметрами запиту (slug у цьому випадку). Якщо обʼєкт не знайдено, Symfony автоматично генерує відповідь 404.

Синтаксис {slug:post} зіставляє параметр маршруту з назвою slug з аргументом контролера з назвою $post. Він також підказує «param converter» знайти відповідний обʼєкт BlogPost у базі даних за слагом.

Коли ви зіставляєте кілька сутностей із параметрів маршруту, можуть виникати конфлікти назв. У цьому прикладі маршрут намагається визначити два зіставлення: одне для автора й одне для категорії; обидва використовують один і той самий параметр name. Це не дозволено, бо маршрут у підсумку оголошує name двічі:

#[Route('/search-book/{name:author}/{name:category}')]

Такі маршрути слід натомість визначати за допомогою такого синтаксису:

#[Route('/search-book/{authorName:author.name}/{categoryName:category.name}')]

Таким чином назви параметрів маршруту унікальні (authorName і categoryName), і «param converter» може коректно зіставити їх з аргументами контролера ($author і $category), завантаживши обидва за їхніми іменами.

Складніші зіставлення можна реалізувати за допомогою атрибута #[MapEntity]. Перегляньте документацію з перетворення параметрів у Doctrine, щоб дізнатися, як налаштувати запити до бази даних, які використовуються для отримання обʼєкта за параметром маршруту.

Параметри-переліки (backed enum)

Ви можете використовувати PHP backed enumerations як параметри маршруту, бо Symfony автоматично перетворить їх на їхні скалярні значення.

// src/Controller/OrderController.php
namespace App\Controller;

use App\Enum\OrderStatusEnum;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class OrderController extends AbstractController
{
    #[Route('/orders/list/{status}', name: 'list_orders_by_status')]
    public function list(OrderStatusEnum $status = OrderStatusEnum::Paid): Response
    {
        // ...
    }
}

Спеціальні параметри

Окрім ваших власних параметрів, маршрути можуть містити будь-який із наведених нижче спеціальних параметрів, створених Symfony:

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

_format : Зіставлене значення використовується, щоб задати «формат запиту» обʼєкта Request. Це використовується для таких речей, як встановлення Content-Type відповіді (наприклад, формат json перетворюється на Content-Type application/json).

_fragment : Використовується, щоб задати ідентифікатор фрагмента URL (також званий «hash» або «anchor») — необовʼязкову останню частину URL, що починається з символу # і використовується для ідентифікації частини документа.

_locale : Використовується, щоб задати локаль для запиту.

_query : Масив параметрів рядка запиту, які додаються до згенерованого URL.

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

// src/Controller/ArticleController.php
namespace App\Controller;

// ...
class ArticleController extends AbstractController
{
    #[Route(
        path: '/articles/{_locale}/search.{_format}',
        locale: 'en',
        format: 'html',
        query: ['page' => 1],
        requirements: [
            '_locale' => 'en|fr',
            '_format' => 'html|xml',
        ],
    )]
    public function search(): Response
    {
    }
}
# config/routes.yaml
article_search:
  path:        /articles/{_locale}/search.{_format}
  controller:  App\Controller\ArticleController::search
  locale:      en
  format:      html
  query:
      page:    1
  requirements:
      _locale: en|fr
      _format: html|xml
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\ArticleController;

return Routes::config([
    'article_show' => [
        'path' => '/articles/{_locale}/search.{_format}',
        'controller' => [ArticleController::class, 'search'],
        'locale' => 'en',
        'format' => 'html',
        'query' => ['page' => 1],
        'requirements' => ['_locale' => 'en|fr', '_format' => 'html|xml'],
    ],
]);

Додаткові параметри

В опції defaults маршруту ви можете за бажанням визначити параметри, не включені до конфігурації маршруту. Це корисно, щоб передавати додаткові аргументи контролерам маршрутів:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog/{page}', name: 'blog_index', defaults: ['page' => 1, 'title' => 'Hello world!'])]
    public function index(int $page, string $title): Response
    {
        // ...
    }
}
# config/routes.yaml
blog_index:
    path:       /blog/{page}
    controller: App\Controller\BlogController::index
    defaults:
        page: 1
        title: "Hello world!"
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\BlogController;

return Routes::config([
    'blog_index' => [
        'path' => '/blog/{page}',
        'controller' => [BlogController::class, 'index'],
        'defaults' => ['page' => 1, 'title' => 'Hello world!'],
    ],
]);

Символи слеша в параметрах маршруту

Параметри маршруту можуть містити будь-які значення, крім символу слеша /, бо саме цей символ використовується для розділення різних частин URL. Наприклад, якщо значення token у маршруті /share/{token} містить символ /, цей маршрут не збігатиметься.

Можливе розвʼязання — зробити вимоги до параметра дозвільнішими:

// src/Controller/DefaultController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class DefaultController extends AbstractController
{
    #[Route('/share/{token}', name: 'share', requirements: ['token' => '.+'])]
    public function share($token): Response
    {
        // ...
    }
}
# config/routes.yaml
share:
    path:       /share/{token}
    controller: App\Controller\DefaultController::share
    requirements:
        token: .+
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

use App\Controller\DefaultController;

return Routes::config([
    'share' => [
        'path' => '/share/{token}',
        'controller' => [DefaultController::class, 'share'],
        'requirements' => ['token' => '.+'],
    ],
]);

Примітка. Якщо маршрут визначає кілька параметрів і ви застосовуєте цей дозвільний регулярний вираз до всіх них, ви можете отримати неочікувані результати. Наприклад, якщо визначення маршруту — /share/{path}/{token} і обидва, path і token, приймають /, тоді token отримає лише останню частину, а решту зіставить path.

Примітка. Якщо маршрут містить спеціальний параметр {_format}, вам не слід використовувати вимогу .+ для параметрів, які дозволяють слеші. Наприклад, якщо патерн — /share/{token}.{_format} і {token} дозволяє будь-який символ, URL /share/foo/bar.json вважатиме foo/bar.json токеном, а формат буде порожнім. Це можна розвʼязати, замінивши вимогу .+ на [^.]+, щоб дозволити будь-який символ, крім крапок.

Аліаси маршрутів

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

// src/Controller/ProductController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ProductController
{
    #[Route('/product/{id}', name: 'product_show')]
    public function show(): Response
    {
        // ...
    }
}
# config/routes.yaml
product_show:
    path: /product/{id}
    controller: App\Controller\ProductController::show
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

return Routes::config([
    'product_show' => [
        'path' => '/product/{id}',
        'controller' => [ProductController::class, 'show'],
    ],
]);

Тепер скажімо, ви хочете створити новий маршрут із назвою product_details, який працює точно так само, як product_show.

Замість дублювання оригінального маршруту ви можете створити для нього аліас.

// src/Controller/ProductController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ProductController
{
    // аргумент "alias" призначає цьому маршруту альтернативну назву;
    // аліас вказуватиме на фактичний маршрут "product_show"
    #[Route('/product/{id}', name: 'product_show', alias: ['product_details'])]
    public function show(): Response
    {
        // ...
    }
}
# config/routes.yaml
product_show:
    path: /product/{id}
    controller: App\Controller\ProductController::show

product_details:
    # опція "alias" посилається на назву маршруту, оголошеного вище
    alias: product_show
// config/routes.php
namespace Symfony\Component\Routing\Loader\Configurator;

Ще не перекладено такі підрозділи оригіналу: Deprecating Route Aliases, Debugging Route Aliases, Route Groups and Prefixes, Getting the Route Name and Parameters, Special Routes, Redirecting URLs with Trailing Slashes, Sub-Domain Routing, Localized Routes (i18n), Stateless Routes, Generating URLs, Troubleshooting.

ЯК ЦЯ СТОРІНКА ВИГЛЯДАЄ В ПОШУКУ
phpukraine.com/docs/symfony/routing
Маршрутизація — Symfony документація українською
Маршрутизація у Symfony 8.1: переклад офіційної документації українською. Оновлено 4 вересня 2026. Приклади коду, пояснення та посилання на питання зі співбесід.
Переклад робить спільнота

Виправити терміни, дописати розділ або взяти нову главу може кожен. Термінологію узгоджуємо в глосарії, щоб переклад лишався однорідним.

1
Перекладачів
79%
Готовності
0
Вільних розділів
Глосарій термінів