Laravel: Правило валидации с периодом дат и несколькими полями

Источник: «Laravel Custom Validation Rule with Date Period and Multiple Fields»
Недавно я прочитал сообщение на форуме Laracasts с вопросом о валидации периода даты. Решил написать эту статью с возможным решением.

Ситуация, описанная автором:

Я хочу добавить проверку дат. Я генерирую зарплату для рабочих, но если зарплата уже создана, и администратор пытается создать зарплату между уже созданными датами, я хочу показать ему сообщение Пожалуйста, выберите другие даты или что-то подобное. Предположим, что зарплата с 10.01.2022 по 15.01.2022 уже создана, тогда я не хочу создавать другую зарплату между этими датами.

Другими словами, как проверить пару полей с валидацией их определённым правилом в базе данных, что-то вроде select * from salaries where user_id = ? and date_from < ? and date_to > ?

Допустим у нас есть форма или конечная точка API, которая принимает запросе эти параметры:

Если мы преобразуем это в язык Laravel, мы получим следующее:

// app/Http/Controllers/SalaryController.php:
public function store(StoreSalaryRequest $request)
{
Salary::create($request->validated());

return redirect()->route('salaries.index');
}

// app/Http/Requests/StoreSalaryRequest.php:
class StoreSalaryRequest extends FormRequest
{
public function rules()
{
return [
// ... БОЛЬШОЙ ВОПРОС: ЧТО МЫ ЗДЕСЬ НАПИШЕМ?
];
}
}

Проблема в том, что у нас нет такого правила валидации из коробки в списке правил валидации в Laravel по умолчанию. Поэтому нам нужно написать собственное правило валидации.

Я приведу пример с новым вызываемым синтаксисом пользовательских правил валидации. Для более старого способа (который ещё работает) посмотрите документацию по laravel 8.

Сначала, создадим правило:

php artisan make:rule UniqueSalaryRule --invokable

Затем заполняем метод __invoke() вызовом базы данных, чтобы проверить, существует ли зарплата для user_id и указанный период времени.

use App\Models\Salary;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\InvokableRule;

class UniqueSalaryRule implements DataAwareRule, InvokableRule
{
protected $data = [];

public function __invoke($attribute, $value, $fail)
{
if (Salary::where('user_id', $this->data['user_id'])
->where('date_from', '<=', $value)
->Where('date_to', '>=', $this->data['date_to'])
->exists()) {
$fail('Salary for this period already exists');
}
}

public function setData($data)
{
$this->data = $data;

return $this;
}
}

Чтобы получить доступ к другим атрибутам $request, нужно реализовать интерфейс DataAwareRule и иметь метод setData. Прочитайте раздел документации под названием Доступ к дополнительным данным в официальной документации.

Наконец, мы активируем это правило валидации в нашем классе FormRequest:

class StoreSalaryRequest extends FormRequest
{
public function rules()
{
return [
'user_id' => 'required',
'date_from' => ['required', new UniqueSalaryRule()],
'date_to' => 'required',
'amount' => 'required',
];
}
}

Вот и всё: если комбинация date_from и date_to не валидна, она вернёт ошибку валидации поля date_from.

Это всего лишь один из способов сделать это: вы можете создать собственное правило проверки с другим синтаксисом или даже проверять данные непосредственно в Контроллере вместо Запроса Формы и правил валидации. Выбор остаётся за вами!

Дополнительные материалы

Предыдущая Статья

PHP: Разница между Трейтом, Интерфейсом и Абстрактным классом

Следующая Статья

Laravel 9: Ваше первое приложение