Урок 05

Валидация в API

Отличия от web

В API валидация возвращает JSON с кодом 422, а не редирект. Формат:

{
    "message": "The title field is required. (and 2 more errors)",
    "errors": {
        "title": ["The title field is required."],
        "body": ["The body field is required."]
    }
}

Form Request

Для API тоже используют Form Request:

php artisan make:request StorePostRequest
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body' => 'required|string|min:10',
            'tags' => 'array',
            'tags.*' => 'integer|exists:tags,id',
        ];
    }
}

Использование

public function store(StorePostRequest $request)
{
    $post = Post::create($request->validated());

    return new PostResource($post);
}

Разные правила для store и update

public function rules(): array
{
    $postId = $this->route('post')?->id;

    return [
        'title' => 'required|string|max:255|unique:posts,title,' . $postId,
        'body' => 'required|string|min:10',
    ];
}

Сообщения

public function messages(): array
{
    return [
        'title.required' => 'Заголовок обязателен',
        'body.min' => 'Текст слишком короткий',
    ];
}

Кастомный ответ на ошибку

protected function failedValidation(Validator $validator): void
{
    throw new HttpResponseException(
        response()->json([
            'message' => 'Ошибка валидации',
            'errors' => $validator->errors(),
        ], 422)
    );
}

Условные правила

public function rules(): array
{
    return [
        'title' => 'required|string',
        'published_at' => 'nullable|date',
        'body' => Rule::requiredIf(fn () => $this->published_at !== null),
    ];
}