Компонент Маршрутизация

Компонент Маршрутизация соединяет HTTP запрос с набором переменных конфигурации.

Установка

Вы можете установить компонент 2 разными способами:

Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application won't be able to find the classes of this Symfony component.

Использование

Для тог, чтобы установить базову систему маршрутизации, вам нужно три составляющие:

  • RouteCollection, который содержит определения маршрутов (экземпляры класса Route)
  • RequestContext, который содержит информацию о запросе
  • UrlMatcher, который выполняет соединение запроса с единственным маршрутом

Вот быстрый пример. Заметьте, что предполагается, что вы уже сконфигурировали ваш автозагрузчик, чтобы он загружал компонент Маршрутизация:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$route = new Route('/foo', array('_controller' => 'MyController'));
$routes = new RouteCollection();
$routes->add('route_name', $route);

$context = new RequestContext('/');

$matcher = new UrlMatcher($routes, $context);

$parameters = $matcher->match('/foo');
// массив ('_controller' => 'MyController', '_route' => 'route_name')

Note

Параметры RequestContext могут быть наполнены значениями, хранящимися в $_SERVER, но легче использовать компонент HttpFoundation, как объясняется ниже.

Вы можете добавлять к RouteCollection столько маршрутов, сколько хотите.

Метод RouteCollection::add() берёт два аргумента. Первый - это имя маршрута. Второй - объект Route, который ожидает URL путь и некоторый массив пользовательских переменных в своём конструкторе. Этот массив пользовательских переменных может быть чем угодно важным для вашего приложения и возвращаться при сопоставлении маршрута.

UrlMatcher::match() возвращает переменные, которые вы установили в маршруте, а также рандомные заполнители (см. ниже). Теперь ваше приложение может использовать эту информацию, чтобы продолжать обработку запроса. В дополенение к сконфигурированным переменным, добавляется ключ _route, который содержит имя соответствующего маршрута.

Если соответствующий маршрут не может быть найден, будет вызвано ResourceNotFoundException.

Определение маршрутов

Полное определение маршрута может содержать до семи частей:

  1. Маршрут пути URL. Он сопоставляется с URL, переданным RequestContext, и может содержать названные рандомные заполнители (например, {placeholders}), чтобы соответствовать динамическим частям URL.
  2. Массив значений по умолчанию. Содержит массив арбитражных значений, которые будут возвращены, когда запрос соответствует маршруту.
  3. Массив требований. Он определяет ограничения значений заполнителей в виде регулярных выражений.
  4. Массив опций. Они содержит внутренние настройки для маршрута и является наименее необходимыми.
  5. Хост. Сопоставляется с хостом запроса. Смотрите How to Match a Route Based on the Host, чтобы узнать больше.
  6. Массив схем. Форсирует определённую HTTP схему (http, https).
  7. Массив методов. Форсирует определённый метод HTTP запроса (HEAD, GET, POST, ...).

Рассмотрите следующий маршрут, который объединяет несколько из этих идей:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$route = new Route(
    '/archive/{month}', // путь
    array('_controller' => 'showArchive'), // default values
    array('month' => '[0-9]{4}-[0-9]{2}', 'subdomain' => 'www|m'), // требования
    array(), // опции
    '{subdomain}.example.com', // host
    array(), // схемы
    array() // методы
);

// ...

$parameters = $matcher->match('/archive/2012-01');
// array(
//     '_controller' => 'showArchive',
//     'month' => '2012-01',
//     'subdomain' => 'www',
//     '_route' => ...
//  )

$parameters = $matcher->match('/archive/foo');
// вызывает ResourceNotFoundException

В этом случае, маршрут сопоставляется с /archive/2012-01, так как метасимвол {month} соответствует регулярному выражению заданного метасимвола. Однако, /archive/foo не соответствует, так как "foo" не подходит метасимволу месяца.

При использовании метасимволов, они возвращаются в виде массива, при вызове match. Часть пути, с которой сопоставляется метасимвол (например, 2012-01), используется в качестве значения.

Tip

Если вы хотите сопоставить все URL, которые начинаются с определённого пути и заканчиваются произвольным суффиксом, вы можете использовать следующее определение маршрута:

1
2
3
4
5
$route = new Route(
    '/start/{suffix}',
    array('suffix' => ''),
    array('suffix' => '.*')
);

Использование префиксов

