Как реализовать поведение маршрутизации по умолчанию с Lumen или Slim Framework
Я смотрю на использование Lumen или, возможно, Slim для проекта, и удивляюсь, возможно ли автоматически загружать контроллеры на основе структуры каталогов, а не регистрировать все маршруты.
Вот так я бы хотел, чтобы автозагрузка работала.
Пример структуры каталогов / классов:
/app/Http/Controllers/
Foo/
BarController.php # App\Http\Controllers\Foo\BarController
Если бы маршрут был
example.com/foo/bar
== App\Http\Controllers\Foo\BarController::index()
example.com/foo/bar/add
== App\Http\Controllers\Foo\BarController::add()
Зарегистрированные маршруты должны иметь приоритет над автоматически загруженными классами.
Я нашел способ сделать это на основе метода маршрутизации Opencart. У них есть 1145 различных открытых методов на 396 контроллерах, которые вызываются без явного указания контроллера и метода. Вот моя попытка.
.htaccess
RewriteRule ^([^?]*) index.php?route=$1 [L,QSA]
приложение /Http/routes.php
$route = array_shift($_GET);
$method_name = '';
$parts = explode('/', preg_replace('/[^a-zA-Z0-9_\/]/', '', (string)$route));
while ($parts) {
$class = '\App\Http\Controllers\\' . implode('\\', $parts);
if (class_exists($class)){
$app->match($route, $class . '@' . method_exists($class, $method_name) ? $method_name : 'index');
break;
} else {
$method_name = array_pop($parts);
}
}
Если требуется маршрут, который отличается от установленного по умолчанию Opencart, тогда используйте .htaccess RewriteRule
или response->redirect
направить на альтернативный контроллер.
Я хотел бы использовать их подход, но указать свои переопределения маршрута в app/Http/routes.php
, вот так
// route overrides
$app->get('/', 'common/home@index');
$app->get('/home', 'common/home@index');
Правильно ли я думаю, что это заставит приложение работать быстрее, так как ему не придется искать все зарегистрированные маршруты на соответствие?
Есть ли лучший способ выполнить этот процесс автоматической маршрутизации?
1 ответ
Я думаю, что вы могли бы сделать это с помощью комбинации отражений и поддержки Slim 3 для определения маршрутов с помощью методов контроллера вместо замыканий.
Основная стратегия будет выглядеть следующим образом:
- Поиск по каждому из ваших классов контроллеров (используя
glob
или автозагрузчик); - Для каждого класса звоните
ReflectionClass::getMethods
, с использованиемReflectionMethod::IS_PUBLIC
фильтр, чтобы вы получили только открытые методы для класса; - Получить имя класса, используя
ReflectionClass::getName
и пространство имен (при необходимости), используяReflectionClass::getNamespaceName
; - Создайте свою подпись маршрута из пространства имен, имени класса и имени метода, возможно, используя библиотеку слугификации, такую как https://github.com/cocur/slugify;
- Создать соответствующий маршрут
$app->get($route_signature, "$class_name:$method_name")
,
Это интересная идея, хотя вам нужно быть предельно осторожным, чтобы случайно не раскрыть какие-либо методы, которые вы не хотите, чтобы они были напрямую доступны клиенту. Несколько других заметок:
- Отражение очень медленное, поэтому вы, вероятно, захотите реализовать это как дополнительный этап построения, кэшируя сгенерированные маршруты, а не восстанавливая их на лету с каждым запросом.
- Вам может понадобиться дополнительное соглашение об именах, чтобы различать HTTP-глаголы. Например, начиная все имена методов, которые соответствуют
GET
маршруты сget
, Так что вы могли бы иметь\Foo\BarController::getAdd
,\Foo\BarController::postAdd
, так далее. - Построение параметризованных маршрутов (
/bar/add/{id}
) будет немного больше работы, так как вы, вероятно, захотите извлечь соответствующие аргументы метода, используяReflectionFunctionAbstract::getParameters
, Опять же, вам нужно будет принять решение о том, как маршруты должны быть построены на основе этих параметров.