Автоназначение маршрутов контроллер-метод во фреймворке HLEB.

24 Ноября 2022 (ред)

(!) Примеры и описание для более новой версии фреймворка HLEB2 могут отличаться от приведенных в тексте.

В некоторых случаях, особенно когда проект большой, а к URL-адресам не предъявляется особых требований, можно настроить соответствие для маршрутов, при котором маршрутизатор автоматически будет находить нужный контроллер и в нём метод. Для расширения доступных вариантов семантического URL, попросту добавления новой страницы, достаточно создать класс контроллера в папке. Такой вариант есть, к примеру, во фреймворке Yii.

При задании в роутинге одного правила для раздела 'api' (таких разделов может быть несколько), нужно определить его в файле маршрутизации один раз.

Route::prefix('/api/')->getGroup();
        Route::get('{controller}/{method}/')->module('api', '<controller>Controller@action<method>');
Route::endGroup();

Теперь все запросы с начальным префиксом /api/ направлены в папку /modules/api/. Значения из URL подставляются в шаблон и, если он найден, вызывается нужный метод контроллера. Если создать следующую иерархию папок и файлов в modules:

Сами файлы:

<?php
// Файл /modules/api/BoxController.php
namespace Modules\Api;
class BoxController extends \MainController
{
    public function actionGet() {
        return view('box/default', ['action' => 'get']);
    }
    public function actionUpdate() {
        return view('box/default', ['action' => 'update']);
    }
}
<?php
// Файл /modules/api/SphereController.php
namespace Modules\Api;
class SphereController extends \MainController
{
    public function actionGet() {
        return view('sphere/default', ['action' => 'get']);
    }
    public function actionUpdate() {
        return view('sphere/default', ['action' => 'update']);
    }
}

Контент в обоих файлах default.php одинаков:

<?php
/** @var string $action */
echo json_encode(['action' => $action]);

Теперь по следующим путям в URL (запрос - ответ) видно соответствие:

  • /api/sphere/get - {"action":"get"}
  • /api/sphere/update - {"action":"update"}
  • /api/box/get - {"action":"get"}
  • /api/box/update - {"action":"update"}

По образу и подобию Yii:

Единственное структурное отличие здесь может быть в том, что контроллеры находятся в корне целевой папки, в данном случае api, backend или frontend, а не в папке controllers, как во фреймворке Yii.

fomiash fomiash + 221
Опубликовано в PHP фреймворк HLEB

3 Ответа

  1. Evg Evg 25 Ноября 2022

    Вот. Это надо еще раз посмотреть и взять на вооружение. Спасибо.

  1. fomiash fomiash 25 Ноября 2022 (ред.)

    Чтобы каждый раз не писать в view(...) префикс папки, а соотнести контроллер с папкой, можно создать класс MainModuleController в таком виде:

    <?php
    // Файл /modules/MainModuleController.php
    namespace Modules;
    class MainModuleController extends \MainController
    {
        protected function view($to, $data = null): array
        {
            $type = \Request::get('type');
            $controller = \Request::get('controller');
            return view("$type/views/$controller/"  . ltrim($to, '\\/ '), $data);
        }
    }

    и все контроллеры в модулях наследовать от этого класса. Это позволит размещать шаблоны в папке modules/api/views/<название контроллера>/ для api (у остальных аналогично), а в контроллерах вызывать через $this->view(...) по названию шаблона в этой папке.

    P.S. Такой вид маршрутизации через поиск контроллер-метод требует контроля над содержимым папок модулей, так как если скопировать старую версию контроллера как архивную или оставить в папке не до конца доделанный файл, пользователь сможет его вызвать через роутинг. Надо понимать, что все файлы, подчиняющиеся правилам такого динамического маршрута, составляют доступные URL.

  1. fomiash fomiash 28 Ноября 2022

    Ограничить доступность HTTP-методов тоже не помешает, поэтому измененный базовый контроллер для модулей может быть таким:

    <?php
    // Файл /modules/MainModuleController.php
    namespace Modules;
    
    use Hleb\Constructor\Handlers\Request;
    
    class MainModuleController extends \MainController
    {
        public function __construct(array $data = null)
        {
            parent::__construct($data);
            if (method_exists(static::class, 'verbs')) {
                $action = Request::get('method');
                $verbs = $this->verbs();
                if (count($verbs) && (empty($verbs[$action]) || !in_array(Request::getMethod(), $verbs[$action]))) {
                    http_response_code(404);
                    echo $this->error404();
                    exit();
                }
            }
        }
    
        /** Проверка допустимых HTTP-методов запроса для методов в контроллерах. */
        protected function verbs(): array
        {
            return [];
        }
    
        /** Возвращение стандартной ошибки метода verbs(). */
        protected function error404()
        {
            return null;
        }
    
        /** Переопределение стандартной функции view() */
        protected function view($to, $data = null): array
        {
            $type = \Request::get('type');
            $controller = \Request::get('controller');
            return view("$type/views/$controller/"  . ltrim($to, '\\/ '), $data);
        }
    }
    
    
    

    Теперь можно в контроллерах задавать допустимые методы и возвращаемую ошибку.

    <?php
    // Файл /modules/api/IndexController.php
    namespace Modules\Api;
    
    use Modules\MainModuleController;
    
    class IndexController extends MainModuleController
    {
        protected function verbs(): array
        {
            return [
                'get' => ['GET', 'OPTIONS'],
                'update' => ['POST', 'OPTIONS'],
            ];
        }
    
        protected function error404(): string
        {
            return "404 Not Found";
        }   
    
        public function actionGet()
        {
            // Получение 
        }
    
        public function actionUpdate()
        {            
            // Изменение
        }
    }
    
    


Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.