Запросы форм в Laravel — одна из наиболее недооцененных функций. Возможно, даже «спрятанная» во фреймворке. Можете мне не верить, но даже Taylor Otwell согласен с этим:
Правда, — Taylor Otwell
Давайте я покажу вам, что можно сделать с запросами форм и как их можно использовать их для написания красивых и выразительных API.
Что такое запросы форм (Form Requests)?
Классы Form Requests появились в Laravel 5.0 — то есть они существуют с февраля 2015 года! Если мы посмотрим в документацию этой версии, то увидим следующее определение:
Запросы форм — это кастомные классы запросов, содержащие логику валидации.
Итак, официальная документация говорит о том, что запросы форм нужны для валидации. Давайте сгенерируем запрос формы при помощи команды php artisan make:request
и посмотрим, что у него внутри.
namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ExampleRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return false; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ // ]; } }
Валидация
Мы получаем два метода, которые можно заполнить собственной логикой. Метод authorize
содержат логику, может ли быть выполнен запрос. Здесь можно проверить права доступа/гварды текущего пользователя и вернуть true
или false
. Возврат false
приведет к ответу 403 Forbidden
. И метод rules
, где можно задать свои правила валидации, которые должны применяться к параметрам текущего запроса. Это может быть очень удобно, если вы хотите инкапсулировать логику валидации по отдельным классам.
Чтобы начать использовать класс запроса формы, вам нужно использовать тайпхинт в методе контроллера следующим образом:
class StoreBlogPostController { public function __invoke(BlogPostRequest $request) { Post::create($request->validated()); } }
Выглядит очень просто, но под капотом происходит много крутых вещей. Используя внедрение метода в методе контроллера, Laravel знает, что запрос должен быть авторизован. Поэтому, прежде чем вызывать метод контроллера, Laravel просматривает результат authorize
класса запроса формы. Если он возвращает false
, пользователь получает ответ 403. если же true
— то Laravel проверяет ваши rules
и автоматически проверяет данные запроса на соответствие этому набору правил. Если один или несколько из них не пройдут, то Laravel редиректит пользователя на предыдущую страницу с сообщениями $errors
, которые можно использовать для отображения ошибок валидации. Если все прошло успешно — метод authorize
вернул true
и все rules
оказались валидны, то, наконец, вызывается метод контроллера.
И эта инкапсуляция действительно хороша, ведь вам не нужно беспокоиться о логике вашего контроллера. Как только контроллер будет запущен, вы можете быть уверены, что ваши данные верны и пользователь авторизован.
Ну, раз в документации указано, что основным вариантом использования запросов формы является валидация, то мы уже закончили? Нет — совсем нет! Позвольте мне показать, как еще можно использовать классы запроса формы.
За пределами валидации
Поскольку запросы форм — это просто кастомные классы, то они позволяют добавлять дополнительную логику вашим запросам. Это позволит нам создавать очень выразительные и красивые API для приложений. Я покажу вам, как лично я использую это в своих проектах.
Этот запрос взят из нашего видеокурса и используется, когда срабатывает вебхук от Paddle, который мы используем для продажи курсов:
namespace App\Http\Requests; use App\Packages\CourseRepository; use App\Packages\Package; use Illuminate\Foundation\Http\FormRequest; class PaddlePurchaseRequest extends FormRequest { public function authorize() { return true; } public function rules() { return []; } public function getPackage(): ?Package { return CourseRepository::findPackageForPaddleIdle($this->product_id); } public function isBlackFridayBundle(): bool { return $this->product_id == config('courses.blackfriday.bundle_id'); } public function isVideoCourse(): bool { return ! is_null(CourseRepository::findPackageForPaddleIdle($this->product_id)); } public function isTinkerwell(): bool { return is_null(CourseRepository::findPackageForPaddleIdle($this->product_id)); } }
Ого — вообще нет валидации? Как видите, я не использую запрос формы для «предназначенной» цели — или, по крайней мере, не для цели, упомянутой в документации Laravel. Вместо этого я использую их для добавления дополнительных методов, которые я могу использовать в своем контроллере.
Чтобы понять, что делает этот запрос, позвольте мне быстро объяснить, как работает вебхук Paddle. На моей платформе продажи видеокурсов у меня есть несколько «пакетов», например, пакеты «Basic» и «Pro». С этими пакетами связан идентификатор Paddle-ID, чтобы я могу определить, какой продукт был куплен и отправить приветственное сообщение пользователю, добавить отношение к базе данных и т.д.
Поэтому в класс PaddlePurchaseRequest
я добавил несколько методов, чтобы я мог получить класс купленного пакета. Это позволит мне вызывать $request->getPackage()
в контроллере. Или, в случае приобретенной лицензии Tinkerwell, я могу проверить это с помощью метода $request->isTinkerwell()
.
Мне нравится добавлять методы в классы запросов, выполняющие логику с данными в текущем запросе. Это могут быть данные, которые активно отправлялись, например, в рамках запроса POST, но это также может быть просто доступ к домену, реферер запроса или текущий пользователь, вошедший в систему.
Это имеет смысл, ведь вы абстрагируете логику, относящуюся к «разрешению чего-то из текущего запроса» в эти классы запросов. Вы даже можете пойти еще дальше и поместить общие методы запроса в абстрактные классы запросов, которые вы будете расширять.
Посмотрите, что Laravel Nova делает в своих классах запросов форм. Это из запроса панели инструментов:
namespace Laravel\Nova\Http\Requests; use Laravel\Nova\Nova; class DashboardCardRequest extends NovaRequest { /** * Get all of the possible cards for the request. * * @param string $dashboard * * @return \Illuminate\Support\Collection */ public function availableCards($dashboard) { if ($dashboard === 'main') { return collect(Nova::$defaultDashboardCards) ->unique() ->filter ->authorize($this) ->values(); } return Nova::availableDashboardCardsForDashboard($dashboard, $this); } }
Nova не только использует запросы форм, как я рассказывал, — в классе DashboardCardRequest
есть метод availableCards
, который возвращает все карточки панели инструментов, которые должны быть видны пользователю в текущем запросе. Но Nova также расширяет все свои классы запросов формы из абстрактного класса NovaRequest
, который содержит дополнительную логику, такую как возврат модели и ресурса Nova, к которому обращались в этом запросе.
Куда дальше?
Запросы форм могут быть отличным способом инкапсулировать специфичную логику запросов в свои собственные классы. Не ограничивайте их использование только валидацией или авторизации запросов. Рассматривайте их как классы, которые позволяют вам расширить методы работы с определенным типом запросов.
Я уверен, когда вы в следующий раз будете работать с классами запросов, вы найдете множество способов перемещения логики в запросы форм. Таким образом вы сможете создавать более простые и красивые API в своих приложениях.
Автор: Marcel Pociot
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.