Laravel UUID v6.2.0

Eloquent Models Integration

webpatser@dev: ~/laravel-uuid/6.2.0 $ cat eloquent-models.md

Eloquent Models - Laravel UUID v6.2.0

Seamlessly integrate UUIDs into your Laravel Eloquent models with automatic generation, casting, and high-performance alternatives to Laravel's built-in UUID features.

High-Performance HasUuids Trait

Drop-in replacement for Laravel's HasUuids trait with 15% better performance:

use Illuminate\Database\Eloquent\Model;
use Webpatser\LaravelUuid\HasUuids; // Instead of Laravel's trait
 
class User extends Model
{
use HasUuids; // Uses our high-performance library
 
protected $fillable = ['name', 'email'];
}
 
// Usage - same API as Laravel's trait
$user = User::create(['name' => 'John', 'email' => '[email protected]']);
echo $user->id; // Auto-generated UUID: "550e8400-e29b-41d4-a716-446655440000"

Migration Setup

Set up your database tables with UUID primary keys:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('name');
$table->string('email')->unique();
$table->uuid('parent_id')->nullable();
$table->timestamps();
 
// Foreign key to another UUID
$table->foreign('parent_id')->references('id')->on('users');
});
}
};

Customizing UUID Generation

Control which UUID version is used and customize the generation logic:

use Illuminate\Database\Eloquent\Model;
use Webpatser\LaravelUuid\HasUuids;
use Webpatser\Uuid\Uuid;
 
class User extends Model
{
use HasUuids;
 
// Customize UUID generation method
public function newUniqueId(): string
{
return (string) Uuid::v7(); // Use V7 for better database performance
// Or: return (string) Uuid::v4(); // Standard random UUIDs
// Or: return (string) Uuid::generate(1); // Time-based V1
}
 
// Specify which columns get UUIDs (optional)
public function uniqueIds(): array
{
return ['id', 'uuid', 'external_id'];
}
}
 
// Additional trait methods available
$user = User::create(['name' => 'John']);
echo $user->getUuidVersion(); // "7" (or whichever version you're using)
$isOrdered = $user->usesOrderedUuids(); // true for V7 UUIDs
$timestamp = $user->getUuidTimestamp(); // Extract timestamp (V1/V7 only)

UUID Casting

Automatically convert UUID strings to UUID objects in your models:

use Illuminate\Database\Eloquent\Model;
use Webpatser\LaravelUuid\UuidCast;
 
class User extends Model
{
protected $casts = [
'id' => UuidCast::class,
'parent_id' => UuidCast::class,
'session_uuid' => UuidCast::class,
];
}
 
// Usage - automatic conversion
$user = User::find('550e8400-e29b-41d4-a716-446655440000');
$userId = $user->id; // Returns Webpatser\Uuid\Uuid instance
 
// Access UUID properties
echo $userId->string; // "550e8400-e29b-41d4-a716-446655440000"
echo $userId->hex; // "550e8400e29b41d4a716446655440000"
echo $userId->version; // 4
echo $userId->variant; // "RFC 4122"
 
// When saving, UUID objects are automatically converted to strings
$user->parent_id = Uuid::v7();
$user->save(); // Stores as string in database

Model Relationships with UUIDs

UUIDs work seamlessly with Eloquent relationships:

class User extends Model
{
use HasUuids;
 
protected $fillable = ['name', 'email'];
 
// One-to-many relationship
public function posts()
{
return $this->hasMany(Post::class);
}
 
// Many-to-many with pivot UUIDs
public function roles()
{
return $this->belongsToMany(Role::class)
->using(UserRole::class) // Pivot model with UUIDs
->withTimestamps();
}
}
 
class Post extends Model
{
use HasUuids;
 
protected $fillable = ['title', 'content'];
 
public function user()
{
return $this->belongsTo(User::class);
}
}
 
// Pivot model with UUIDs
class UserRole extends Pivot
{
use HasUuids;
 
protected $table = 'user_roles';
}
 