Вы можете добавлять маршруты или другие экземпляры RouteCollection к другой коллекции. Таким образом, вы можете построить древо маршрутов. Кроме того, вы можете определить префикс и значение по умолчанию для параметров, требований, опций, схем и хоста для всех маршрутов поддрева, используя методы, предоставленные классом RouteCollection:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$rootCollection = new RouteCollection();

$subCollection = new RouteCollection();
$subCollection->add(...);
$subCollection->add(...);
$subCollection->addPrefix('/prefix');
$subCollection->addDefaults(array(...));
$subCollection->addRequirements(array(...));
$subCollection->addOptions(array(...));
$subCollection->setHost('admin.example.com');
$subCollection->setMethods(array('POST'));
$subCollection->setSchemes(array('https'));

$rootCollection->addCollection($subCollection);

Установка параметров запроса

RequestContext предоставляет информацию о текущем запросе. Вы можете определить все параметры HTTP запроса с этим классом через его конструктор:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public function __construct(
    $baseUrl = '',
    $method = 'GET',
    $host = 'localhost',
    $scheme = 'http',
    $httpPort = 80,
    $httpsPort = 443,
    $path = '/',
    $queryString = ''
)

Обычно вы можете передать значения из переменной $_SERVER, чтобы наполнить RequestContext. Но если вы используете компонент HttpFoundation , вы можете использовать его класс Request, чтобы наполнить RequestContext с помощью сокращения:

1
2
3
4
use Symfony\Component\HttpFoundation\Request;

$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());

Генерирование URL

В то время как UrlMatcher, пытается найти маршрут, который соответствует заданному запросу, вы можете также построить URL из определённого маршрута:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$routes = new RouteCollection();
$routes->add('show_post', new Route('/show/{slug}'));

$context = new RequestContext('/');

$generator = new UrlGenerator($routes, $context);

$url = $generator->generate('show_post', array(
    'slug' => 'my-blog-post',
));
// /show/my-blog-post

Note

Если вы определили схему, генерируются абсолютный URL, если схема текущего RequestContext не соответствует требованию.

Загрузка маршрутов из файла

Вы уже видели, как вы с лёгкостью можете добавлять маршруты в коллекцию прямо внутри PHP. Но вы также можете загружать маршруты из множества различных файлов.

Компонент Маршрутизация поставляется с определённым количеством классов загрузчиков, и каждый предоставляет вам возможность загружать коллекцию определения маршрутов из внешнего файла какого-либо формата. Каждый загрузчик ожидает экземпляр FileLocator, в качестве аргумента конструктора. Вы можете использовать FileLocator, чтобы определить массив путей, в которых загрузчик будет искать запрошенные файлы. Если файл найден, загрузчик возвращает RouteCollection.

Если вы используете YamlFileLoader, то определения маршрута будут выглядеть так:

1
2
3
4
5
6
7
8
# routes.yml
route1:
    path:     /foo
    defaults: { _controller: 'MyController::fooAction' }

route2:
    path:     /foo/bar
    defaults: { _controller: 'MyController::foobarAction' }

Чтобы загрузить этот файл, вы можете использовать следующий код. Предполагается, что ваш файл routes.yml находится в том же каталоге, что и в коде ниже:

1
2
3
4
5
6
7
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;

// искать внутри *этого* каталога
$locator = new FileLocator(array(__DIR__));
$loader = new YamlFileLoader($locator);
$collection = $loader->load('routes.yml');

Кроме YamlFileLoader, существует два других загрузчика, которые работают таким же образом:

Если вы используете PhpFileLoader, то вам нужно предоставить имя PHP файла, который возвращает RouteCollection:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// RouteProvider.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add(
    'route_name',
    new Route('/foo', array('_controller' => 'ExampleController'))
);
// ...

return $collection;

Маршруты, как завершители

Также существует ClosureLoader, который вызывает завершитель, и использует результат в качестве RouteCollection:

1
2
3
4
5
6
7
8
use Symfony\Component\Routing\Loader\ClosureLoader;

$closure = function () {
    return new RouteCollection();
};

$loader = new ClosureLoader();
$collection = $loader->load($closure);

Маршруты, как аннотации

Наконец, существует AnnotationDirectoryLoader и AnnotationFileLoader, для загрузки определений маршрута из аннотаций класса. Конкретные детали здесь не рассматриваются.

Маршрутизавтор "всё-в-одном"

Класс Router является пакетом "всё-в-одном", для быстрого использования компонента Маршрутизация. Конструктор ожидают экземпляр загрузчика, путь к главному определению маршрута и некоторые другие настройки:

