juniorb2ss/eloquent-uuid
Eloquent UUID adds UUID support to Laravel's Eloquent models, letting you use universally unique identifiers as primary keys with minimal configuration. Useful for distributed apps or anywhere non-sequential IDs are preferred.
This is a single-purpose trait package, not a framework extension — there is nothing to publish, no config file, and likely no service provider to register. Before wiring it in, open the repo's src/ directory and composer.json: you need the exact trait namespace and the illuminate/database version constraint, because a package this small and this old (3 stars, 0 dependents) will often pin ^5.x and refuse to install on modern Laravel.
composer require juniorb2ss/eloquent-uuid
composer show juniorb2ss/eloquent-uuid # confirm version + illuminate constraint
use Juniorb2ss\EloquentUuid\UuidTrait; // verify actual name in src/
class Post extends Model
{
use UuidTrait;
public $incrementing = false;
protected $keyType = 'string';
}
Schema::create('posts', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('title');
$table->timestamps();
});
Post::create(['title' => 'hello']) in php artisan tinker, then dump $post->id and confirm it is a 36-character string that also exists in the database. If you get 0, your $incrementing/$keyType settings or the migration column type are wrong — the trait succeeded but Eloquent clobbered the value on read.Primary key replacement. The canonical pattern above: trait + $incrementing = false + $keyType = 'string' + uuid column. Keep the key name id so relationships, route binding, and find() behave normally. Do not add id to $fillable.
Secondary UUID column. If the trait writes to a fixed attribute (many of these packages hardcode the primary key, others accept a property), you can keep an integer PK for joins and expose a UUID externally. Only do this if the source actually supports a configurable attribute — otherwise write your own creating hook, which is five lines and easier to reason about:
protected static function booted()
{
static::creating(fn ($m) => $m->uuid ??= (string) Str::uuid());
}
Relationships and foreign keys. Child tables must use $table->uuid('post_id') and $table->foreign('post_id')->references('id')->on('posts'). A mismatched unsignedBigInteger FK is the most common silent failure here — inserts succeed, joins return nothing. Define belongsTo/hasMany exactly as normal; no override needed when the UUID is the primary key.
Route model binding. With a UUID primary key, Route::get('/posts/{post}', ...) works untouched, because implicit binding uses getRouteKey(). With a secondary UUID column, override getRouteKeyName() to return 'uuid' and ensure the column is uniquely indexed.
Factories and seeders. Never set the key manually in a factory definition — omit id entirely and let the trait fire on creating. If you need deterministic IDs in tests, set the attribute explicitly before save() only after confirming the trait guards against overwriting; if it does not guard, either extend the trait locally or bypass it:
$post = new Post(['title' => 'x']);
$post->id = '00000000-0000-4000-8000-000000000000';
$post->save();
Bulk inserts. Model::insert() skips model events entirely — the trait will not fire. Use create()/save() in loops, or generate UUIDs yourself with Str::uuid() before calling insert().
Layering with other boot traits. Trait boot methods (boot{TraitName}) run in the order traits are declared on the class. If another trait's creating listener reads $model->getKey(), place UuidTrait first so the key exists when that listener fires.
$keyType = 'string' is the number-one bug. MySQL returns 'a1b2...', Eloquent casts it to int, you get 0. Symptoms: find() returns null for an object you just saved, save() on an existing model inserts a duplicate.$table->uuid() is char(36) on MySQL, uuid on Postgres, varchar on SQLite. Some UUID packages store binary(16) instead — mixing the two produces unreadable keys and broken comparisons. Match the migration to whatever the package generates.id, expect index fragmentation and arbitrary ordering. Use created_at for chronological queries, or prefer Laravel's HasUuids / Str::orderedUuid() if you control the codebase.Illuminate\Database\Eloquent\Concerns\HasUuids and HasUniqueStringIds natively — for any new project, use those instead; this package is only defensible in a legacy codebase where it is already in use.$model->getKeyName(), $model->getKeyType(), $model->getIncrementing(), Schema::getColumnType('posts', 'id'), and DB::listen(...) to see the actual INSERT payload.creating listener in booted() that overwrites the key after the trait runs — that gives you ordered UUIDs, prefixed IDs, or test doubles without touching vendor code.str_random fallback). Wrap batch imports in a retry or pre-generate keys.How can I help you explore Laravel packages today?