Middle: питання на співбесіді з теми «Валідація»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
FormRequest - окремий клас, що містить правила валідації та авторизацію, виносячи їх із контролера.
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return ['title' => ['required', 'max:255']];
}
}
// $request - вже провалідовано
public function store(StorePostRequest $request)
{
Post::create($request->validated());
}
Переваги: тонкі контролери, перевикористання правил, метод prepareForValidation() для нормалізації вводу, власні повідомлення в messages().
Згенерувати клас, що реалізує ValidationRule:
php artisan make:rule Uppercase
class Uppercase implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (strtoupper($value) !== $value) {
$fail('Поле :attribute має бути у верхньому регістрі.');
}
}
}
$request->validate(['code' => [new Uppercase]]);
Для разових перевірок можна передати замикання прямо в правило: 'field' => [fn ($attr, $value, $fail) => ...].
Через «крапкову» нотацію й зірочку, яка означає кожен елемент масиву.
$request->validate([
'items' => ['required', 'array', 'min:1', 'max:50'],
'items.*.product_id' => ['required', 'integer', 'distinct', 'exists:products,id'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
'tags' => ['array'],
'tags.*' => ['string', 'max:30'],
]);
На що звернути увагу:
- Перевіряйте сам масив (
array,max), а не лише елементи. Без обмеження клієнт надішле 10 000 елементів, і для кожного виконаються запитиexists. distinctзабороняє повтори - той самий товар двічі в одному замовленні.existsна кожен елемент - окремий запит до бази. Для великих масивів краще перевірити всі ID одним запитом уafter().- Повідомлення можуть містити позицію елемента:
:index(з нуля) чи:position(з одиниці) - «Товар #3: кількість має бути не менше 1».
validated() поверне лише ті ключі елементів, для яких є правила, тож зайві поля всередині елементів масиву теж відкинуться.
Є кілька рівнів - від рядкових правил до логіки в коді.
1. Готові умовні правила:
'company_name' => ['required_if:type,business'],
'vat_number' => ['required_with:company_name'],
'doctor_name' => ['exclude_if:has_appointment,false', 'required'],
'discount' => ['prohibited_unless:role,manager'],
exclude_if прибирає поле і з перевірки, і з validated() - зручно, коли воно не має сенсу за іншої відповіді.
2. Умова кодом:
'role_id' => Rule::requiredIf(fn () => $this->user()->isAdmin()),
'coupon' => Rule::when($this->boolean('has_coupon'), ['required', 'string']),
3. Перевірки на кілька полів одразу - метод after() запиту форми:
public function after(): array
{
return [function (Validator $validator) {
if ($this->date('ends_at') <= $this->date('starts_at')) {
$validator->errors()->add('ends_at', 'Кінець має бути пізніше за початок.');
}
}];
}
Для такого простого випадку вистачить і правила after:starts_at; after() потрібен, коли логіка складніша.