Skip to main content
Tutorial Free

Laravel Custom Validation Rule with Date Period and Multiple Fields

December 07, 2022
3 min read

I've recently read a Laracasts forum post with a question about validating a date period. Decided to write this article with a possible solution.

The situation, as described by the author:

I want to add validation to dates. I am generating a salary for workers but If the salary is already created and the admin tries to create the salary between already created dates then I want to show him the message "Please select different dates" or something else.

Suppose the salary between 1-10-2022 to 1-15-2022 is already created then I don't want to create another salary between these dates.

In other words, how to validate a pair of fields with checking their certain rule in the database, something like select * from salaries where user_id = ? and date_from < ? and date_to > ?.

Let's say that we have a form, or an API endpoint that accepts these parameters in request:

  • user_id
  • date_from
  • date_to
  • amount

If we transform that into Laravel's language, we have this:

// 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 [
// ... BIG QUESTION: WHAT DO WE PUT HERE?
];
}
}

The problem is that we don't have such validation rule out of the box in the list of default Laravel validation rules. So we need to create a custom rule here.

I will give you an example with the new invokable syntax of custom validation rules. For older way (which still works), please visit the Laravel 8 docs here.

First, we create our rule:

php artisan make:rule UniqueSalaryRule --invokable

Then we fill in the __invoke method with the database call, to check if the salary for the user_id and time period exists.

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;
}
}

To access the other attributes of the $request, we need to implement DataAwareRule interface and have setData method, read the docs section called Accessing Additional Data in the official docs.

Finally, we activate that validation rule in our FormRequest class:

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

And that's it: if the date_from and date_to combination is invalid, it will return the validation error on the date_from field.

Again, this is just one way of doing it: you can create a custom validation rule with another syntax, or even validate the data directly in the Controller instead of Form Request and validation rules. The choice is yours!

Enjoyed This Tutorial?

Get access to all premium tutorials, video and text courses, and exclusive Laravel resources. Join our community of 10,000+ developers.

Comments & Discussion

V
VP ✓ Link copied!

if i want pass some other value to the custom rule

MM
Miguel Miller ✓ Link copied!

You can use the construct function. Example:

class UniqueSalaryRule implements ValidationRule
{
private $foo;
 
public function __construct($foo)
{
$this->foo = $foo;
}
 
// ...
}
 
 
class StoreSalaryRequest extends FormRequest
{
public function rules()
{
$foo = 'random value';
return [
'user_id' => 'required',
'date_from' => ['required', new UniqueSalaryRule($foo)],
'date_to' => 'required',
'amount' => 'required',
];
}
}

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.