Laravel UUID v6.2.0
Binary UUID Storage (55% Space Savings)
webpatser@dev:
~/laravel-uuid/6.2.0
$ cat binary-storage.md
Binary UUID Storage - Laravel UUID v6.2.0
Save 55% database space by storing UUIDs as 16-byte binary data instead of 36-character strings. This guide shows you how to implement transparent binary UUID storage in Laravel.
💾
Storage Comparison
• String UUID: 36 characters = 36 bytes
• Binary UUID: 16 bytes = 16 bytes
• Space Savings: 20 bytes per UUID (55.6% reduction)
• Million records: ~20MB saved per UUID column
Basic Binary UUID Model
Use the HasBinaryUuids
trait for automatic binary storage:
use Illuminate\Database\Eloquent\Model;use Webpatser\LaravelUuid\HasBinaryUuids;use Webpatser\LaravelUuid\BinaryUuidCast; class User extends Model{ use HasBinaryUuids; // Automatic binary UUID support protected $casts = [ 'id' => BinaryUuidCast::class, // Auto-converts binary ↔ UUID 'parent_id' => BinaryUuidCast::class, // Handles nullable columns ]; protected $fillable = ['name', 'email'];} // Usage - works exactly like string UUIDsecho $user->id; // Displays as string: "550e8400-e29b-41d4-a716-446655440000"echo $user->getUuidAsString(); // Explicit string conversion$user->setUuidFromString($uuid); // Set from string UUID
Database Migration
Create tables with binary UUID columns using our migration helpers:
use Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Webpatser\LaravelUuid\BinaryUuidMigrations; return new class extends Migration{ public function up() { Schema::create('users', function (Blueprint $table) { // Creates optimal binary column for your database BinaryUuidMigrations::addBinaryUuidPrimary($table); // Add nullable binary UUID foreign key BinaryUuidMigrations::addBinaryUuidColumn($table, 'parent_id', true); $table->string('name'); $table->string('email')->unique(); $table->timestamps(); // Index for performance $table->index('parent_id'); }); }};
Database-Specific Implementation
Database
|
Column Type
|
Storage Size
|
---|---|---|
MySQL/MariaDB | BINARY(16) | 16 bytes |
PostgreSQL | bytea | 16 bytes |
SQLite | BLOB | 16 bytes |
SQL Server | uniqueidentifier | 16 bytes |
Generated SQL Examples
Here's what the migration helpers generate for each database:
-- MySQL/MariaDBCREATE TABLE users ( id BINARY(16) PRIMARY KEY, -- 16 bytes (55% savings!) parent_id BINARY(16) NULL, -- 16 bytes name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE); -- PostgreSQLCREATE TABLE users ( id bytea PRIMARY KEY, -- 16 bytes (55% savings!) parent_id bytea NULL, -- 16 bytes name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE); -- SQLiteCREATE TABLE users ( id BLOB PRIMARY KEY, -- 16 bytes (55% savings!) parent_id BLOB NULL, -- 16 bytes name TEXT NOT NULL, email TEXT NOT NULL UNIQUE); -- SQL ServerCREATE TABLE users ( id uniqueidentifier PRIMARY KEY, -- 16 bytes (native UUID support!) parent_id uniqueidentifier NULL, -- 16 bytes name NVARCHAR(255) NOT NULL, email NVARCHAR(255) NOT NULL UNIQUE);
Binary UUID Relationships
Binary UUIDs work seamlessly with Eloquent relationships:
class User extends Model{ use HasBinaryUuids; protected $casts = [ 'id' => BinaryUuidCast::class, ]; public function posts() { return $this->hasMany(Post::class); } public function parent() { return $this->belongsTo(User::class, 'parent_id'); }} class Post extends Model{ use HasBinaryUuids; protected $casts = [ 'id' => BinaryUuidCast::class, 'user_id' => BinaryUuidCast::class, ]; public function user() { return $this->belongsTo(User::class); }} // Usage - exactly like string UUIDs$post = $user->posts()->create(['title' => 'Hello', 'content' => 'World']);echo $post->user->name; // "John"
Route Model Binding
Binary UUIDs work transparently with Laravel's route model binding:
// routes/web.phpRoute::get('/user/{user}', function (User $user) { // Accepts string UUID in URL, finds binary UUID in database return view('user.profile', compact('user'));}); // URLs work with string UUIDs:// /user/550e8400-e29b-41d4-a716-446655440000 // Laravel automatically:// 1. Takes string UUID from URL// 2. Converts to binary for database query// 3. Returns User model if found
Direct Binary Operations
Use Str macros for direct binary UUID operations:
use Illuminate\Support\Str; // Generate binary UUIDs directly$binaryUuid = Str::fastBinaryUuid(); // 16 bytes v4$orderedBinary = Str::fastBinaryOrderedUuid(); // 16 bytes v7 // Convert between formats$stringUuid = '550e8400-e29b-41d4-a716-446655440000';$binary = Str::uuidToBinary($stringUuid); // Convert to 16 bytes$backToString = Str::binaryToUuid($binary); // Convert back to string echo strlen($binary); // 16echo strlen($stringUuid); // 36echo $backToString; // "550e8400-e29b-41d4-a716-446655440000"
Migration from String UUIDs
Convert existing string UUID tables to binary storage:
use Illuminate\Database\Migrations\Migration;use Webpatser\LaravelUuid\BinaryUuidMigrations; return new class extends Migration{ public function up() { // Get database-specific conversion SQL $conversionSql = BinaryUuidMigrations::getConversionSql('users', 'id'); Schema::table('users', function (Blueprint $table) { // Add new binary column $table->binary('id_binary', 16)->nullable(); }); // Convert existing data DB::statement($conversionSql); Schema::table('users', function (Blueprint $table) { // Drop old column, rename new one $table->dropColumn('id'); $table->renameColumn('id_binary', 'id'); }); } public function down() { // Reverse conversion available $reverseConversionSql = BinaryUuidMigrations::getReverseConversionSql('users', 'id'); // ... implement reverse conversion }};
Performance Benefits
💾 Storage Benefits
- • 55% smaller storage footprint
- • Faster table scans
- • Better memory utilization
- • Reduced backup sizes
⚡ Query Benefits
- • Faster index lookups
- • Better clustering (V7 UUIDs)
- • Improved join performance
- • Reduced network traffic
Real-World Example: User System
// Complete user system with binary UUIDsclass User extends Model{ use HasBinaryUuids; protected $casts = [ 'id' => BinaryUuidCast::class, 'parent_id' => BinaryUuidCast::class, ]; protected $fillable = ['name', 'email']; // Use V7 UUIDs for better database performance public function newUniqueId(): string { return (string) Uuid::v7(); } public function children() { return $this->hasMany(User::class, 'parent_id'); } public function posts() { return $this->hasMany(Post::class); }} // Migration for complete user systemSchema::create('users', function (Blueprint $table) { BinaryUuidMigrations::addBinaryUuidPrimary($table); BinaryUuidMigrations::addBinaryUuidColumn($table, 'parent_id', true); $table->string('name'); $table->string('email')->unique(); $table->timestamps(); $table->index('parent_id'); $table->foreign('parent_id')->references('id')->on('users');}); // Storage comparison for 1 million users:// String UUIDs: ~36MB for ID column alone// Binary UUIDs: ~16MB for ID column (55% savings)// Total savings: ~20MB per UUID column
Testing Binary UUIDs
// Test binary UUID functionalityuse Tests\TestCase;use Illuminate\Foundation\Testing\RefreshDatabase; class BinaryUuidTest extends TestCase{ use RefreshDatabase; public function test_binary_uuid_creation() { // UUID displays as string $this->assertTrue(Str::isUuid($user->id)); // But is stored as binary in database $binaryId = DB::table('users')->where('id', $user->getUuidAsBinary())->first(); $this->assertNotNull($binaryId); } public function test_route_model_binding() { $user = User::factory()->create(); // Test with string UUID in URL $response = $this->get("/user/{$user->id}"); $response->assertStatus(200); } public function test_relationships() { $parent = User::factory()->create(); $child = User::factory()->create(['parent_id' => $parent->id]); $this->assertEquals($parent->id, $child->parent_id); $this->assertEquals($parent->name, $child->parent->name); }}
⚠️
Important Considerations
- • Binary UUIDs are not human-readable in database tools
- • Some database admin tools may not display them correctly
- • Always use the cast classes for proper conversion
- • Test thoroughly in your specific database environment