// Usage works exactly like integer keys
$user = User::create(['name' => 'John', 'email' => '[email protected]']);
$post = $user->posts()->create(['title' => 'Hello World', 'content' => '...']);
echo $post->user->name; // "John"

Migration for Relationships

// Posts table migration
Schema::create('posts', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('user_id'); // Foreign key
$table->string('title');
$table->text('content');
$table->timestamps();
 
$table->foreign('user_id')->references('id')->on('users');
$table->index('user_id'); // Index for performance
});
 
// Many-to-many pivot table
Schema::create('user_roles', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('user_id');
$table->uuid('role_id');
$table->timestamps();
 
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('role_id')->references('id')->on('roles');
$table->unique(['user_id', 'role_id']);
});

Route Model Binding

UUIDs work seamlessly with Laravel's route model binding:

// routes/web.php
Route::get('/user/{user}', [UserController::class, 'show']);
Route::get('/user/{user}/post/{post}', [PostController::class, 'show']);
 
// UserController.php
class UserController extends Controller
{
public function show(User $user)
{
// Laravel automatically finds the user by UUID
return view('user.show', compact('user'));
}
}
 
// URLs work with UUIDs:
// /user/550e8400-e29b-41d4-a716-446655440000
// /user/550e8400-e29b-41d4-a716-446655440000/post/018f4e7c-8b3a-7000-8000-123456789abc

Custom Route Key Names

class User extends Model
{
use HasUuids;
 
// Use a different column for route model binding
public function getRouteKeyName()
{
return 'uuid'; // Instead of 'id'
}
}
 
// Or specify in your routes
Route::get('/user/{user:uuid}', [UserController::class, 'show']);

Performance Considerations

🚀

Performance Tips

  • • Use V7 UUIDs (Uuid::v7()) for better database clustering
  • • Our HasUuids trait is 15% faster than Laravel's
  • • Index UUID foreign keys for optimal query performance
  • • Consider binary storage for 55% space savings (see Binary Storage docs)
// For maximum database performance
class User extends Model
{
use HasUuids;
 
public function newUniqueId(): string
{
// V7 UUIDs sort chronologically - better for database clustering
return (string) Uuid::v7();
}
}
 
// Migration with proper indexing
Schema::create('users', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('parent_id')->nullable();
$table->string('email')->unique();
$table->timestamps();
 
// Index foreign key columns
$table->index('parent_id');
 
// Composite indexes for common queries
$table->index(['parent_id', 'created_at']);
});

Advanced Model Patterns

Base Model with UUID

// Base model for all your UUID models
abstract class BaseUuidModel extends Model
{
use HasUuids;
 
public function newUniqueId(): string
{
return (string) Uuid::v7(); // Consistent across all models
}
}
 
// Use in your models
class User extends BaseUuidModel
{
protected $fillable = ['name', 'email'];
}
 
class Post extends BaseUuidModel
{
protected $fillable = ['title', 'content', 'user_id'];
}

Soft Deletes with UUIDs

use Illuminate\Database\Eloquent\SoftDeletes;
 
class User extends Model
{
use HasUuids, SoftDeletes;
 
protected $fillable = ['name', 'email'];
 
protected $dates = ['deleted_at'];
}
 
// Migration
Schema::create('users', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('name');
$table->string('email');
$table->timestamps();
$table->softDeletes(); // Adds deleted_at timestamp
});

Testing with UUIDs

// In your tests
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
 
class UserTest extends TestCase
{
use RefreshDatabase;
 
public function test_user_creation()
{
$user = User::create([
'name' => 'Test User',
'email' => '[email protected]'
]);
 
// Assert UUID was generated
$this->assertNotNull($user->id);
$this->assertTrue(Str::isUuid($user->id));
 
// Test route model binding
$response = $this->get("/user/{$user->id}");
$response->assertStatus(200);
}
 
public function test_uuid_relationships()
{
$user = User::factory()->create();
$post = Post::factory()->create(['user_id' => $user->id]);
 
$this->assertEquals($user->id, $post->user_id);
$this->assertEquals($user->name, $post->user->name);
}
}

Next Steps