Маршрутизация
Компонент маршрутизатора позволяет определять маршруты, которые сопоставляются с контроллерами или обработчиками, которые должны получать запрос. Маршрутизатор просто анализирует URI, чтобы определить эту информацию. Маршрутизатор имеет два режима: режим MVC и режим только соответствия. Первый режим идеально подходит для работы с приложениями MVC.
Определение маршрутов
Phalcon\Mvc\Router предоставляет расширенные возможности маршрутизации. В режиме MVC вы можете определять маршруты и сопоставлять их с необходимыми контроллерами/действиями. Маршрут определяется следующим образом:
// Create the router
$router = new \Phalcon\Mvc\Router();
//Define a route
$router->add(
"/admin/users/my-profile",
array(
"controller" => "users",
"action" => "profile",
)
);
//Another route
$router->add(
"/admin/users/change-password",
array(
"controller" => "users",
"action" => "changePassword",
)
);
$router->handle();
Метод add() получает в качестве первого параметра шаблон и необязательно набор путей в качестве второго параметра. В этом случае, если URI точно равен: /admin/users/my-profile, то будет выполнен контроллер «users» с действием «profile». В настоящее время маршрутизатор не выполняет контроллер и действие, он только собирает эту информацию, чтобы сообщить правильному компоненту (например, Phalcon\Mvc\Dispatcher), что это контроллер/действие, которое он должен выполнить.
Приложение может иметь множество путей, определять маршруты по одному может быть трудоемкой задачей. В таких случаях мы можем создавать более гибкие маршруты:
// Create the router
$router = new \Phalcon\Mvc\Router();
//Define a route
$router->add(
"/admin/:controller/a/:action/:params",
array(
"controller" => 1,
"action" => 2,
"params" => 3,
)
);
В приведенном примере, используя подстановки, мы делаем маршрут допустимым для многих URI. Например, обратившись к следующему URL (/admin/users/a/delete/dave/301), тогда:
| Контроллер | users |
| Действие | delete |
| Параметр | dave |
| Параметр | 301 |
Метод add() получает шаблон, который необязательно может содержать предопределенные подстановки и модификаторы регулярных выражений. Все шаблоны маршрутизации должны начинаться с символа косой черты (/). Синтаксис регулярных выражений используется тот же, что и в PCRE регулярных выражениях. Обратите внимание, что не нужно добавлять разделители регулярных выражений. Все шаблоны маршрутов нечувствительны к регистру.
Второй параметр определяет, как сопоставляемые части должны связываться с контроллером/действием/параметрами. Сопоставляемые части являются подстановками или субпаттернами, ограниченными круглыми скобками.
Эти подстановки помогают писать регулярные выражения, которые более читабельны для разработчиков и легче понимаются. Поддерживаются следующие подстановки:
| Подстановка | Регулярное выражение | Использование |
|---|---|---|
| /:module | /([a-zA-Z0-9_-]+) | Сопоставляет допустимое имя модуля только с буквенно-цифровыми символами |
| /:controller | /([a-zA-Z0-9_-]+) | Сопоставляет допустимое имя контроллера только с буквенно-цифровыми символами |
| /:action | /([a-zA-Z0-9_]+) | Сопоставляет допустимое имя действия только с буквенно-цифровыми символами |
| /:params | (/.*)* | Сопоставляет список необязательных слов, разделенных косыми чертами. Используйте только эту подстановку в конце маршрута |
| /:namespace | /([a-zA-Z0-9_-]+) | Сопоставляет имя пространства имен одного уровня |
| /:int | /([0-9]+) | Сопоставляет целое число параметра |
Имена контроллеров написаны в camelized стиле, это означает, что символы (-) и (_) удаляются, а следующий символ преобразуется в заглавную букву. Например, some_controller преобразуется в SomeController.
Поскольку вы можете добавлять столько маршрутов, сколько вам нужно, используя add(), порядок добавления маршрутов указывает на их релевантность. Последние добавленные маршруты более релевантны, чем первые. Внутренне все определенные маршруты просматриваются в обратном порядке, пока Phalcon\Mvc\Router не найдет тот, который соответствует заданному URI, и не обработает его, проигнорировав остальные.
Параметры с именами
Пример ниже демонстрирует, как определять имена для параметров маршрута:
$router->add(
"/news/([0-9]{4})/([0-9]{2})/([0-9]{2})/:params",
array(
"controller" => "posts",
"action" => "show",
"year" => 1, // ([0-9]{4})
"month" => 2, // ([0-9]{2})
"day" => 3, // ([0-9]{2})
"params" => 4, // :params
)
);
В приведенном примере маршрут не определяет часть «controller» или «action». Эти части заменяются фиксированными значениями («posts» и «show»). Пользователь не будет знать контроллер, который фактически обрабатывается запросом. Внутри контроллера эти именованные параметры можно получить следующим образом:
class PostsController extends \Phalcon\Mvc\Controller
{
public function indexAction()
{
}
public function showAction()
{
// Return "year" parameter
$year = $this->dispatcher->getParam("year");
// Return "month" parameter
$month = $this->dispatcher->getParam("month");
// Return "day" parameter
$day = $this->dispatcher->getParam("day");
}
}
Обратите внимание, что значения параметров получаются из диспетчера. Это происходит потому, что это компонент, который в конечном итоге взаимодействует с драйверами вашего приложения. Кроме того, есть и другой способ создания именованных параметров как части шаблона:
$router->add(
"/documentation/{chapter}/{name}.{type:[a-z]+}",
array(
"controller" => "documentation",
"action" => "show"
)
);
Вы можете получить их значения так же, как и раньше:
class DocumentationController extends \Phalcon\Mvc\Controller
{
public function showAction()
{
// Returns "name" parameter
$name = $this->dispatcher->getParam("name");
// Returns "type" parameter
$type = $this->dispatcher->getParam("type");
}
}
Короткая синтаксис
Если вам не нравится использовать массив для определения путей маршрута, доступен и альтернативный синтаксис. Следующие примеры дают тот же результат:
// Short form
$router->add("/posts/{year:[0-9]+}/{title:[a-z\-]+}", "Posts::show");
// Array form
$router->add(
"/posts/([0-9]+)/([a-z\-]+)",
array(
"controller" => "posts",
"action" => "show",
"year" => 1,
"title" => 2,
)
);
Смешение массива и короткого синтаксиса
Массив и короткий синтаксис могут быть объединены для определения маршрута. В этом случае обратите внимание, что именованные параметры автоматически добавляются к путям маршрута в соответствии с позицией, в которой они были определены:
//First position must be skipped because it is used for
//the named parameter 'country'
$router->add('/news/{country:[a-z]{2}}/([a-z+])/([a-z\-+])',
array(
'section' => 2, //Positions start with 2
'article' => 3
)
);
Маршрутизация к модулям
Вы можете определять маршруты, пути которых включают модули. Это особенно подходит для приложений с несколькими модулями. Можно определить маршрут по умолчанию, который включает подстановку имени модуля:
$router = new Phalcon\Mvc\Router(false);
$router->add('/:module/:controller/:action/:params', array(
'module' => 1,
'controller' => 2,
'action' => 3,
'params' => 4
));
В этом случае имя модуля всегда должно быть частью URL. Например, следующий URL: /admin/users/edit/sonny, будет обработан как:
| Модуль | admin |
| Контроллер | users |
| Действие | edit |
| Параметр | sonny |
Или вы можете связать определенные маршруты с определенными модулями:
$router->add("/login", array(
'module' => 'backend',
'controller' => 'login',
'action' => 'index',
));
$router->add("/products/:action", array(
'module' => 'frontend',
'controller' => 'products',
'action' => 1,
));
Или связать их со специфическими пространствами имен:
$router->add("/:namespace/login", array(
'namespace' => 1,
'controller' => 'login',
'action' => 'index'
));
Пространства имен/имена классов должны передаваться раздельно:
$router->add("/login", array(
'namespace' => 'Backend\Controllers',
'controller' => 'login',
'action' => 'index'
));
Ограничения по HTTP-методам
Когда вы добавляете маршрут, используя просто add(), маршрут будет доступен для любого HTTP-метода. Иногда можно ограничить маршрут определенным методом, это особенно полезно при создании RESTful приложений:
// This route only will be matched if the HTTP method is GET
$router->addGet("/products/edit/{id}", "Products::edit");
// This route only will be matched if the HTTP method is POST
$router->addPost("/products/save", "Products::save");
// This route will be matched if the HTTP method is POST or PUT
$router->add("/products/update")->via(array("POST", "PUT"));
Использование преобразований
Преобразования позволяют свободно преобразовывать параметры маршрута перед передачей их диспетчеру. Следующие примеры показывают, как их использовать:
//The action name allows dashes, an action can be: /products/new-ipod-nano-4-generation
$router
->add('/products/{slug:[a-z\-]+}', array(
'controller' => 'products',
'action' => 'show'
))
->convert('slug', function($slug) {
//Transform the slug removing the dashes
return str_replace('-', '', $slug);
});
Группы маршрутов
Если набор маршрутов имеет общие пути, их можно сгруппировать для удобства поддержки:
$router = new \Phalcon\Mvc\Router();
//Create a group with a common module and controller
$blog = new \Phalcon\Mvc\Router\Group(array(
'module' => 'blog',
'controller' => 'index'
));
//All the routes start with /blog
$blog->setPrefix('/blog');
//Add a route to the group
$blog->add('/save', array(
'action' => 'save'
));
//Add another route to the group
$blog->add('/edit/{id}', array(
'action' => 'edit'
));
//This route maps to a controller different than the default
$blog->add('/blog', array(
'controller' => 'blog',
'action' => 'index'
));
//Add the group to the router
$router->mount($blog);
Вы можете перемещать группы маршрутов в отдельные файлы, чтобы улучшить организацию и повторное использование кода в приложении:
class BlogRoutes extends Phalcon\Mvc\Router\Group
{
public function initialize()
{
//Default paths
$this->setPaths(array(
'module' => 'blog',
'namespace' => 'Blog\Controllers'
));
//All the routes start with /blog
$this->setPrefix('/blog');
//Add a route to the group
$this->add('/save', array(
'action' => 'save'
));
//Add another route to the group
$this->add('/edit/{id}', array(
'action' => 'edit'
));
//This route maps to a controller different than the default
$this->add('/blog', array(
'controller' => 'blog',
'action' => 'index'
));
}
}
Затем смонтировать группу в маршрутизаторе:
//Add the group to the router $router->mount(new BlogRoutes());
Сопоставление маршрутов
Для того чтобы маршрутизатор проверил маршрут, соответствующий заданному URI, должен быть передан допустимый URI. По умолчанию URI маршрутизации берется из переменной $_GET[‘_url’], которая создается модулем модуля переписывания. Несколько правил переписывания, которые отлично работают с Phalcon:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
Следующий пример демонстрирует, как использовать этот компонент в автономном режиме:
// Creating a router
$router = new \Phalcon\Mvc\Router();
// Define routes here if any
// ...
// Taking URI from $_GET["_url"]
$router->handle();
// or Setting the URI value directly
$router->handle("/employees/edit/17");
// Getting the processed controller
echo $router->getControllerName();
// Getting the processed action
echo $router->getActionName();
//Get the matched route
$route = $router->getMatchedRoute();
Именование маршрутов
Каждый маршрут, который добавляется в маршрутизатор, хранится внутри как объект Phalcon\Mvc\Router\Route. Этот класс инкапсулирует все детали каждого маршрута. Например, мы можем присвоить имя пути, чтобы однозначно идентифицировать его в нашем приложении. Это особенно полезно, если вы хотите создать URL из него.
$route = $router->add("/posts/{year}/{title}", "Posts::show");
$route->setName("show-posts");
//or just
$router->add("/posts/{year}/{title}", "Posts::show")->setName("show-posts");
Затем, например, используя компонент Phalcon\Mvc\Url, мы можем создать маршруты по его имени:
// returns /posts/2012/phalcon-1-0-released
echo $url->get(array(
"for" => "show-posts",
"year" => "2012",
"title" => "phalcon-1-0-released"
));
Примеры использования
Ниже приведены примеры пользовательских маршрутов:
// matches "/system/admin/a/edit/7001"
$router->add(
"/system/:controller/a/:action/:params",
array(
"controller" => 1,
"action" => 2,
"params" => 3
)
);
// matches "/es/news"
$router->add(
"/([a-z]{2})/:controller",
array(
"controller" => 2,
"action" => "index",
"language" => 1
)
);
// matches "/es/news"
$router->add(
"/{language:[a-z]{2}}/:controller",
array(
"controller" => 2,
"action" => "index"
)
);
// matches "/admin/posts/edit/100"
$router->add(
"/admin/:controller/:action/:int",
array(
"controller" => 1,
"action" => 2,
"id" => 3
)
);
// matches "/posts/2010/02/some-cool-content"
$router->add(
"/posts/([0-9]{4})/([0-9]{2})/([a-z\-]+)",
array(
"controller" => "posts",
"action" => "show",
"year" => 1,
"month" => 2,
"title" => 4
)
);
// matches "/manual/en/translate.adapter.html"
$router->add(
"/manual/([a-z]{2})/([a-z\.]+)\.html",
array(
"controller" => "manual",
"action" => "show",
"language" => 1,
"file" => 2
)
);
// matches /feed/fr/le-robots-hot-news.atom
$router->add(
"/feed/{lang:[a-z]+}/{blog:[a-z\-]+}\.{type:[a-z\-]+}",
"Feed::get"
);
// matches /api/v1/users/peter.json
$router->add('/api/(v1|v2)/{method:[a-z]+}/{param:[a-z]+}\.(json|xml)',
array(
'controller' => 'api',
'version' => 1,
'format' => 4
)
);
Будьте внимательны к символам, разрешенным в регулярных выражениях для контроллеров и пространств имен. Поскольку они становятся именами классов, а те в свою очередь передаются через файловую систему, злоумышленники могут использовать их для считывания несанкционированных файлов. Безопасное регулярное выражение: /([a-zA-Z0-9_-]+)
Поведение по умолчанию
Phalcon\Mvc\Router имеет поведение по умолчанию, предоставляющее очень простую маршрутизацию, которая всегда ожидает URI, соответствующий следующему шаблону: /:controller/:action/:params
Например, для URL, такого как http://phalconphp.com/documentation/show/about.html, этот маршрутизатор переведет его следующим образом:
| Контроллер | documentation |
| Действие | show |
| Параметр | about.html |
Если вы не хотите использовать эти маршруты по умолчанию в своем приложении, вы должны создать маршрутизатор, передав false в качестве параметра:
// Create the router without default routes $router = new \Phalcon\Mvc\Router(false);
Установка маршрута по умолчанию
Когда к вашему приложению обращаются без какого-либо маршрута, маршрут '/' используется для определения путей, которые должны использоваться для отображения начальной страницы вашего сайта/приложения:
$router->add("/", array(
'controller' => 'index',
'action' => 'index'
));
Пути не найдены
Если ни один из маршрутов, указанных в маршрутизаторе, не соответствует, вы можете определить группу путей, которые будут использоваться в этом случае:
//Set 404 paths
$router->notFound(array(
"controller" => "index",
"action" => "route404"
));
Установка путей по умолчанию
Можно определить значения по умолчанию для общих путей, таких как модуль, контроллер или действие. Когда маршрут отсутствует какой-либо из этих путей, они могут быть автоматически заполнены маршрутизатором:
//Setting a specific default
$router->setDefaultModule('backend');
$router->setDefaultNamespace('Backend\Controllers');
$router->setDefaultController('index');
$router->setDefaultAction('index');
//Using an array
$router->setDefaults(array(
'controller' => 'index',
'action' => 'index'
));
Обработка дополнительных/конечных косых черт
Иногда к маршруту можно обратиться с дополнительными/конечными косыми чертами в конце маршрута. Эти дополнительные косые черты приведут к статусу «не найдено» в диспетчере. Вы можете настроить маршрутизатор на автоматическое удаление косых черт с конца обрабатываемого маршрута:
$router = new \Phalcon\Mvc\Router(); //Remove trailing slashes automatically $router->removeExtraSlashes(true);
Или вы можете изменить определенные маршруты, чтобы они могли по желанию принимать конечные косые черты:
$router->add(
'/{language:[a-z]{2}}/:controller[/]{0,1}',
array(
'controller' => 2,
'action' => 'index'
)
);
Обратные вызовы соответствия
Иногда маршруты должны соответствовать, если они удовлетворяют определенным условиям. Вы можете добавить произвольные условия к маршрутам, используя обратный вызов «beforeMatch». Если эта функция возвращает false, маршрут будет обрабатываться как несоответствующий:
$router->add('/login', array(
'module' => 'admin',
'controller' => 'session'
))->beforeMatch(function($uri, $route) {
//Check if the request was made with Ajax
if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'xmlhttprequest') {
return false;
}
return true;
});
Вы можете повторно использовать эти дополнительные условия в классах:
class AjaxFilter
{
public function check()
{
return $_SERVER['HTTP_X_REQUESTED_WITH'] == 'xmlhttprequest';
}
}
И использовать этот класс вместо анонимной функции:
$router->add('/get/info/{id}', array(
'controller' => 'products',
'action' => 'info'
))->beforeMatch(array(new AjaxFilter(), 'check'));
Ограничения на имена хостов
Маршрутизатор позволяет задавать ограничения на имена хостов, что означает, что определенные маршруты или группа маршрутов могут быть ограничены, чтобы соответствовать только в том случае, если маршрут также соответствует ограничению имени хоста:
$router->add('/login', array(
'module' => 'admin',
'controller' => 'session',
'action' => 'login'
))->setHostName('admin.company.com');
Имя хоста также может быть регулярными выражениями:
$router->add('/login', array(
'module' => 'admin',
'controller' => 'session',
'action' => 'login'
))->setHostName('([a-z+]).company.com');
В группах маршрутов вы можете задать ограничение на имя хоста, которое применяется ко всем маршрутам в группе:
//Create a group with a common module and controller
$blog = new \Phalcon\Mvc\Router\Group(array(
'module' => 'blog',
'controller' => 'posts'
));
//Hostname restriction
$blog->setHostName('blog.mycompany.com');
//All the routes start with /blog
$blog->setPrefix('/blog');
//Default route
$blog->add('/', array(
'action' => 'index'
));
//Add a route to the group
$blog->add('/save', array(
'action' => 'save'
));
//Add another route to the group
$blog->add('/edit/{id}', array(
'action' => 'edit'
));
//Add the group to the router
$router->mount($blog);
Источники URI
По умолчанию информация URI извлекается из переменной $_GET[‘_url’], которая передается Rewrite-Engine в Phalcon. Вы также можете использовать $_SERVER[‘REQUEST_URI’], если это необходимо:
$router->setUriSource(Router::URI_SOURCE_GET_URL); // use $_GET['_url'] (default) $router->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI); // use $_SERVER['REQUEST_URI'] (default)
Или вы можете вручную передать URI методу «handle»:
$router->handle('/some/route/to/handle');
Тестирование ваших маршрутов
Поскольку этот компонент не зависит от других, вы можете создать файл, как показано ниже, для тестирования ваших маршрутов:
//These routes simulate real URIs
$testRoutes = array(
'/',
'/index',
'/index/index',
'/index/test',
'/products',
'/products/index/',
'/products/show/101',
);
$router = new Phalcon\Mvc\Router();
//Add here your custom routes
//...
//Testing each route
foreach ($testRoutes as $testRoute) {
//Handle the route
$router->handle($testRoute);
echo 'Testing ', $testRoute, '<br>';
//Check if some route was matched
if ($router->wasMatched()) {
echo 'Controller: ', $router->getControllerName(), '<br>';
echo 'Action: ', $router->getActionName(), '<br>';
} else {
echo 'The route wasn\'t matched by any route<br>';
}
echo '<br>';
}
Маршрутизатор с аннотациями
Этот компонент предоставляет вариант, интегрированный с сервисом аннотаций. Используя эту стратегию, вы можете записывать маршруты непосредственно в контроллеры, вместо добавления их в регистрацию сервиса:
$di['router'] = function() {
//Use the annotations router
$router = new \Phalcon\Mvc\Router\Annotations(false);
//Read the annotations from ProductsController if the uri starts with /api/products
$router->addResource('Products', '/api/products');
return $router;
};
Аннотации можно определить следующим образом:
/**
* @RoutePrefix("/api/products")
*/
class ProductsController
{
/**
* @Get("/")
*/
public function indexAction()
{
}
/**
* @Get("/edit/{id:[0-9]+}", name="edit-robot")
*/
public function editAction($id)
{
}
/**
* @Route("/save", methods={"POST", "PUT"}, name="save-robot")
*/
public function saveAction()
{
}
/**
* @Route("/delete/{id:[0-9]+}", methods="DELETE",
* conversors={id="MyConversors::checkId"})
*/
public function deleteAction($id)
{
}
public function infoAction($id)
{
}
}
Используются только методы, помеченные допустимыми аннотациями, как маршруты. Список поддерживаемых аннотаций:
| Имя | Описание | Использование |
|---|---|---|
| RoutePrefix | Префикс, который добавляется к каждому URI маршрута. Эта аннотация должна быть размещена в блоке документации класса | @RoutePrefix(“/api/products”) |
| Route | Эта аннотация помечает метод как маршрут. Эта аннотация должна быть размещена в блоке документации метода | @Route(“/api/products/show”) |
| Get | Эта аннотация помечает метод как маршрут, ограничивая HTTP-метод GET | @Get(“/api/products/search”) |
| Post | Эта аннотация помечает метод как маршрут, ограничивая HTTP-метод POST | @Post(“/api/products/save”) |
| Put | Эта аннотация помечает метод как маршрут, ограничивая HTTP-метод PUT | @Put(“/api/products/save”) |
| Delete | Эта аннотация помечает метод как маршрут, ограничивая HTTP-метод DELETE | @Delete(“/api/products/delete/{id}”) |
| Options | Эта аннотация помечает метод как маршрут, ограничивая HTTP-метод OPTIONS | @Option(“/api/products/info”) |
Для аннотаций, добавляющих маршруты, поддерживаются следующие параметры:
| Имя | Описание | Использование |
|---|---|---|
| methods | Определяет один или несколько HTTP-методов, которым должен соответствовать маршрут | @Route(“/api/products”, methods={“GET”, “POST”}) |
| name | Определяет имя маршрута | @Route(“/api/products”, name=”get-products”) |
| paths | Массив путей, подобный передаваемому в Phalcon\Mvc\Router::add | @Route(“/posts/{id}/{slug}”, paths={module=”backend”}) |
| conversors | Хеш конверторов, которые необходимо применить к параметрам | @Route(“/posts/{id}/{slug}”, conversors={id=”MyConversor::getId”}) |
Если маршруты сопоставляются с контроллерами в модулях, лучше использовать метод addModuleResource:
$di['router'] = function() {
//Use the annotations router
$router = new \Phalcon\Mvc\Router\Annotations(false);
//Read the annotations from Backend\Controllers\ProductsController if the uri starts with /api/products
$router->addModuleResource('backend', 'Products', '/api/products');
return $router;
};
Регистрация экземпляра маршрутизатора
Вы можете зарегистрировать маршрутизатор во время регистрации сервиса с помощью инжектора зависимостей Phalcon, чтобы сделать его доступным внутри контроллера.
Вам нужно добавить код ниже в свой файл загрузки (например, index.php или app/config/services.php, если вы используете Phalcon Developer Tools)
/**
* add routing capabilities
*/
$di->set('router', function(){
require __DIR__.'/../app/config/routes.php';
return $router;
});
Вам нужно создать app/config/routes.php и добавить код инициализации маршрутизатора, например:
$router = new \Phalcon\Mvc\Router();
$router->add("/login", array(
'controller' => 'login',
'action' => 'index',
));
$router->add("/products/:action", array(
'controller' => 'products',
'action' => 1,
));
return $router;
Реализация собственного маршрутизатора
Интерфейс Phalcon\Mvc\RouterInterface должен быть реализован для создания собственного маршрутизатора, заменяющего предоставленный Phalcon.
© 2011–2016 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/2.0.0/reference/routing.html