How to Handle Composite Unique Key Validation in Filament

In this article, we will learn how to achieve composite unique key validation in filament.

In most practical scenarios, a single column is insufficient to ensure uniqueness.

For example:

  1. Roll number unique per class or per section
  2. section name should be unique per grade
  3. subject code unique per semester and so on..

This is known as composite unique constraint, where more than one column need to be unique together. In this article, we will learn how to achieve composite unique key validation in filament.

Single Column Unique Validation

In Filament, using a unique validation rule on a single column is very easy. You just chain the unique() method directly on the form component to validate uniqueness on the column level

TextInput::make('code')
                    ->label(__('resource.career.fields.code'))
                    ->required()
                    ->unique(),

In the above code, ->unique auto handles the validation.

Look at other validation rules.

Composite Unique Key

Lets assume, we have the following migration:

Schema::create('sections', function (Blueprint $table) {
    $table->id();
    $table->foreignId('grade_id')->constrained()->cascadeOnDelete();
    $table->string('name');
    $table->timestamps();

    $table->unique(['grade_id', 'name']);
});

The above migration ensures that the grade_id and name are always unique as the database level.

Filament Form Component (Create Mode)

Now, in our filament resource:

TextInput::make('name')
    ->required()
    ->rules([
        fn ($get) => Rule::unique('sections')
            ->where('grade_id', $get('grade_id'))
    ]);

Here,
Rule::unique(‘sections’) -> check uniqueness in sections table,
->where(‘grade_id’, $get(‘grade_id’)) -> add second condition
so, it validates combination of grade_id + name.

Edit Mode

Most importantly, in edit we must need to ignore the current record, else the update will always fail.

TextInput::make('name')
    ->required()
    ->rules([
        fn ($get, $record) => Rule::unique('sections')
            ->where('grade_id', $get('grade_id'))
            ->ignore($record?->id)
    ]);

Here ->ignore($record?->id) ignores the current model during update and prevents false validation errors.

Other ways

There are many other alternative ways for composite unique key validation in filament.

1. beforeCreate or beforeSave hooks in resource

You can use lifecycle hooks method to add the validation logic too.

protected function beforeCreate(): void
{ 
$exists = Sections::where('grade_id', $this->data['grade_id'])
        ->where('name', $this->data['name'])
        ->exists();

    if ($exists) {
        Notification::make()
            ->title('This combination already exists.')
            ->danger()
            ->send();

        $this->halt();
    }
}

Create a custom rule

You can also create a custom rule in laravel and use that for validation. Please check this article to implement the custom rule.

You can also check out this video.

Create a model level observer

You can also create an observer and use the same validation logic. This works for seeders, API but gives less control over API. You need to throw and catch an exception.

Create a custom validator

You can also create the custom validator and register in your provider.

Validator::extend('unique_combination', function ($attribute, $value, $parameters, $validator) {
    $data = $validator->getData();
    return !DB::table($parameters[0])
        ->where($parameters[1], $value)
        ->where($parameters[2], $data[$parameters[2]])
        ->exists();
});

Then we can use that in our field:

->rules(['unique_combination:sections,grade_id,name'])

Conclusion

ApproachReusbaleUI ControlWorks outside filament
Rule::unique()NoField level errorNo
Custom ValidatorPartialField Level ErrorYes
beforeCreate/Save lifecycle hookNoNotificationNO
Custom Rule ClassYesField-level errorYes
Model ObserverYesLimitedYes

For most Filament use cases, combining Custom Rule Class (for reusability) + database unique constraint (as a safety net) is the recommended approach.

In this way, we can achieve composite unique key validation in filament.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top