Перейти к основному содержимому

Обработка запросов

Теперь рассмотрим обработку запросов и отрисовку контента модулями.

Во Flute нет ничего обычного. Существуют route и Controller. Сами роуты инициализируются либо в файлике routes.php, либо напрямую вызывая метод router().

Роуты

Рассмотрим как выглядит базовая структура роутов:

примечание

Мы рассмотрим роуты в контексте модуля (не в админ-панели), поэтому важное замечане.

Каждый роут должен быть уникален. Это означает, что в модуле shop не может быть роута с путем /, может быть только shop/.

<?php

$router->group(function ($router) {
$router->middleware(HasPermissionMiddleware::class);

$router->group(function (RouteGroup $admin) {
$admin->get('/', [IndexView::class, 'index']);
$admin->get('/settings', [MainSettingsView::class, 'index']);

$admin->group(function (RouteGroup $adminModule) {
$adminModule->get('/list', [ModulesView::class, 'list']);
}, '/modules');
});
});

Что можно делать с роутами:

  • Группировать
  • Вешать на них Middleware
  • Создавать роуты только на определенный метод (GET / POST / PUT / DELETE)
  • Создавать роуты, дающие только интерфейс view()
  • И многое другое...

В аргумент каждого метода должны передаваться:

  • Путь до контроллера
  • Функция

Пример роута с методом GET на путь /somepath:

router()->get('/somepath', [
SomeController::class, // Путь до нашего контроллера
'index' // Метод, который будет вызываться в этом контроллере на этом пути
])

Не вижу смысла расписывать каждую функцию. Вы можете посмотреть какие методы существуют в роутах в файлике app\Core\Router\RouteGroup.php.

Контроллеры

Контроллеры - это классы, которые будут обрабатывать конкретные маршруты (роуты). Каждый контроллер должен наследовать класс AbstractController и обязан содержать в себе необходимые методы, указанные в маршрутизаторе.

Пример реализации контроллера отдающего интерфейс:

<?php

use Flute\Core\Support\AbstractController;

class HomeController extends AbstractController
{
public function index()
{
// Отдаем наш интерфейс в виде Response
return view('Modules/Monitoring/Resources/Views/servers');
}
}

Безусловно, контроллеры могут и обрабатывать API запросы. Вот пример реализации API метода:

<?php

use Flute\Core\Support\AbstractController;

class ApiController extends AbstractController
{
public function edit( FluteRequest $request, $id )
{
$entity = rep(Entity::class)->findByPK((int) $id);

$entity->name = $request->name;

transaction($entity)->run();

return $this->success();
}
}

Я так понял Вы видите немножко нестандартную схему.

Стоит сперва уточнить, что каждый аргумент внутри метода контроллера дополняется DI (Dependency Injection), что позволяет импортировать кастомные классы или сервисы прямо внутри метода.

$id - Это {id} определенный внутри роута, который автоматически передается в контроллер по названию.

примечание

Вот так бы выглядел роут:

router()->put('/some/edit/{id}', [ApiController::class, 'edit']);

В этом примере видно, что мы изменяем имя какой-то сущности, а далее возвращаем success().

Да, в AbstractController существуют множество методов, позволяющих облегчить создание контроллеров. Там есть и error(), json() и др.

Вы можете открыть этот файл и посмотреть вручную, там все задокументировано.

Интерфейс

Принцип реализации интерфейсов описан тут

Советы

Пожалуй дам несколько советов, которых я рекомендую придерживаться при создании маршрутов и контроллеров.

1. Всегда разделяйте логику от вида

Важно создавать контроллеры, которые имеют в себе только те методы, которые определены в названии класса.

К примеру: У нас есть функционал отрисовки и добавления пункта. Для реализации этой задачи, нам нужны два роута:

  • GET - /list/
  • POST - /list/add

Вот так бы выглядели роуты:

router()->group(function(RouteGroup $router) {
$router->get('/', [ViewController::class, 'index']);

$router->post('/add', [ApiController::class, 'add']);
}, '/list');

Как Вы видите, я выделил 2 отдельных класса:

  • ViewController
  • ApiController

Каждый из которых, отвечает за конкретную обязанность.

  • ViewController - отвечает лишь за отрисовку контента на странице
  • ApiController - отвечает только за обработку запросов

2. Разделяйте контроллеры

Я Вам настоятельно рекомендую разделять Ваш контроллер на сервисы (Service) при увеличении логики.

Почему? - Все просто

Если в контроллере будет большое количество логики, то этот контроллер будет просто невозможно поддерживать. Ваш код превратится в какой-то монолит, от которого Вас будет мутить.

Поэтому создаем произвольные сервисы и вызываем их внутри контроллера

ApiController.php
public function someAction(FluteRequest $request, UserService $user)
{
// Мы отлавливаем исключение от сервиса.
// Это необязательно, просто как демонстрация
try {
$user->someFunc($request->input());

return $this->success();
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}

И сам сервис:

UserService.php
class UserService
{
public function someFunc(array $args)
{
if( !$args ) throw new \Exception('Args is empty');

/** Тут может быть что угодно */

return true;
}
}

3. Не брезгуйте Middleware

Middleware - это можно сказать некий Firewall служащий для облегчения проверок внутри контроллеров.

Сами Middleware выглядят подобным образом:

<?php

use Flute\Core\Support\AbstractMiddleware;
use Flute\Core\Support\FluteRequest;

class CSRFMiddleware extends AbstractMiddleware
{
public function __invoke(FluteRequest $request, \Closure $next)
{
// Пример проверки на CSRF
if( !template()->getBlade()->csrfIsValid() )
return $this->error(__('def.csrf_expired'));

return $next($request);
}
}

Вы можете выделить в Middleware проверку на авторизацию, существование товара, существование пользователя и т.п.

Сами Middleware вы можете определить в группе роутов:

router()->group(function ($routeGroup) {
$routeGroup->middleware(CSRFMiddleware::class);
});

Либо прямо в контроллере:

ApiController.php
class ApiController
{
public function __construct()
{
$this->middleware(CSRFMiddleware::class);
}
}

В общем полезная штука. Обязательно используйте в своих проектах