Laravel Unique Validation for Multiple Columns

Laravel Validation already has a Unique rule to check if some record exists in the database. But what if you want a combination of TWO fields to be unique? Let me demonstrate.

The unique rule is commonly used for a single field like this:

// ...
 
public function rules(): array
{
return [
'user_id' => ['required', 'integer'],
'date' => [
'required',
'unique:expenses,date'
],
'amount' => ['required', 'numeric'],
];
}
 
// ...

And it works to validate a single field:

But what if you must validate the Date and User ID to be unique together? In other words, a user can have only one record on a particular date.

I see many developers don't use Validation rules for this and write this check manually:

app/Http/Controllers/ExpenseController.php

use App\Http\Requests\StoreExpenseRequest;
use App\Models\Expense;
 
// ...
 
public function store(StoreExpenseRequest $request)
{
$hasExpenseSavedForUser = Expense::query()
->where('date', $request->input('date'))
->where('user_id', $request->input('user_id'))
->exists();
 
if ($hasExpenseSavedForUser) {
return back()->withErrors([
'date' => 'Expense already saved for this user on this date'
]);
}
 
Expense::create($request->validated());
 
return to_route('expenses.index');
}

It works as expected, and Validation will be triggered if the Expense already exists for the User on the same Date:

But what if I told you that you could use Laravel Validation to extend the unique rule?

You can use Rule::unique() and add ->where() condition, making it work for multiple fields:

app/Http/Requests/StoreExpenseRequest.php

use Illuminate\Validation\Rule;
 
// ...
 
public function rules(): array
{
return [
'user_id' => ['required', 'integer'],
'date' => [
'required',
'unique:expenses,date',
Rule::unique('expenses', 'date')->where('user_id', $this->input('user_id')),
],
'amount' => ['required', 'numeric'],
];
}
 
public function messages()
{
return [
'date.unique' => 'Expense for selected user on selected date already exists.',
];
}

With that rule in place, we can remove the code from our Controller and let the Form Request Validation handle it:

app/Http/Controllers/ExpenseController.php

use App\Http\Requests\StoreExpenseRequest;
use App\Models\Expense;
 
// ...
 
public function store(StoreExpenseRequest $request)
{
$hasExpenseSavedForUser = Expense::query()
->where('date', $request->input('date'))
->where('user_id', $request->input('user_id'))
->exists();
 
if ($hasExpenseSavedForUser) {
return back()->withErrors([
'date' => 'Expense already saved for this user on this date'
]);
}
 
Expense::create($request->validated());
 
return to_route('expenses.index');
}
 
// ...

It will result in the same validation message displayed to the User:

The Rule::unique() class can accept multiple conditions using the where() method. You can read more about it in the Laravel Docs.

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 40 courses (785 lessons, total 44 h 48 min)
  • 48 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord (Yearly members)

Recent Premium Tutorials