Маршрутизація (частина 3)

02/06/2015 0 symfony, маршрутизація, маршрут, створення URL

Параметри маршруту та аргументи контролера

Параметри маршруту (наприклад, {slug}) особливо важливі через те, що кожен з них доступний в якості аргумента до методу контролера:

public function showAction($slug)
{
    // ...
}

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

Іншими словами, для кожного аргумента вашого методу контролера Symfony шукає параметр маршруту цього імені та присвоює його значення цьому аргументу. У прикладі, що розглядався в підрозділі “Маршрутизація вищого рівня у прикладах”, будь-яка комбінація та будь-який порядок змінних, перелічених нижче, може використовуватися як аргумент до методу showAction():

  • $_locale

  • $year

  • $title

  • $_format

  • $_controller

  • $_route

Оскільки заповнювачі та набір defaults зливаються у єдине ціле, доступна навіть така змінна, як $_controller. Нагадуємо, що більш детально ви можете про це прочитати в розділі "Контролер", підрозділі "Параметри маршруту в якості аргументів Контролера". 

! Спеціальна змінна $_route встановлюється для імені того маршруту, який було встановлено у відповідність.

До визначення вашого маршруту ви в праві включати й певну додаткову інформацію та мати до неї доступ через контролер. Більше про це читайте за посиланням на оригінальну статтю.

Підключаємо зовнішні ресурси маршрутизації

Усі маршрути завантажуються через один уніфікований файл з конфігураціями. Як правило, це app/config/routing.yml (див. підрозділ “Створення маршрутів” тут). Якщо ж ви, однак, використовуєте анотації для маршрутизації (routing annotations), не забудьте вказати маршрутизатор до контролерів з потрібними анотаціями. Зробити це можна через "імпортування" директорій до налаштувань маршрутизації:

YAML

# app/config/routing.yml
app:
    resource: "@AppBundle/Controller/"
    type:     annotation # required to enable the Annotation reader for this resource

XML

<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">

    <!-- the type is required to enable the annotation reader for this resource -->
    <import resource="@AppBundle/Controller/" type="annotation"/>
</routes>

PHP

// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();
$collection->addCollection(
    // second argument is the type, which is required to enable
    // the annotation reader for this resource
    $loader->import("@AppBundle/Controller/", "annotation")
);

return $collection;

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

Ключ resource завантажує вказаний ресурс маршрутизації. В даному прикладі ресурс є директорією, в той час як синтаксис скорочень @AppBundle зазначає повний шлях до пакета AppBundle. Коли ви посилаєтесь на ту чи іншу директорію, усі файли цієї директорії розбиваються на складові та поміщаються до маршрутизації.

! Ви також можете включати й інші файли конфігурації маршрутизації, проте ми рекомендуємо робити так лиш для імпортування маршрутизації пакетів другорядного значення.

YAML

# app/config/routing.yml
app:
    resource: "@AcmeOtherBundle/Resources/config/routing.yml"

XML

<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">

    <import resource="@AcmeOtherBundle/Resources/config/routing.xml" />
</routes>

PHP

// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();
$collection->addCollection(
    $loader->import("@AcmeOtherBundle/Resources/config/routing.php")
);

return $collection;

Приписування префіксів для імпортованих маршрутів

У Symfony вам надається змога приписувати так звані “префікси” для імпортованих маршрутів. Для наочності розглянемо такий приклад: скажімо, ви хотіли б приписати префікс /site до усіх маршрутів пакету AppBundle (тобто, /site/blog/{slug} замість /blog/{slug}):

YAML

# app/config/routing.yml
app:
    resource: "@AppBundle/Controller/"
    type:     annotation
    prefix:   /site

XML

<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">

    <import
        resource="@AppBundle/Controller/"
        type="annotation"
        prefix="/site" />
</routes>

PHP

// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;

$app = $loader->import('@AppBundle/Controller/', 'annotation');
$app->addPrefix('/site');

$collection = new RouteCollection();
$collection->addCollection($app);

return $collection;

Віднині шлях для кожного з маршрутів, що завантажуються з оновленого ресурсу маршрутизації, матиме префікс /site.

Додавання вимоги host для імпортованих маршрутів

Для імпортованих маршрутів ви маєте змогу проставляти регулярний вираз host. Більше інформації на цю тему ви знайдете за посиланням на оригінальну статтю.

Візуалізація й налагодження маршрутів

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

$ php app/console debug:router

! До версії Symfony 2.6 дана команда називалась router:debug.

Результат виконання даної команди - виведення на екран корисного і зручного списку усіх налаштованих маршрутів вашого додатку:

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 app/console debug:router article_show

За аналогією, якщо ви хочете перевірити, чи URL відповідає даному маршруту, просто використайте консольну команду router:match:

$ php app/console router:match /blog/my-latest-post

