Как реализовать поведение маршрутизации по умолчанию с 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 для определения маршрутов с помощью методов контроллера вместо замыканий.

Основная стратегия будет выглядеть следующим образом:

  1. Поиск по каждому из ваших классов контроллеров (используя glob или автозагрузчик);
  2. Для каждого класса звоните ReflectionClass::getMethods, с использованием ReflectionMethod::IS_PUBLIC фильтр, чтобы вы получили только открытые методы для класса;
  3. Получить имя класса, используя ReflectionClass::getName и пространство имен (при необходимости), используя ReflectionClass::getNamespaceName;
  4. Создайте свою подпись маршрута из пространства имен, имени класса и имени метода, возможно, используя библиотеку слугификации, такую ​​как https://github.com/cocur/slugify;
  5. Создать соответствующий маршрут $app->get($route_signature, "$class_name:$method_name"),

Это интересная идея, хотя вам нужно быть предельно осторожным, чтобы случайно не раскрыть какие-либо методы, которые вы не хотите, чтобы они были напрямую доступны клиенту. Несколько других заметок:

  • Отражение очень медленное, поэтому вы, вероятно, захотите реализовать это как дополнительный этап построения, кэшируя сгенерированные маршруты, а не восстанавливая их на лету с каждым запросом.
  • Вам может понадобиться дополнительное соглашение об именах, чтобы различать HTTP-глаголы. Например, начиная все имена методов, которые соответствуют GET маршруты с get, Так что вы могли бы иметь \Foo\BarController::getAdd, \Foo\BarController::postAdd, так далее.
  • Построение параметризованных маршрутов (/bar/add/{id}) будет немного больше работы, так как вы, вероятно, захотите извлечь соответствующие аргументы метода, используя ReflectionFunctionAbstract::getParameters, Опять же, вам нужно будет принять решение о том, как маршруты должны быть построены на основе этих параметров.
Другие вопросы по тегам