Маршрутизация URL-адресов PHP-приложений
-
02-07-2019 - |
Вопрос
Итак, я пишу фреймворк, на котором я хочу основать несколько приложений, над которыми я работаю (фреймворк есть, поэтому у меня есть среда для работы и система, которая позволит мне, например, использовать единый вход)
Я хочу создать этот фреймворк и приложения, которые в нем есть, используя ресурсоориентированную архитектуру.
Теперь я хочу создать класс маршрутизации URL, который может быть расширен авторами приложений (и, возможно, также пользователями приложений CMS, но это еще далеко в будущем), и я пытаюсь найти наилучший способ сделать это, посмотрев, как это делают другие приложения.
Решение
Я предпочитаю использовать reg ex, а не создавать свой собственный формат, поскольку это общеизвестно.Я написал небольшой класс, который я использую, который позволяет мне объединять эти таблицы маршрутизации reg ex.Я привык использовать что-то подобное, что было реализовано по наследованию, но оно не нуждалось в наследовании, поэтому я переписал его.
Я выполняю reg ex для ключа и сопоставляю его с моей собственной управляющей строкой.Возьмем приведенный ниже пример.Я навещаю /api/related/joe
и мой класс маршрутизатора создает новый объект ApiController
и вызывает этот метод relatedDocuments(array('tags' => 'joe'));
// the 12 strips the subdirectory my app is running in
$index = urldecode(substr($_SERVER["REQUEST_URI"], 12));
Route::process($index, array(
"#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags",
"#^thread/(.*)/post$#Di" => "ThreadController/post/title",
"#^thread/(.*)/reply$#Di" => "ThreadController/reply/title",
"#^thread/(.*)$#Di" => "ThreadController/thread/title",
"#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags",
"#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
"#^ajax/reply/(.*)$#Di" => "ArticleController/newReply/id",
"#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle",
"#^$#Di" => "HomeController",
));
Чтобы уменьшить количество ошибок и упростить работу, вы можете разделить свою таблицу на части.Таким образом, вы можете поместить таблицу маршрутизации в класс, которым она управляет.Взяв приведенный выше пример, вы можете объединить три вызова потока в один.
Route::process($index, array(
"#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags",
"#^thread/(.*)$#Di" => "ThreadController/route/uri",
"#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags",
"#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id",
"#^ajax/reply/(.*)$#Di" => "ArticleController/newReply/id",
"#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle",
"#^$#Di" => "HomeController",
));
Затем вы определяете ThreadController::route таким образом.
function route($args) {
Route::process($args['uri'], array(
"#^(.*)/post$#Di" => "ThreadController/post/title",
"#^(.*)/reply$#Di" => "ThreadController/reply/title",
"#^(.*)$#Di" => "ThreadController/thread/title",
));
}
Также вы можете определить любые значения по умолчанию, которые вы хотите, для вашей строки маршрутизации справа.Только не забудьте задокументировать их, иначе вы собьете людей с толку.В данный момент я вызываю index, если вы не указываете имя функции справа. Здесь это мой текущий код.Возможно, вы захотите изменить его, чтобы обрабатывать ошибки так, как вам нравится, и / или действия по умолчанию.
Другие советы
Еще один фреймворк?-- в любом случае...
Хитрость с маршрутизацией заключается в том, чтобы передать все это вашему контроллеру маршрутизации.
Вероятно, вы хотели бы использовать что-то похожее на то, что я описал здесь:
http://www.hm2k.com/posts/friendly-urls
Второе решение позволяет вам использовать URL-адреса, аналогичные Zend Framework.
Используйте список регулярных выражений, чтобы определить, какой объект я должен использовать
Например
^/users/[\w-]+/bookmarks/(.+)/$
^/users/[\w-]+/bookmarks/$
^/users/[\w-]+/$
Плюсы:Красиво и просто, позволяет мне определять маршруты напрямую Минусы:Пришлось бы заказывать, что не облегчало бы добавление новых элементов (очень подвержено ошибкам)
Вот, afaik, как Django это делает
Я думаю, что многие фреймворки используют комбинацию mod_rewrite от Apache и фронтального контроллера.С помощью mod_rewrite вы можете изменить URL следующим образом:/ люди/вовлекаются/3 в это:index.php?контроллер=людииметод=получитьиидентификатор=3.Index.php реализовал бы ваш фронт-контроллер, который маршрутизирует запрос страницы на основе заданных параметров.
Как и следовало ожидать, существует множество способов сделать это.
Например, в Тонкий Каркас , примером механизма маршрутизации может быть following (основанный на шаблоне ${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK})
):
$app->get("/Home", function() {
print('Welcome to the home page');
}
$app->get('/Profile/:memberName', function($memberName) {
print( 'I\'m viewing ' . $memberName . '\'s profile.' );
}
$app->post('/ContactUs', function() {
print( 'This action will be fired only if a POST request will occure');
}
Итак, инициализированный экземпляр ($app
) получает метод для каждого метода запроса (например,get, post, put, delete и т.д.) и получает маршрут в качестве первого параметра и обратный вызов в качестве второго.
Маршрут может получать токены - это "переменная", которая будет меняться во время выполнения на основе некоторых данных (таких как имя участника, идентификатор статьи, название местоположения организации или что-то еще - вы знаете, точно так же, как в каждом контроллере маршрутизации).
Лично мне нравится этот способ, но я не думаю, что он будет достаточно гибким для продвинутого фреймворка.
Поскольку в настоящее время я работаю с ZF и Yii, у меня есть пример маршрутизатора, который я создал как часть платформы для компании, в которой я работаю:
Механизм маршрутизации основан на регулярных выражениях (аналогично механизму @gradbot), но поддерживает двусторонний диалог, поэтому, если ваш клиент не может запустить mod_rewrite (в Apache) или добавить правила перезаписи на своем сервере, он все равно может использовать традиционные URL-адреса со строкой запроса.
Файл содержит массив, каждый из которых, каждый элемент аналогичен этому примеру:
$_FURLTEMPLATES['login'] = array(
'i' => array( // Input - how the router parse an incomming path into query string params
'pattern' => '@Members/Login/?@i',
'matches' => array( 'Application' => 'Members', 'Module' => 'Login' ),
),
'o' => array( // Output - how the router parse a query string into a route
'@Application=Members(&|&)Module=Login/?@' => 'Members/Login/'
)
);
Вы также можете использовать более сложные комбинации, такие как:
$_FURLTEMPLATES['article'] = array(
'i' => array(
'pattern' => '@CMS/Articles/([\d]+)/?@i',
'matches' => array( 'Application' => "CMS",
'Module' => 'Articles',
'Sector' => 'showArticle',
'ArticleID' => '$1' ),
),
'o' => array(
'@Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)@' => 'CMS/Articles/$4'
)
);
Суть, как я думаю, в том, что возможности безграничны, это просто зависит от того, насколько сложным вы хотите сделать свой фреймворк и что вы хотите с ним делать.
Если это, например, просто веб-сервис или простая оболочка веб-сайта - просто используйте стиль написания Slim framework - очень простой и привлекательный код.
Однако, если вы хотите разрабатывать сложные сайты, используя его, я думаю, что регулярное выражение - это решение.
Удачи вам!:)
Тебе стоит проверить Pux https://github.com/c9s/Pux
Вот краткий обзор
<?php
require 'vendor/autoload.php'; // use PCRE patterns you need Pux\PatternCompiler class.
use Pux\Executor;
class ProductController {
public function listAction() {
return 'product list';
}
public function itemAction($id) {
return "product $id";
}
}
$mux = new Pux\Mux;
$mux->any('/product', ['ProductController','listAction']);
$mux->get('/product/:id', ['ProductController','itemAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$mux->post('/product/:id', ['ProductController','updateAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$mux->delete('/product/:id', ['ProductController','deleteAction'] , [
'require' => [ 'id' => '\d+', ],
'default' => [ 'id' => '1', ]
]);
$route = $mux->dispatch('/product/1');
Executor::execute($route);
MVC-фреймворк Zend по умолчанию использует структуру, подобную
/router/controller/action/key1/value1/key2/value2
где router
является ли файл маршрутизатора (сопоставленный через mod_rewrite
, controller
происходит от обработчика действия контроллера, который определяется классом, производным от Zend_Controller_Action
и action
ссылается на метод в контроллере с именем actionAction
.Пары ключ/значение могут располагаться в любом порядке и доступны методу action в виде ассоциативного массива.
Я использовал нечто подобное в прошлом в своем собственном коде, и до сих пор это работало довольно хорошо.
Попробуйте взглянуть на MVC закономерность.
Например, Zend Framework использует его, но также CakePHP, CodeIgniter, ...
Лично мне не нравится модель MVC, но в большинстве случаев она реализована как компонент "Просмотр для Интернета".
Решение в значительной степени зависит от предпочтений...