Ця команда виведе той маршрут, якому стає у відповідність ваш URL.

Route "blog_show" matches

Створення URL-ів

Система маршрутизації повинна використовуватися ще для однієї цілі - створення URL-ів. Насправді маршрутизація є бінарною, “двосторонньою” системою: вона прокладає шлях від URL-а до контролера + параметрів, а також у зворотному порядку: від маршруту + параметрів переходить назад до URL-а. Таку бінарну систему формують методи match() i generate(). Для прикладу візьмімо хоча б приклад маршруту blog_show, який ми вже розглядали раніше:

$params = $this->get('router')->match('/blog/my-blog-post');
// array(
//     'slug'        => 'my-blog-post',
//     '_controller' => 'AppBundle:Blog:show',
// )

$uri = $this->get('router')->generate('blog_show', array(
    'slug' => 'my-blog-post'
));
// /blog/my-blog-post

Для того, аби створити URL, нам слід вказати ім'я маршруту (у нашому випадку це blog_show) та будь-які метасимволи (такі як slug = my-blog-post), що використовуються у шляху цього маршруту. Маючи цю інформацію, можемо створити наступний URL:

class MainController extends Controller
{
    public function showAction($slug)
    {
        // ...

        $url = $this->generateUrl(
            'blog_show',
            array('slug' => 'my-blog-post')
        );
    }
}

! У тих контролерах, які не розширюють бази Symfony Controller, ви можете використовувати метод generate() сервісу router:

use Symfony\Component\DependencyInjection\ContainerAware;

class MainController extends ContainerAware
{
    public function showAction($slug)
    {
        // ...

        $url = $this->container->get('router')->generate(
            'blog_show',
            array('slug' => 'my-blog-post')
        );
    }
}

У наступному розділі книги ви дізнаєтеся про те, яким чином генерувати URL-и із внутрішніх шаблонів.

! У випадку, якщо верстка вашого додатку побудована із використанням запитів Ajax, у вас може виникнути бажання створювати URL-и у JavaScript на основі налаштувань вашого маршруту. Використовуючи FOSJsRoutingBundle, можна зробити, власне, таке:

var url = Routing.generate(
    'blog_show',
    {"slug": 'my-blog-post'}
);

Бажаєте більше інформації на задану тематику? В такому разі радимо почитати документацію по відповідному пакету.

Створення URL-ів через рядки запиту

Метод generate бере масив значень метасимволів і з них уже створює URI. Та якщо ви передасте котрийсь додатковий, не згаданий вище параметр, його буде додано до URI у вигляді рядка запиту:

$this->get('router')->generate('blog', array(
    'page' => 2,
    'category' => 'Symfony'
));
// /blog/2?category=Symfony

Створення URL-ів із шаблонів

Найприродніше створювати певний URL із шаблона тоді, коли ви встановлюєте зв'язки між сторінками вашого додатку. Виконання такої задачі нічим особливо не відрізняється від аналогічних, які ми розглядали раніше, за винятком, хіба, використання шаблонної функції-помічника:

Twig

<a href="{{ path('blog_show', {'slug': 'my-blog-post'}) }}">
  Read this blog post.
</a>

PHP

<a href="<?php echo $view['router']->generate('blog_show', array(
    'slug' => 'my-blog-post',
)) ?>">
    Read this blog post.
</a>

Створення абсолютних URL-ів

За замовчуванням, маршрутизатор створюватиме відповідні URL-и (наприклад, /blog).  Просто проставте true з контролера для третього аргумента методу generateUrl():

$this->generateUrl('blog_show', array('slug' => 'my-blog-post'), true);
// http://www.example.com/blog/my-blog-post

Для шаблона у Twig вам всього лиш потрібно використати функцію url() (що буде генерувати абсолютний URL) замість функції path() (яка генерує відповідний URL). Для PHP проставте true для generate():

Twig

<a href="{{ url('blog_show', {'slug': 'my-blog-post'}) }}">
  Read this blog post.
</a>

PHP

<a href="<?php echo $view['router']->generate('blog_show', array(
    'slug' => 'my-blog-post',
), true) ?>">
    Read this blog post.
</a>

! Хост, який використовується при створенні абсолютного URL, визначається автоматично через використання поточного об'єкта Request. При створенні абсолютних URL-ів за межами веб контексту (скажімо, через консольні команди) даний підхід працювати не буде. Див. посилання на статтю в оригіналі для детальнішого ознайомлення із методами вирішення цієї проблеми.

Висновки

Маршрутизація - це система, яка будує URL вхідного запиту до функції контролера, яку треба викликати для обробки вхідного запиту. Маршрутизація дозволяє вам контролювати як чіткість та візуальність вашого URL, так і його функціональність та незалежність від усього додатку. Маршрутизація - це “двосторонній” механізм, і це значить, що його рекомендовано використовувати і для створення URL-ів.

Поділитися