В большинстве проектов валидация моделей может осуществляться с использованием FormRequest объектов. И это правильно. Либо проверка может быть реализована в самом контроллере. Что, очевидно, часто не несет ничего хорошего.
Для некоторых задач, создание отдельного объекта валидации может быть избыточным, а написание валидации в контроллере не будет позволять использовать код повторно (DRY).
Как мы могли бы сделать
Для таких целей можно один раз описать правила валидации в самой модели, и затем, используя события, сделать валидацию.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Validator;
class Post extends Model
{
public function getValidationRules(): array
{
return [
'title' => 'required|string|max:255'
];
}
public static function boot()
{
parent::boot();
static::creating(
function (Post $post) {
Validator::validate($post->toArray(), $post->getValidationRules());
}
);
}
}
В методе getValidationRules мы можем описывать все правила валидации. Так же в ней можем описывать условия, например, поле slug нужно проверять на уникальность только при создании поста.
При таком подходе есть один нюанс. Это использование метода toArray, т.к. валидатор не умеет работать с объектами. Но этот метод так же не возращает скрытые поля, такие как password_hash, например. Для этого, нам необходимо лишь сделать эти поля видимыми
protected $hidden = ['password_hash'];
static::creating(function (Post $post) {
$post->makeVisible(['password_hash']);
Validator::validate($post->toArray(), $post->getValidationRules());
});
Какие в этом подходе могут быть плюсы?
Инкапсуляция знаний
Самое главное то, что наш HTTP уровень (контроллеры) знает как можно меньше о нашей базе данных. В Laravel это означает, что ваш класс становится полностью автономным, полным знаний о БД, а контроллеры лишь вызывают методы, не зная даже, какие столбцы присутствуют и какие типы данных они представляют.
С таким подходом вам не нужно больше нигде выполнять валидацию или беспокоиться о том, что вы где то забыли это сделать. Ваша модель сама, автоматически делает это и инкапсулирует все знания в одном месте.
Повторное использование
Даже если вы не хотите использовать автоматическую валидацию данных, то вы все равно можете поместить правила валидации в модель и использовать их в других местах. Например, если вы пользуетесь интерфейсами, то ваш контроллер все так же ничего не знает о правилах валидации. Он лишь знает, что нужно дернуть определенный метод, чтобы получить правила валидации и выполнить ее.
Самое главное в том, что вам не придется больше бегать по всем контроллерам/методам и все их менять, если вдруг появилось новое поле или добавилась новая проверка поля. Теперь все в одном месте.
Минусы
Циклы перенаправления
Когда проверка проходит неудачно, метод Validator::validate()
бросает исключение ValidationException
. Фреймворк пытается поймать его и перенаправить обратно в предыдущие место вызова.
Если по какой-то причине, предыдущее место вызова было то же , что и вызвавшее ошибку, то мы застрянем в цикле перенаправления.
Такой паттерн автоматической проверки можно использовать только на отдельных маршрутах GET /posts и POST /posts
Побочные эффекты
Как упоминалось выше, Laravel перехватывает ValidationException
и пытается перенаправить нас обратно с набором ошибок. Это означает, что то, что происходит на уровне БД может оказывать некоторое влияние на уровень HTTP нашего приложения.
Данный момент может для многих показаться недопустимым. Но в некоторых задачах, такой подход может оказаться удачным.
Дно ящика
Такой подход сдвигает всю нашу проверку на “дно ящика”. Я имею ввиду, что сама проверка оказывается скрыта и в случае чего, вам придется немного поломать голову, чтобы понять, где она выполняется.
Итог
Как видите, такой подход имеет место быть, но не лишен множества существенных недостатков. Могут существовать задачи, где эти недостатки будут не существенны. Но я все же выступаю за то, чтобы использовать FormRequest
объекты.