Laravel: Поговорим о запросах формы / Form Request

Источник: «Let's talk about Form Requests»
Запросы формы наиболее известны тем, что переносят логику проверки из контроллеров в класс, который будет выполнять предварительную проверку. Они фантастические, и я постоянно опираюсь на них. Что ещё мы можем сделать с запросами форм?

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

Подготовка ввода для проверки

Этот метод фантастический, когда нужно преобразовать или добавить больше в запрос до запуска валидатора. Документация показывает пример слияния slug с чем-то полезным, но как насчёт другого примера?

protected function prepareForValidation(): void
{
$this->merge([
'locale' => $this->user()->locale,
]);
}

Мы объединяем локаль аутентифицированных пользователей с запросом, поэтому мы можем выполнять динамическую проверку на основе локали пользователя. Допустим, прямо сейчас в нашем приложении действуют ограничения, это означает, что пользователи из Уэльса (где я живу) должны добавлять данные в определённом формате. Случайно, но я увидел несколько вариантов как разработчик, где это могло быть полезным.

В этом методе мы могли бы даже динамически заменять содержимое.

protected function prepareInputForValidation(): void
{
$replace = match ($this->user()->locale) {
'en_GB' => 17.5,
'en_US' => 10.0,
'de_DE' => 12.5,
};

$this->replace([
'tax_percentage' => $replace,
]);
}

В этом примере мы динамически меняем процент налога в зависимости от региона пользователя. Это полезно, если у вас есть определённые налоговые ставки как часть налога на импорт в качестве дистрибьютора.

Пройденная проверка

Как и при подготовке к проверке, мы можем использовать данные, полученные после проверки, стандартизировать или отформатировать данные запроса в определённом формате. Для человека, ежедневно работающего с данными, это очень полезно.

protected function passedValidation(): void
{
$this->replace([
'name' => Str::uppercase($this->name),
]);
}

Опять, это не самые полезные примеры — но при желании вы могли бы многое сделать, от превращения свойств в Объекты Значения для работы с деньгами до проверки контента на наличие спама.

Неудачная авторизация

Обычно, когда ваш запрос формы не проходит авторизацию, фреймворк генерирует исключение AuthorizationException. В 99% случаев это всё, что вам нужно. Однако есть ещё несколько неясных случаев, когда вы хотите избежать выбрасывание исключения, подобного этому. Допустим вы перехватываете веб-хуки из стороннего API, и выбрасывание AuthorizationException приведёт к странному, неконтролируемому поведению в этой третьей стороне. Вместо этого вы можете спокойно потерпеть неудача, переопределив этот метод в своём запросе формы.

protected function failedAuthorization(): void
{
Log::info('Failed authorization for webhook capture ....');
}

Это всего лишь несколько методов, доступных в запросе формы, не говоря уж обо всех тех, которые доступны с использованием Laravel Precognition. Но, как я упоминал ранее, мы можем добавить наши методы, что позволит ещё немного расширить функциональность наших запросов форм. Прекрасным примером является исходный код Laravel Breeze, где в LoginRequest добавлен метод authenticate, чтобы можно было ещё больше упростить код контроллера.

Давайте посмотрим на пример контроллера, который я обычно могу написать:

final class StoreController
{
public function __construct(
private readonly StoreCommand $command,
) {}

public function __invoke(StoreRequest $request): Responsable
{
try {
$this->command->handle(
instance: new Instance(
name: $request->string('name')->toString(),
),
);
} catch (Throwable $exception) {
throw new FailedToCreateInstance(
message: 'Failed to create a new instance.',
previous: $exception,
);
}

return new CreatedResponse();
}
}

Это упрощённый контроллер, в котором я использую креативный класс для отправки через Объект Данных, чтобы код был последовательным и чистым, как мне нравиться. Мы можем упростить этот код, опираясь на запрос формы.

final class StoreController
{
public function __construct(
private readonly StoreCommand $command,
) {}

public function __invoke(StoreRequest $request): Responsable
{
try {
$this->command->handle(
instance: $request->dto(),
);
} catch (Throwable $exception) {
throw new FailedToCreateInstance(
message: 'Failed to create a new instance.',
previous: $exception,
);
}

return new CreatedResponse();
}
}

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

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

Дополнительные материалы

Предыдущая Статья

Laravel: Хелперы массива которые нужно знать

Следующая Статья

Laravel: Всё, что вы можете протестировать в приложении