2025-01-07

Сейчас работаю над проектом на yii2 весь backend
которого - REST-API. Соответственно, там много форм и много шаблонного
кода в действиях (Action):
public function run(Request $request): FormName|ResponseName
{
$form = new FormName();
$form->load($request->getBodyParams());
if (!$form->validate()) {
return $form;
}
// ...
return new ResponseName($data);
}
Надоело мне его писать каждый раз, а, кроме того, мне не нравится:
Хочется как в Laravel: если данные не прошли валидацию, то и метод контроллера не запустится. Т.е. чтобы было как-то так:
public function run(FormName $form): ResponseName
{
// ...
return new ResponseName($data);
}
Тут сразу в голову приходят middleware и метод
beforeAction, но первыx в yii2 не завезли а
второй не подходит по причине отсутсвия возможности вернуть ответ из
него пользователю кроме как кинуть исключение.
В голову пришли два варианта решения:
Action, переопределить у него
метод runWithParams и все свои классы уже наследовать от
него;Action на свой.Первый вариант не особо подходит потому как придётся перелапатить
весь проект, второй меня смущает тем что при обновлении фреймворка нужно
будет внимательно посмотреть не поменяли ли там чего в классе
Action, но, пока что, ничего лучше я не придумал (да и в
первом варианте тоже просто так обновляться будет немного тревожно).
Второй путь выглядит так:
yii\base\Action в свою директорию (оставляем у
него исходный namespace) и переписываем метод
runWithParams:<?php
namespace yii\base;
use Yii;
class Action extends Component
{
// ...
public function runWithParams($params)
{
if (!method_exists($this, 'run')) {
throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
}
$args = $this->controller->bindActionParams($this, $params);
Yii::debug('Running action: ' . get_class($this) . '::run(), invoked by ' . get_class($this->controller), __METHOD__);
if (Yii::$app->requestedParams === null) {
Yii::$app->requestedParams = $args;
}
if ($this->beforeRun()) {
$result = null;
$request = Yii::$app->getRequest();
foreach ($args as $value) {
if ($value instanceof Model) {
$value->load(match ($request->getMethod()) {
'POST' => $request->getBodyParams(),
default => $request->getQueryParams()
});
if (!$value->validate()) {
$result = $value;
break;
}
}
}
$result = is_null($result)
? call_user_func_array([$this, 'run'], $args)
: $result;
$this->afterRun();
return $result;
}
return null;
}
// ...
}
// bootstrap.php
// ...
Yii::$classMap['yii\base\Action'] = __DIR__ . '/../component/Action.php';
components,
класс нашей формы (чтобы контейнер приложения смог создать объект формы,
иначе он скажет что не знает таких):<?php
return [
'components' => [
// ...
FormName::class => FormName::class,
],
]
На этом всё: если пришёл запрос, и форма не прошла валидацию, пользователь увидит ответ с ошибками, действие в контроллере не запустится, а мы не будем писать шаблонный код создания наполнения и валидации формы каждый раз.
Happy end!
Комментарии
Комментарии на GitHub