PHPackages                             willvincent/laravel-unique - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. willvincent/laravel-unique

ActiveLibrary[Database &amp; ORM](/categories/database)

willvincent/laravel-unique
==========================

Provides a model trait to automatically append a value to model values that should be unique.

1.2(1y ago)5711.8k↑75%2[1 PRs](https://github.com/willvincent/laravel-unique/pulls)MITPHPPHP ^8.2CI passing

Since Mar 12Pushed 4mo ago1 watchersCompare

[ Source](https://github.com/willvincent/laravel-unique)[ Packagist](https://packagist.org/packages/willvincent/laravel-unique)[ Docs](https://github.com/willvincent/laravel-unique)[ GitHub Sponsors](https://github.com/willvincent)[ RSS](/packages/willvincent-laravel-unique/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (8)Versions (4)Used By (0)

Unique Names
============

[](#unique-names)

[![Latest Version on Packagist](https://camo.githubusercontent.com/833a3772d89c6d0c18ff95f2dcf68646c0bebcbc19a0dd3be8949b7349884dc2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f77696c6c76696e63656e742f6c61726176656c2d756e697175652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/willvincent/laravel-unique)[![GitHub Tests Action Status](https://camo.githubusercontent.com/f2a171d3e1571ba3b3ecc30523fe81b338de17ba1707ce193c4536d3c0b7e948/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f77696c6c76696e63656e742f6c61726176656c2d756e697175652f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/willvincent/laravel-unique/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/8fb4095d741468d723da6d595bbd4d91895a6184ac7a19ce3d02510e40355ed6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f77696c6c76696e63656e742f6c61726176656c2d756e697175652f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/willvincent/laravel-unique/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/3ae4b70aa85ae898090f4046ee4c91d0b1194eaa3c2abd076d8d4879584c53e2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f77696c6c76696e63656e742f6c61726176656c2d756e697175652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/willvincent/laravel-unique)

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.

How It Works
------------

[](#how-it-works)

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:

- On **create**, it generates a unique value.
- On **update**, it only adjusts the field if it has changed (i.e., if it’s “dirty”).

If a duplicate exists, the trait either appends a suffix (e.g., `Foo (1)`) or uses a custom generator to produce a unique value.

Features
--------

[](#features)

- Enforces uniqueness at the application level before saving.
- Supports custom suffix formats (e.g., ` ({n})` or `-{n}`).
- Allows custom value generators for advanced uniqueness logic.
- Configurable via a config file or model properties.
- Handles constraints (e.g., uniqueness within a specific scope).

Installation
------------

[](#installation)

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,
];
```

Usage
-----

[](#usage)

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})')
}
```

### Configuration Options

[](#configuration-options)

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.

### Examples

[](#examples)

#### Default Suffix

[](#default-suffix)

Ensure names are unique within an organization:

```
protected $uniqueField = 'name';
protected $constraintFields = ['organization_id'];
protected $uniqueSuffixFormat = ' ({n})';
```

- Input: `name: "Foo", organization_id: 1`
    - Output: `"Foo"` (if unique)
    - Output: `"Foo (1)"` (if `"Foo"` exists)
    - Output: `"Foo (2)"` (if `"Foo"` and `"Foo (1)"` exist)

#### Slug Format

[](#slug-format)

Use a slug-friendly suffix:

```
protected $uniqueField = 'slug';
protected $constraintFields = ['organization_id'];
protected $uniqueSuffixFormat = '-{n}';
```

- Input: `slug: "bar", organization_id: 1`
    - Output: `"bar"` (if unique)
    - Output: `"bar-1"` (if `"bar"` exists)

#### Custom Generator

[](#custom-generator)

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

- Input: `name: "baz"`
    - Output: `"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.

### Using a Custom Unique Table

[](#using-a-custom-unique-table)

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.

#### Example

[](#example)

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'];
}
```

- When saving a new Item, the trait will check for duplicates in the legacy\_items table (not items).
- If a duplicate is found in legacy\_items, it will append a suffix (e.g., "Foo (1)") or use a custom generator to make the name unique.

#### Important Notes:

[](#important-notes)

- The custom table (e.g., legacy\_items) must have the same columns as specified in `$uniqueField` (e.g., name) and `$constraintFields` (e.g., organization\_id).
- Soft delete behavior (if enabled) will respect the custom table's deleted\_at column.

#### Soft Deletes with Custom Tables

[](#soft-deletes-with-custom-tables)

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:

- When \_uniqueIncludesTrashed is true, soft-deleted records in the custom table are included in uniqueness checks.
- When false, they are ignored.

This ensures consistent behavior whether using the model's primary table or a custom table.

Advanced Configuration
----------------------

[](#advanced-configuration)

### Custom Generator Details

[](#custom-generator-details)

The custom generator can be:

- A **string**: The name of a method on the model.
- A **callable**: An anonymous function or closure.

If the generated value isn’t unique, the trait retries with increasing attempt counts until `max_tries` is reached, then throws an exception.

### Database Considerations

[](#database-considerations)

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.

Testing
-------

[](#testing)

The package includes a test suite with over 97% coverage of the code, testing:

- Uniqueness on create and update
- Suffix formats
- Custom generators
- Constraint handling
- Edge cases (e.g., null constraints, max tries)

Run the tests with:

```
composer test
```

Source Code
-----------

[](#source-code)

View or contribute to the package on GitHub: [willvincent/laravel-unique](https://github.com/willvincent/laravel-unique)

Changelog
---------

[](#changelog)

See [CHANGELOG](CHANGELOG.md) for recent updates.

Credits
-------

[](#credits)

- [Will Vincent](https://github.com/willvincent)
- [All Contributors](../../contributors)

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE.md) for more information.

###  Health Score

45

—

FairBetter than 93% of packages

Maintenance63

Regular maintenance activity

Popularity39

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 75% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

431d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0c6239b14bdb77aba00cd0bc65f561ad8dd694ec4a18508c69eff119352d54fa?d=identicon)[willvincent](/maintainers/willvincent)

---

Top Contributors

[![willvincent](https://avatars.githubusercontent.com/u/689891?v=4)](https://github.com/willvincent "willvincent (24 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (5 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (3 commits)")

---

Tags

deduplicationeloquentlaravelmodel-observermulti-value-constraintmultitenanttraitlaravelWill Vincentlaravel-unique

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/willvincent-laravel-unique/health.svg)

```
[![Health](https://phpackages.com/badges/willvincent-laravel-unique/health.svg)](https://phpackages.com/packages/willvincent-laravel-unique)
```

###  Alternatives

[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)[spatie/laravel-model-flags

Add flags to Eloquent models

4301.1M1](/packages/spatie-laravel-model-flags)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)[spatie/laravel-sql-commenter

Add comments to SQL queries made by Laravel

1931.4M1](/packages/spatie-laravel-sql-commenter)[spatie/laravel-deleted-models

Automatically copy deleted records to a separate table

409109.8k4](/packages/spatie-laravel-deleted-models)[wnx/laravel-backup-restore

A package to restore database backups made with spatie/laravel-backup.

203330.1k2](/packages/wnx-laravel-backup-restore)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
