mcandylab / laravel-cuid2
CUID2 support for Laravel
Requires
- php: ^8.2
- illuminate/support: ^12.0 || ^13.0
- visus/cuid2: ^6.0
Requires (Dev)
- fakerphp/faker: ^1.24
- orchestra/testbench: ^10.0 || ^11.0
- phpunit/phpunit: ^11.0 || ^12.0
README
Use CUID2 as primary keys for your Eloquent
models in Laravel. The package provides a model trait, a global cuid2() helper and
schema macros for migrations. Generation is delegated to the
visus/cuid2 library.
Requirements
- PHP >= 8.2
- Laravel 12 or 13 (Laravel 13 requires PHP 8.3+)
Installation
composer require mcandylab/laravel-cuid2
The package uses auto-discovery. Publish the config if needed:
php artisan vendor:publish --provider="Mcandylab\LaravelCuid2\LaravelCuid2ServiceProvider" --tag="config"
Usage
Model trait
Add the HasCuid2 trait — the primary key will be automatically populated with a
valid CUID2 when a record is created:
use Illuminate\Database\Eloquent\Model; use Mcandylab\LaravelCuid2\Concerns\HasCuid2; class Post extends Model { use HasCuid2; }
The trait sets keyType = 'string' and incrementing = false for you.
To generate a cuid2 for more than just the primary key, override uniqueIds():
public function uniqueIds(): array { return [$this->getKeyName(), 'public_id']; }
Migrations
The cuid2() and foreignCuid2() macros declare varchar columns (Laravel's
Schema::defaultStringLength, 255 by default). They are fully indexable across
databases and fit any value the package generates, including prefixed
{prefix}_{cuid2} identifiers:
Schema::create('posts', function (Blueprint $table) { $table->cuid2()->primary(); // id column $table->string('title'); $table->timestamps(); }); Schema::create('comments', function (Blueprint $table) { $table->cuid2()->primary(); $table->foreignCuid2('post_id')->constrained(); $table->text('body'); });
For polymorphic relations use cuid2Morphs() (and nullableCuid2Morphs()),
the CUID2 counterparts of Laravel's ulidMorphs(). They add a {name}_type
string column, a {name}_id varchar column and a composite index:
Schema::create('tokens', function (Blueprint $table) { $table->cuid2()->primary(); $table->cuid2Morphs('tokenable'); // tokenable_type + tokenable_id $table->string('token'); }); // nullable variant $table->nullableCuid2Morphs('tokenable');
Helper
$id = cuid2(); // 24 characters (or config('laravel-cuid2.length')) $short = cuid2(10); // exactly 10 characters (4..32 allowed)
Facade
use Mcandylab\LaravelCuid2\LaravelCuid2Facade as Cuid2; Cuid2::generate(); // generate an id Cuid2::isValid($someId); // validate a string
Str macros
Aligned with the core Str::uuid() / Str::ulid() helpers:
use Illuminate\Support\Str; Str::cuid2(); // generate (respects config('laravel-cuid2.length')) Str::cuid2(10); // exactly 10 characters (4..32 allowed) Str::isCuid2($value); // validate a value (false for non-strings)
Faker
A cuid2() Faker formatter is available for factories and seeders:
use App\Models\Post; Post::factory()->create(['id' => fake()->cuid2()]); fake()->cuid2(); // generate (respects config('laravel-cuid2.length')) fake()->cuid2(10); // exactly 10 characters (4..32 allowed)
Validation
The cuid2 rule validates that a value is a well-formed CUID2 of any valid length
(4..32). It is available in three forms:
use Illuminate\Validation\Rule; use Mcandylab\LaravelCuid2\Rules\Cuid2; $request->validate([ 'id' => 'cuid2', // string rule 'ref' => [new Cuid2], // rule object 'ext' => [Rule::cuid2()], // rule macro ]);
Prefixed identifiers (Stripe-style)
You can produce human-readable identifiers such as user_p6p168tx… by declaring
a $cuid2Prefix property on the model. The prefix is just an envelope — the
cuid2 part after the _ stays fully spec-compliant:
class User extends Model { use HasCuid2; protected string $cuid2Prefix = 'user'; }
No special column type is needed: cuid2() and foreignCuid2() already create a
varchar that fits any {prefix}_{cuid2} value:
Schema::create('users', function (Blueprint $table) { $table->cuid2()->primary(); $table->string('name'); }); // a foreign key referencing a prefixed model $table->foreignCuid2('user_id');
The generators accept an optional prefix too:
cuid2(prefix: 'user'); // user_… Str::cuid2(prefix: 'user'); // user_… fake()->cuid2(prefix: 'user'); // user_…
Validation can optionally check the prefix — it verifies the {prefix}_ and
that the remainder is a valid CUID2. Without a prefix the rule is unchanged:
$request->validate([ 'id' => 'cuid2:user', // string rule 'id2' => [new Cuid2('user')], // rule object 'id3' => [Rule::cuid2(prefix: 'user')], // rule macro ]); Str::isCuid2($value, 'user'); // and via the Str macro
Note:
cuid2Morphs()/nullableCuid2Morphs()take no prefix argument — a polymorphic column may point at models with different prefixes, and the prefix is already implied by the{name}_typecolumn. Like every id column in this package,{name}_idis avarchar, so a prefixed model can safely be a polymorphic target without truncation.
Configuration
config/laravel-cuid2.php:
return [ // Identifier length (4..32). The cuid2 standard is 24. 'length' => (int) env('CUID2_LENGTH', 24), ];
length only affects generation (cuid2(), Str::cuid2(), fake()->cuid2(),
HasCuid2). Schema macros are unaffected — they always declare a varchar, so
changing the length does not require a migration.
Testing
composer test
Changelog
See CHANGELOG.
Contributing
See CONTRIBUTING.
Security
If you discover any security related issues, please open an issue.
Credits
License
The MIT License (MIT). See License File.