Using UUIDs with Laravel

Published Jan 15, 20204 min read 0 comments

Tags: laravel php

What is a UUID?

A UUID, short for Universally Unique Identifier, is a 128-bit number often used as a database primary key. It's often notated as a 32 character, hexadecimal string seperated in to five groups with hyphens. It has been widely adopted in a variety of applications since the chance of duplicating another of the same value is infinitesimally small.

UUIDs come with both pros and cons and your application should dictate whether or not to use them.

Some reasons in favor of using UUIDs
  • They're unique. UUIDs can be generated prior to a database insert which is beneficial in distributed systems or performing asynchronous actions.
  • They're random. Unlike auto-incrementing IDs, they do not expose the size of the database making it difficult brute force records by cycling through integers.
Some reasons against using UUIDs
  • They're computationally expensive. A binary UUID is 16 bytes vs. a standard 4 byte integer. Not only does storage increase, but indexes do as well.
  • They're not very readable. It's much easier to refer to a number such as 1234 than a long string such as "e626ed56-d4ea-4173-967a-a99ea57a945b."

Configuring your Eloquent model to use UUIDs

Laravel already comes with some built-in methods that support UUIDs out of the box. Assuming that you have already created a project, start by creating an Example model and corresponding migration.

Note: Code blocks that start with a shell prompt $ or artisan prompt >>> contain example output and shouldn't be copied verbatim.

php artisan make:model -m Example

This command creates two files, the model app/Example.php and the migration database/migrations/yyyy_mm_dd_hhmmss_create_examples_table.php. Start by updating the migration that creates the examples table.

File: database/migrations/*create_examples_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateExamplesTable extends Migration
{
    public function up()
    {
        Schema::create("preferences", function (Blueprint $table) {
            $table->uuid("id");
            $table->string("name");
            $table->timestamps();
            $table->primary("id");
        });
    }

    public function down()
    {
        Schema::dropIfExists("preferences");
    }
}

Now run the migration.

php artisan migrate

Rather than directly making the necessary changes to the model, proceed to create a Uuid trait that the model will use. The benefit to this approach is reusability. Any other model that you want to integrate with UUIDs can now use the same trait.

Create a Traits directory with a new file Uuid.php.

mkdir app/Traits

File: app/Traits/Uuid.php

<?php

namespace App\Traits;

use Illuminate\Support\Str;

trait Uuid
{
    protected static function bootUuid() {
        static::creating(function ($model) {
            $key = $model->getKeyName();
            $model->$key = (string) Str::uuid();
        });
    }

    public function getIncrementing() {
        return false;
    }

    public function getKeyType() {
        return "string";
    }

}

The trait will create a UUID before saving the record to the database and override the default mutators that define a primary key as an auto-incrementing integer. Now we can provide an alias in app/Example.php and insert it in the model class.

File: app/Example.php

<?php

namespace App;

use App\Traits\Uuid;
use Illuminate\Database\Eloquent\Model;

class Example extends Model
{
    use Uuid;

    protected $fillable = ["name", ];
    protected $hidden = ["created_at", "updated_at", ];
}

That's all there is to it. You can continue to use all of the same Eloquent methods as usual except numeric IDs have been replaced with UUIDs. If you want to see it in action, start a tinker console and create a new record.

php artisan tinker
>>> App\Example::create(["name" => "An example with a UUID"])
=> App\Example {#2996
     name: "An example with a UUID",
     id: "dfe5c0a0-a510-4840-9c59-638907f2a259",
   }

There are a couple of notes in closing:

  • Foreign keys must use the same data type as the primary key they reference. For example, a users table using UUIDs requires that the sessions.user_id column must also be a UUID if you're using the session database driver.
  • The MySQL implementation of UUID in Laravel is CHAR(36) and not BINARY(16). This adds significant weight to large tables and may cause performance degradation.
More Articles
Using Laravel Passport with AJAX requests
Using Laravel Passport with AJAX requests
Jan 3, 2020
Copyright © 2017-2023   Mark Brody | Blog