Документация/События

События

Во фреймворке HLEB2 существует несколько предопределённых общих событий, назначенных к конкретному типу действия каждое.
Все классы событий расположены в папке /app/Bootstrap/Events/ и доступны к внесению изменений. Технически они заменяют собой конфигурацию, избавляя проект от лишней "магии".

Учитывая, что перечисленные классы привязаны к глобальным событиям, код в них, зависящий от частных реализаций, рекомендовано выносить в отдельные классы.

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


#ControllerEvent

Метод before() этого класса выполняется перед каждым вызовом контроллера из фреймворка. Через аргументы можно определить, что за класс и метод задействованы и, если требуется, внести изменения в аргументы в виде именованного массива, вернув их в метод вызываемого контроллера.
Например, если используется валидация входящего Request сторонней библиотекой, эту проверку можно реализовать через событие ControllerEvent.

Метод after(), при его наличии, позволяет переопределить ответ контроллера и выполняется сразу после контроллера. Метод принимает этот результат в аргументе 'result' по ссылке, в процессе можно изменить возвращаемые данные для конкретного класса и метода контроллера.
Глобально это может быть преобразование возвращаемого массива не в JSON как установлено по умолчанию, а в другой формат, например, в XML.

На следующем примере показано прикрепление дополнительного действия перед вызовом определенного класса и метода контроллера:

<?php

declare(strict_types=1);

namespace 
App\Bootstrap\Events;

use 
Hleb\Base\Event;

final class 
ControllerEvent extends Event
{
    public function 
before(string $classstring $method, array $arguments): array|false
    
{
        switch([
$class$method]) {
            case [
ExampleController::class, 'index']:
                return (new 
ExampleControllerEvent())->beforeIndex($arguments);
                
// ... //
            
default:
        }
        return 
$arguments;
    }

    public function 
after(string $classstring $methodmixed &$result): void
    
{
        
// ... //
    
}
}

#MiddlewareEvent

Метод before() этого класса-посредника выполняется перед каждым вызовом middleware из фреймворка. Аргументы метода дают возможность выяснить, что за класс и метод задействованы, а также является ли этот middleware выполняющимся после основного действия.
При необходимости, есть возможности внести изменения в аргументы целевого метода middleware, изменив их и вернув из текущего метода. В таком случае необходимо определить условие, что после вывода результата нужно прекратить выполнение скрипта, для этого из метода after() достаточно вернуть false.

Очередность выполнения middlewares может меняться в маршрутах, это необходимо учесть при назначении событий к ним, при необходимости заменив зависящие от порядка выполнения элементы События на соответствующие отдельные middlewares.


#ModuleEvent

Так как модули существуют обособленно, то контроллеры модуля получили собственное Событие.
Метод before() класса ModuleEvent выполняется перед каждым вызовом контроллера любого модуля из фреймворка.
В отличие от ControllerEvent здесь присутствует дополнительный аргумент $module для определения названия модуля.
Аналогично событию контроллера, это Событие тоже может иметь метод after().


#PageEvent

Это ещё одно событие по аналогии с ControllerEvent, оно привязано к вызовам специальных 'контроллеров для страниц'.
Такие страницы используются в библиотеке регистрации фреймворка для административной панели и еще на этом сайте с документацией.


#TaskEvent

Выполняется перед запуском каждой команды фреймворка, кроме встроенных в него по умолчанию. Также позволяет определить вызываемый класс и источник вызова (из кода или из консоли). TaskEvent принимает и возвращает итоговые данные для аргументов конечного метода, вследствие чего здесь также можно подключить стороннюю библиотеку. Например, это может быть стандартный консольный обработчик из Symfony.

Метод after() для этого события отличается тем, что имеет доступ к данным, установленные в задаче как setResult(). Эти данные передаются по ссылке аргументу 'result' и могут быть изменены.
По необходимости, можно таким же образом изменить возвращаемый статус ответа, применив метод statusCode().

Демонстрационный пример, показывающий один из способов организовать реагирование (с одним общим интерфейсом) на выполнение различныx задач:

<?php

namespace App\Bootstrap\Events;

use 
Hleb\Base\Event;

final class 
TaskEvent extends Event
{
    private ?
TaskEventInterface $action null;

    public function 
before(string $classstring $method, array $args): array
    {
        switch (
$class) {
            case 
FirstTask::class:
                
$this->action = new FirstTaskEvent($method);
                break;
            case 
SecondTask::class:
                
$this->action = new SecondTaskEvent($method);
                break;
            
// ... //
            
default:
        }
        return 
$this->action $this->action->getBeforeAction($args) :  $args;
    }

    public function 
after(string $classstring $methodmixed &$result): void
    
{
        
$this->action and $result $this->action->updateAfterAction($result);
    }

    public function 
statusCode(string $classstring $methodint $code): int
    
{
        return 
$this->action $this->action->getCode($code) : $code;
    }
}

Такой принцип может быть применён не только к событиям для задач, но и к другим Событиям.

Оператор switch для События выбран из-за его способности сопоставлять один результат к нескольким блокам case.


#Расширенные условия

Сопутствующие действия могут быть назначены и по иным условиям, например по общей группе в namespace:

if (str_starts_with($class'App\\Controllers\\Api\\')) {
    
// ... //
}

Кроме того, классы событий унаследованы от Hleb\Base\Container, что даёт возможность использовать в них сервисы из контейнера. Также в конструкторе классов событий можно получить эти сервисы через Dependency Injection.
Возможности использования их не ограничены, конечно в рамках правил написания читабельного и оптимизированного кода. Вот так можно назначить условие по методу HTTP-запроса для конкретного класса и метода:

if ([$class$method] === [MainController::class, 'index'] && $this->request()->isMethod('GET')) {
    
// ... //
}
Консольные команды Введение

Страница создана: @fomiash
К началу страницы