Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Uuid Laravel Package

healthengine/laravel-uuid

A small Laravel package for using UUIDs with Eloquent models. It helps generate UUID primary keys automatically and integrate UUID-backed models into your app with minimal setup, making it easier to add collision-resistant identifiers to Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a single-purpose Eloquent trait: it makes a model use a UUID string primary key and generates the UUID when the model is created. There is no admin UI, migration generator, or large API surface to learn.

  1. Install it (it is archived but still installable):
composer require healthengine/laravel-uuid

Before installing, open the package's composer.json and check its illuminate/* / PHP constraints. The last release was 2020-11-10, so expect Laravel 6–8-era compatibility rather than current Laravel.

  1. Look first at src/HasUuids.php. That file is the package. It should show the trait setting the key type to string, disabling incrementing, and registering a creating listener that assigns a UUID when the key is empty. Reading that file takes a minute and tells you everything the package does.

  2. Add the trait to a model:

namespace App\Models;

use HealthEngineAU\LaravelUuid\HasUuids;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasUuids;
}

You normally do not need to set $incrementing = false or $keyType = 'string' yourself; the trait handles that.

  1. Create a matching migration:
Schema::create('posts', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('title');
    $table->timestamps();
});
  1. First use case:
$post = Post::create(['title' => 'Hello']);

echo $post->id; // "9b2f..." — generated automatically

The ID is assigned during the creating event, so you never set it manually. Route model binding works without changes:

Route::get('/posts/{post}', [PostController::class, 'show']);

Implementation Patterns

Standard model setup. Put the trait on each UUID-keyed model, or on a shared base model. Do not duplicate $incrementing / $keyType unless you have a reason; let the trait control them.

Parent and child tables. Use UUID column types consistently:

Schema::create('comments', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('post_id')->constrained()->cascadeOnDelete();
    $table->text('body');
    $table->timestamps();
});

On older Laravel versions without foreignUuid, use:

$table->uuid('post_id');
$table->foreign('post_id')->references('id')->on('posts');

Relationships. Relationships work normally as long as the foreign keys are UUIDs and both models use string keys:

public function comments(): HasMany
{
    return $this->hasMany(Comment::class);
}

Polymorphic relationships. Use UUID-aware morph columns:

$table->uuidMorphs('commentable');
// or
$table->nullableUuidMorphs('commentable');

Factories. Do not set the ID in factories; let the trait generate it:

$post = Post::factory()->create();

If a test needs a deterministic or ordered ID, pass one explicitly:

$post = Post::factory()->create([
    'id' => (string) Str::orderedUuid(),
]);

Seeding and bulk inserts. Model events do not fire for insert(), upsert(), or raw query-builder inserts. Generate IDs yourself:

Post::insert([
    [
        'id' => (string) Str::uuid(),
        'title' => 'First',
        'created_at' => now(),
        'updated_at' => now(),
    ],
]);

API design. UUID primary keys are safe to expose in URLs and JSON responses, so you usually do not need hashids or opaque-ID packages for these models. Validate incoming UUIDs with Laravel's uuid rule:

$request->validate([
    'post_id' => ['required', 'uuid', 'exists:posts,id'],
]);

Testing. UUIDs are strings, so assertions are straightforward:

$post = Post::factory()->create();

$this->assertTrue(Str::isUuid($post->id));
$this->assertDatabaseHas('posts', ['id' => $post->id]);

RefreshDatabase and factories work normally.

Custom ID generation. If you need ordered UUIDs, ULIDs, or a different UUID version, inspect src/HasUuids.php for the generation method and override it in your own trait or base model. If the package only hooks creating, you can generate the ID yourself in a booted() listener:

protected static function booted(): void
{
    static::creating(function (self $model) {
        if (empty($model->getKey())) {
            $model->{$model->getKeyName()} = (string) Str::orderedUuid();
        }
    });
}

Be careful about listener ordering relative to the package trait's own creating listener.

Distributed writes. The main architectural benefit is that multiple services, queues, or database shards can insert rows without coordinating an auto-increment sequence. This is useful for merging datasets, replication, or migrating between systems.

Migrating an existing bigint table. Add a new UUID column, backfill it, update foreign keys, then swap the primary key. Do not attempt an in-place primary-key type change on a large production table.

Gotchas and Tips

The package is archived and very small. Last release 2020-11-10, 1 star, no dependents. Treat it as a snippet rather than a maintained dependency. On Laravel 9+, use the framework's built-in trait instead:

use Illuminate\Database\Eloquent\Concerns\HasUuids;

class Post extends Model
{
    use HasUuids;
}

Laravel 10/11's built-in HasUuids generates ordered UUIDs, which is usually better for index locality than random v4 UUIDs.

Do not use both traits in one model. Mixing HealthEngineAU\LaravelUuid\HasUuids with Illuminate\Database\Eloquent\Concerns\HasUuids will cause conflicting properties or methods and confusing behavior. Pick one.

Bulk operations bypass UUID generation. insert(), upsert(), DB::table(...)->insert(), and Model::withoutEvents() skip the creating event, leaving id null unless the column has a database default. Generate IDs explicitly in those paths.

Null IDs can fail silently. If your uuid('id') column is nullable, a broken event listener can write null rows without error. For debugging, add a listener that throws when the key is empty:

static::creating(function (self $model) {
    if (empty($model->getKey())) {
        throw new \RuntimeException('UUID was not generated for '.static::class);
    }
});

Fillable and guarded attributes. The trait assigns the key directly, not through mass assignment, so $guarded = ['id'] is fine. If you make id fillable, callers can supply their own UUID; validate it if that matters.

UUIDv4 is random, not sortable. Do not use latest('id') or assume UUID order reflects creation order. Use created_at, or switch to ordered UUIDs/ULIDs if you need sortable IDs.

Index performance matters on write-heavy tables. Random UUIDv4 values as a clustered primary key in MySQL/InnoDB cause page splits and larger indexes. For high-volume inserts, benchmark against bigint primary keys, ordered UUIDs, or ULIDs. For small tables this is rarely a problem.

Keep UUID column types consistent. Use uuid (char(36)) everywhere: primary keys, foreign keys, pivot tables, morph columns. Mixing binary(16) storage with the package's string UUIDs will cause join and cast problems.

Route binding and validation. A malformed UUID in a URL typically becomes a 404 because the database lookup fails. In JSON APIs, prefer explicit uuid validation to return a 422. Route constraints such as ->whereUuid('post') are useful when you want to reject invalid formats before controller execution, but they still produce 404s.

Audit package migrations when your User is UUID-keyed. Sanctum's personal_access_tokens table and some third-party packages use morphs / bigint foreign keys. If your User model uses UUIDs, check those migrations and adjust the tokenable column type where necessary. Default session authentication is unaffected.

Config and provider caching. If you publish or modify any package configuration or manually register the service provider, clear caches after changes:

php artisan config:clear
php artisan clear-compiled
php artisan config:cache

Stale caches are a common reason a trait or config change appears not to work.

Vendor the trait instead of depending on the package. Because the package is archived and tiny, the lowest-risk approach is often to copy the relevant trait into app/Models/Concerns/HasUuids.php, adjust it for your Laravel version, and remove the dependency. That gives you full control over UUID generation, ordered UUID support, and future framework compatibility.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource