2 min read

Case-Insensitive Unique Validation in Laravel

Need to validate uniqueness without case sensitivity in Laravel? Here’s a custom rule that does just that — perfect for things like emails, titles, or usernames.

Create a custom validation rule like this:

/app/Rules/UniqueCaseInsensitive.php
namespace App\Rules;
 
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
 
class UniqueCaseInsensitive implements ValidationRule
{
    protected string $table;
    protected string $column;
    protected $ignore = null;
    protected string $idColumn = 'id';
    protected array $wheres = [];
 
    public function __construct(string $table, string $column = 'email')
    {
        $this->table = $table;
        $this->column = $column;
    }
 
    public function ignore($id, ?string $idColumn = null): self
    {
        if ($id instanceof Model) {
            return $this->ignoreModel($id, $idColumn);
        }
 
        $this->ignore = $id;
        $this->idColumn = $idColumn ?? 'id';
 
        return $this;
    }
 
    public function ignoreModel(Model $model, ?string $idColumn = null): self
    {
        $this->idColumn = $idColumn ?? $model->getKeyName();
        $this->ignore = $model->getAttribute($this->idColumn);
 
        return $this;
    }
 
    public function where(Closure $callback): self
    {
        $this->wheres[] = $callback;
 
        return $this;
    }
 
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $query = DB::table($this->table)
            ->whereRaw("LOWER({$this->column}) = ?", [strtolower($value)]);
 
        foreach ($this->wheres as $callback) {
            $query = $query->where($callback);
        }
 
        if (!is_null($this->ignore)) {
            $query->where($this->idColumn, '!=', $this->ignore);
        }
 
        if ($query->exists()) {
            $fail(__('validation.unique'));
        }
    }
}

Example Usage

In your form request or controller:

use App\Rules\UniqueCaseInsensitive;
 
$rules = [
    'title' => [
        'required',
        new UniqueCaseInsensitive('departments', 'title')
            ->where(function ($query) use ($companyId) {
                return $query->where('company_id', $companyId)
                             ->whereNull('deleted_at');
            })
            ->ignore($departmentId),
    ],
];

This rule respects soft deletes, ignores a specific row, and ensures lowercase uniqueness — all while staying clean and readable.