Provides a model trait to automatically append a value to model values that should be unique.
willvincent/laravel-unique is a Laravel package for provides a model trait to automatically append a value to model values that should be unique..
It currently has 57 GitHub stars and 17.307 downloads on Packagist (latest version 1.2).
Install it with composer require willvincent/laravel-unique.
Discover more Laravel packages by willvincent
or browse all Laravel packages to compare alternatives.
Last updated
A trait for Laravel Eloquent models to ensure a field remains unique within specified constraints.
It offers flexible suffix formats or custom value generators, making it ideal for scenarios like unique names, slugs, or identifiers.
The HasUniqueNames trait hooks into the Laravel Eloquent saving event, which fires before a model is persisted
to the database (on both create and update operations). It checks if the designated unique field (e.g., name)
already exists within the defined constraints (e.g., organization_id). If a duplicate is detected:
If a duplicate exists, the trait either appends a suffix (e.g., Foo (1)) or uses a custom generator
to produce a unique value.
({n}) or -{n}).Install the package via Composer:
composer require willvincent/laravel-unique
Publish the configuration file (optional) to customize defaults:
php artisan vendor:publish --provider="WillVincent\LaravelUnique\LaravelUniqueServiceProvider"
Here’s the default configuration file (config/unique_names.php):
return [
/*
|-----------------------------------------------------------------------------------------
| Unique Name Field
|-----------------------------------------------------------------------------------------
| The default field name to enforce uniqueness on.
*/
'unique_field' => 'name',
/*
|-----------------------------------------------------------------------------------------
| Constraint Fields
|-----------------------------------------------------------------------------------------
| Fields defining the scope of uniqueness. For example, to ensure unique equipment names
| within a department, set 'constraint_fields' to ['department_id'].
*/
'constraint_fields' => [],
/*
|-----------------------------------------------------------------------------------------
| Suffix Format
|-----------------------------------------------------------------------------------------
| Defines how suffixes are appended to duplicates. Use '{n}' as a placeholder for the number.
| Examples: ' ({n})' → 'Foo (1)', '-{n}' → 'foo-1'.
*/
'suffix_format' => ' ({n})',
/*
|-----------------------------------------------------------------------------------------
| Deduplication Max Tries
|-----------------------------------------------------------------------------------------
| Maximum attempts to generate a unique value before throwing an exception.
*/
'max_tries' => 10,
];
Add the HasUniqueNames trait to your Eloquent model and optionally configure it:
use WillVincent\LaravelUnique\HasUniqueNames;
class YourModel extends Model
{
use HasUniqueNames;
// Optional: Override default settings
protected $uniqueField = 'name'; // Field to keep unique (default: 'name')
protected $constraintFields = ['organization_id']; // Scope of uniqueness (default: [])
protected $uniqueSuffixFormat = ' ({n})'; // Suffix format (default: ' ({n})')
}
You can customize the trait’s behavior either in the config/unique_names.php file or by
overriding properties in your model:
uniqueField: The field to enforce uniqueness on (default: 'name').constraintFields: Array of fields defining the uniqueness scope (default: []).uniqueSuffixFormat: Format for suffixes, with {n} as the number placeholder (default: ' ({n})').uniqueValueGenerator: Optional custom generator (see below).uniqueTableName: Optional alternate table to enforce uniqueness within (see below).Model properties take precedence over config file settings.
Ensure names are unique within an organization:
protected $uniqueField = 'name';
protected $constraintFields = ['organization_id'];
protected $uniqueSuffixFormat = ' ({n})';
name: "Foo", organization_id: 1
"Foo" (if unique)"Foo (1)" (if "Foo" exists)"Foo (2)" (if "Foo" and "Foo (1)" exist)Use a slug-friendly suffix:
protected $uniqueField = 'slug';
protected $constraintFields = ['organization_id'];
protected $uniqueSuffixFormat = '-{n}';
slug: "bar", organization_id: 1
"bar" (if unique)"bar-1" (if "bar" exists)Define a custom method or callable for unique values:
Method on Model:
protected $uniqueValueGenerator = 'generateUniqueSlug';
public function generateUniqueSlug(string $base, array $constraints, ?int $attempt): string
{
return $base . '-' . \Str::random(5);
}
Callable:
protected $uniqueValueGenerator;
public function __construct() {
$this->uniqueValueGenerator = function (string $base, array $constraints, int $attempt): string {
return $base . '-' . \Str::random(5);
};
}
name: "baz"
"baz-abc12" (random 5-character suffix)The generator receives the base value, constraint values, and the retry attempt, and must return a unique string.
It retries up to max_tries times if the generated value isn’t unique, the first attempt will be 0, retries will
be numbered 1 through your limit.
By default, the HasUniqueNames trait checks for uniqueness in the model's primary table (e.g., items).
However, you can specify a different table for uniqueness checks using the $uniqueTableName property.
This is useful when your model saves to one table but needs to enforce uniqueness based on data in another table.
It is also useful if you're updating data in a table that is relevant to your model, but not necessarily represented
by a model itself; as an example subdomain records for multi-tenant applications.
Suppose you have a model that saves to the items table but needs to ensure uniqueness based on records in
a legacy_items table:
use WillVincent\LaravelUnique\HasUniqueNames;
class Item extends Model
{
use HasUniqueNames;
protected $table = 'items'; // Model saves to 'items'
protected $uniqueTableName = 'legacy_items'; // Uniqueness checked in 'legacy_items'
protected $uniqueField = 'name';
protected $constraintFields = ['organization_id'];
}
$uniqueField (e.g., name)
and $constraintFields (e.g., organization_id).If your model uses soft deletes and you’ve enabled unique_names.soft_delete in the config, the trait will consider soft-deleted records in the custom table based on the _uniqueIncludesTrashed setting:
This ensures consistent behavior whether using the model's primary table or a custom table.
The custom generator can be:
If the generated value isn’t unique, the trait retries with increasing attempt counts until max_tries is reached,
then throws an exception.
The trait enforces uniqueness at the application level. For data integrity, especially in high-concurrency scenarios, consider adding database-level unique constraints (e.g., unique indexes) alongside this trait.
The package includes a test suite with over 97% coverage of the code, testing:
Run the tests with:
composer test
View or contribute to the package on GitHub: willvincent/laravel-unique
See CHANGELOG for recent updates.
MIT License. See LICENSE for more information.