2016-01-20 5 views
2

Я изучаю использование Lumen или, возможно, Slim для проекта, и задавался вопросом, можно ли автозагружать контроллеры на основе структуры каталогов, а не регистрировать все маршруты.Как реализовать поведение маршрутизации по умолчанию с помощью структуры 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()

Зарегистрированные маршруты должны иметь приоритет перед автозагрузка s.

Я нашел способ сделать это на основе метода маршрутизации 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/[email protected]'); 
$app->get('/home', 'common/[email protected]'); 

Правильно ли я думать, что это сделало бы приложение работало быстрее, так как не нужно будет искать все зарегистрированные маршруты для матча?

Есть ли лучший способ выполнить этот автоматический процесс маршрутизации?

+1

То, что вы пытаетесь сделать, не позволит вашему приложению работать быстрее или медленнее, по какой-то причине люди сосредотачиваются на неправильных вещах, чтобы оптимизировать. Однако ваша идея полностью разрушает цель маршрутизации. Вы ** хотите ** определить маршрут, и вы ** хотите ** явно указать, что будет с ним обращаться. Если вы пытаетесь «автоматизировать» обработчики маршрутов, вы реализуете скрытую магию. В конечном итоге маршрут будет согласован с обработчиком (контроллером/функцией). Это просто бесконечно лучше, чтобы четко указать, что его обрабатывает. Вы ничего не получаете от этого типа автоматизации, кроме проблем. – Mjh

ответ

1

Я думаю, вы можете сделать эту работу с комбинацией reflection и поддержкой Slim 3 для defining routes with controller methods instead of closures.

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

  1. Поиск через каждый из ваших классов контроллера (с использованием glob или автозагрузчика);
  2. Для каждого класса позвоните ReflectionClass::getMethods, используя фильтр ReflectionMethod::IS_PUBLIC, чтобы вы получали только общедоступные методы для класса;
  3. Получить имя класса используя ReflectionClass::getName и пространство имен (при необходимости) с использованием ReflectionClass::getNamespaceName;
  4. Создайте свою подпись маршрута из пространства имен, имени класса и имени метода, возможно, используя библиотеку slugification, такую ​​как 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. Опять же, вам нужно будет принять решение относительно какой-либо конвенции относительно того, как маршруты должны быть построены на основе этих параметров.