Компонент маршрутизации связывает HTTP запрос с набором заранее сконфигурированных данных.
Установка
Вы можете установить компонент двумя способами:
- Через Composer (symfony/routing – проект packagist.org)
- Используя официальный Git репозиторий (https://github.com/symfony/routing)
Затем, подключить автозагрузчик vendor/autoload.php, который предоставляет Composer. Иначе, ваше приложение не сможет найти необходимые для компонента классы.
Использование
Для развертывания системы маршрутизации, вам понадобится три класса:
- RouteCollection, который содержит описание маршрутов (экземпляры класса Route).
- RequestContext, — в нем информация о текущем запросе.
- UrlMatcher, — непосредственно связывает запрос, с заранее описанным маршрутом.
Простой пример. Предполагается, что вы уже сконфигурировали автолоадер, для загрузки классов компонента Routing.
- 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’);
- // array(‘_controller’ => ‘MyController’, ‘_route’ => ‘route_name’)
Параметры для RequestContext могут быть взяты из суперглобальной переменной $_SERVER, но проще воспользоваться компонентом HttpFoundation. Как это сделать, объясняется ниже.
Вы можете добавлять столько маршрутов в коллекцию RouteCollection, сколько захотите.
Метод RouteCollection::add() принимает два аргумента. Первый – название маршрута, второй – экземпляр класса Route. В конструктор Route, передается URL и массив с некоторыми данными, которые могут быть любыми, в зависимости от вашего приложения. Они будут возвращены при совпадении маршрута с текущим URL запроса.
UrlMatcher::match() – возвращает вышеупомянутые данные, вместе с плейсхолдерами (см. ниже). Ваше приложение может использовать их, для дальнейшей обработки запроса. В дополнение, к массиву данных, добавляется ключ _route, с именем совпавшего маршрута.
Если ни один из маршрутов не подходит для текущего запроса, выбрасывается исключение ResourceNotFoundException.
Определение (описание) маршрутов.
Полное описание маршрута содержит до семи составляющих.
- URL запроса. Он сравнивается с URL, переданным в RequestContext, и может содержать именованные плейсхолдеры (например, {placeholders}) для сравнения динамических частей урла.
- Массив значений по умолчанию. Некоторые данные, которые будут возвращены для найденного роута.
- Массив с обязательными параметрами. Проверяет значения (обычно часть URL) по шаблону, на базе регулярных выражений.
- Массив с настройками. В нем внутренние параметры для роута. Используется редко.
- Имя хоста. Сравнивается со значением из запроса. Смотрите статью «поиск маршрутов по хосту».
- Массив со схемами. Определяет конкретную HTTP схему: http или https.
- Массив с методами. Содержит конкретные методы HTTP запросов (HEAD, GET, POST, … )
Взгляните на следующий пример, который сочетает в себе некоторые из описанных идей:
- $route = new Route(
- ‘/archive/{month}’, // path
- array(‘_controller’ => ‘showArchive’), // default values
- // requirements
- array(‘month’ => ‘[0-9]{4}-[0-9]{2}’, ‘subdomain’ => ‘www|m’),
- array(), // options
- ‘{subdomain}.example.com’, // host
- array(), // schemes
- array() // methods
- );
- // …
- $parameters = $matcher—>match(‘/archive/2012-01’);
- // array(
- // ‘_controller’ => ‘showArchive’,
- // ‘month’ => ‘2012-01’,
- // ‘subdomain’ => ‘www’,
- // ‘_route’ => …
- // )
- $parameters = $matcher—>match(‘/archive/foo’);
- // throws ResourceNotFoundException
В данном примере, найден маршрут /archive/2012-01, потому что регулярное выражение шаблона совпало с запросом. Для /archive/foo совпадений не нашлось, исходя из той же логики.
Когда используются шаблоны, при вызове match в возвращаемом массиве оказывается часть совпавшего урла.
- $route = new Route(
- ‘/start/{suffix}’,
- array(‘suffix’ => »),
- array(‘suffix’ => ‘.*’)
- );
Использование префиксов
Существует возможность добавлять коллекции RouteCollection друг в друга. Таким образом, получается дерево маршрутов. В дополнение вы можете определить для поддеревьев префикс и значения по умолчанию: переменных, обязательных параметров, настроек, схем соединения и хоста через методы класса RouteCollection.
- $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 запроса для этого класса, через его конструктор:
- public function __construct(
- $baseUrl = »,
- $method = ‘GET’,
- $host = ‘localhost’,
- $scheme = ‘http’,
- $httpPort = 80,
- $httpsPort = 443,
- $path = ‘/’,
- $queryString = »
- )
В общем случае, можно передать значения из массива $_SERVER в RequestContext. Но если вы установили компонент HttpFoundation, просто передайте экземпляр класса Request:
- use Symfony\Component\HttpFoundation\Request;
- $context = new RequestContext();
- $context—>fromRequest(Request::createFromGlobals());
Более подробная информация о компоненте HttpFoundation.
Генерация URL на базе роута.
В то время, как задача UrlMatcher найти маршрут, соответствующий текущему запросу, вы так же можете произвести обратную операцию — сгенерировать URL на базе определенного маршрута:
- 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
Если была указана схема (http, https), то будет сгенерирован абсолютный URL, но лишь в том случае если схема текущего запроса в RequestContext отличается.
Загрузка маршрутов из файла
Вы уже убедились, насколько легко добавлять маршруты в коллекцию прямо в PHP скрипте. Однако, существует возможность их загрузки из различных типов файлов.
Компонент Routing поставляется вместе с классами-загрузчиками, каждый из которых дает возможность загрузить коллекцию маршрутов из внешнего файла, определенного формата. Каждому загрузчику следует передать экземпляр FileLocator, в качестве аргуметна конструктора. Вы можете использовать FileLocator для определения (создания) массива путей, по которым загрузчик будет искать запрошенный файл. Если таковой будет найден, загрузчик вернет объект RouteCollection.
К примеру, если используется загрузчик YamlFileLoader, описание маршрутов будет выглядеть так:
# routes.yml route1: path: /foo defaults: { _controller: 'MyController::fooAction' } route2: path: /foo/bar defaults: { _controller: 'MyController::foobarAction' } |
Для загрузки вышеозначенного файла, используется нижеследующий код. Подразумевается, что ваш routes.yml находится в той же директории, что и скрипт:
- use Symfony\Component\Config\FileLocator;
- use Symfony\Component\Routing\Loader\YamlFileLoader;
- // look inside *this* directory
- $locator = new FileLocator(array(__DIR__));
- $loader = new YamlFileLoader($locator);
- $collection = $loader—>load(‘routes.yml’);
Кроме YamlFileLoader, есть еще два загрузчика, которые работают по тому же принципу:
- XmlFileLoader
- PhpFileLoader
Если вы выберите PhpFileLoader, необходимо указать имя PHP файла, который вернет RouteCollection:
- // 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;
Описание маршрутов через замыкания (Closures)
Так же присутствует ClosureLoader, он вызывает замыкание, и использует результат в качестве RouteCollection:
- use Symfony\Component\Routing\Loader\ClosureLoader;
- $closure = function () {
- return new RouteCollection();
- };
- $loader = new ClosureLoader();
- $collection = $loader—>load($closure);
Описание маршрутов через аннотации
Последний, но не менее интересный тип – AnnotationDirectoryLoader и AnnotationFileLoader, для загрузки маршрутов из аннотаций классов. Описание указанных загрузчиков отсутствует в данной статье.
Универсальный класс Route
Route, является универсальным классом, который позволяет удобно использовать компонент Routing. Его конструктор предполагает в качестве аргументов — экземпляр загрузчика, путь к файлу с маршрутами, и еще некоторые параметры:
- public function __construct(
- LoaderInterface $loader,
- $resource,
- array $options = array(),
- RequestContext $context = null,
- LoggerInterface $logger = null
- );
С помощью опции cache_dir, включается кэширование маршрутов (в том случае если передан путь), или выключается (если он установлен в null). Кэширование происходит автоматически и в «фоновом режиме». Пример кода, в данном случае, будет таким:
- $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’);
При использовании кэширования, компонент Routing компилирует классы и сохраняет их в cache_dir. Ваш скрипт должен обладать правами записи в эту директорию.
Поддержка юникода (UTF-8)
Начиная с Symfony 3.2 включена поддержка UTF-8 для маршрутов и обязательных параметров.
Благодаря параметру utf8, компонент Routing поддерживает соответствующую кодировку:
- // 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, вместо однобайтовых. То есть, запрос /category/日本語 будут совпадать с маршрутом. Если интересно, при включенном параметре, так же поддерживаются символы эмоций в URL.
Аналогичным образом можно использовать UTF-8 в шаблонах:
- // 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;