Laravel: Правило валидации с периодом дат и несколькими полями
Ситуация, описанная автором:
Я хочу добавить проверку дат. Я генерирую зарплату для рабочих, но если зарплата уже создана, и администратор пытается создать зарплату между уже созданными датами, я хочу показать ему сообщение
Пожалуйста, выберите другие датыили что-то подобное. Предположим, что зарплата с 10.01.2022 по 15.01.2022 уже создана, тогда я не хочу создавать другую зарплату между этими датами.
Другими словами, как проверить пару полей с валидацией их определённым правилом в базе данных, что-то вроде select * from salaries where user_id = ? and date_from < ? and date_to > ?
Допустим у нас есть форма или конечная точка API, которая принимает запросе эти параметры:
user_id
date_from
date_to
amount
Если мы преобразуем это в язык 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
.
Это всего лишь один из способов сделать это: вы можете создать собственное правило проверки с другим синтаксисом или даже проверять данные непосредственно в Контроллере вместо Запроса Формы и правил валидации. Выбор остаётся за вами!