1
2
3
4
5
6
7
public function __construct(
    LoaderInterface $loader,
    $resource,
    array $options = array(),
    RequestContext $context = null,
    LoggerInterface $logger = null
);

С опцией cache_dir, вы можете включить кеширование маршрута (если вы предоставите путь), или отключить кеширование (если оно установлено, как null). Кеширование производится фоново автоматически, если вы хотите его использовать. Базовым примером класса Router будет:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$locator = new FileLocator(array(__DIR__));
$requestContext = new RequestContext('/');

$router = new Router(
    new YamlFileLoader($locator),
    'routes.yml',
    array('cache_dir' => __DIR__.'/cache'),
    $requestContext
);
$router->match('/foo/bar');

Note

Если вы используете кэширование, компонент Маршрутизация скомпилирует новые классы, которые сохраняются в cache_dir. Это значит, что ваш скрипт должен иметь разрешение на запись в этой локации.

Поддержка маршрутизации Unicode

New in version 3.2: Поддержка UTF-8 для путей и требований маршрута была представлена в Symfony 3.2.

Компонент Маршрутизация поддерживает символы UTF-8 в путях маршрута и требованиях. Благодаря опции маршрута utf8 , вы можете сделать так, чтобы Symfony сопоставляла и генерировала маршрут с использованием UTF-8 символов:

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    // src/AppBundle/Controller/DefaultController.php
    namespace AppBundle\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    
    class DefaultController extends Controller
    {
        /**
         * @Route("/category/{name}", name="route1", options={"utf8": true})
         */
        public function categoryAction()
        {
            // ...
        }
    
  • YAML
    1
    2
    3
    4
    5
    6
    # app/config/routing.yml
    route1:
        path:     /category/{name}
        defaults: { _controller: 'AppBundle:Default:category' }
        options:
            utf8: true
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    <!-- 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">
    
        <route id="route1" path="/category/{name}">
            <default key="_controller">AppBundle:Default:category</default>
            <option key="utf8">true</option>
        </route>
    </routes>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    
    $collection = new RouteCollection();
    $collection->add('route1', new Route('/category/{name}',
        array(
            '_controller' => 'AppBundle:Default:category',
        ),
        array(),
        array(
            'utf8' => true,
        )
    );
    
    // ...
    
    return $collection;
    

В этом маршруте, опция utf8 установлена как true, что заставляет Symfony рассмотреть требование . на соответствие каким-либо символам UTF-8, вместо одного символа байта. Это означает, что следующие URL будут соответствовать: /category/日本語, /category/فارسی, /category/한국어, и т.д. В случае, если вы хотели знать, эта опция также позволяет включать и сопоставлять эмоджи в URL.

Вы можете тоже включать UTF-8 строки в качестве требований маршрутизации:

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // src/AppBundle/Controller/DefaultController.php
    namespace AppBundle\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    
    class DefaultController extends Controller
    {
        /**
         * @Route(
         *     "/category/{name}",
         *     name="route2",
         *     requirements={"default"="한국어"},
         *     options={"utf8": true}
         * )
         */
        public function defaultAction()
        {
            // ...
        }
    
  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    # app/config/routing.yml
    route2:
        path:     /default/{default}
        defaults: { _controller: 'AppBundle:Default:default' }
        requirements:
            default: "한국어"
        options:
            utf8: true
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    <!-- 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">
    
        <route id="route2" path="/default/{default}">
            <default key="_controller">AppBundle:Default:default</default>
            <requirement key="default">한국어</requirement>
            <option key="utf8">true</option>
        </route>
    </routes>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    use Symfony\Component\Routing\Route;
    
    $collection = new RouteCollection();
    $collection->add('route2', new Route('/default/{default}',
        array(
            '_controller' => 'AppBundle:Default:default',
        ),
        array(
            'default' => '한국어',
        ),
        array(
            'utf8' => true,
        )
    );
    
    // ...
    
    return $collection;
    

Tip

Кроме символов UTF-8, компонент Маршрутизация также поддерживает все `свойства PCRE Unicode`_, которые экранируют последовательности, соответствующие общим типам символов. Например, \p{Lu} соответствует любым символам верхнего регистра на любом языке, \p{Greek} соответствует любому греческому символу, \P{Han} соответствует любому символу, не включённому в скрипт Chinese Han.

Note

В Symfony 3.2 нет необходимости ясно устанавливать опцию utf8. Как только Symfony находит символ в пути маршрута или требованиях, он автоматически включант поддержку UTF-8. Однако, это поведение устарело, и установка опции будет необходима в Symfony 4.0.

Узнать больше

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.