UUIDs in Laravel 7
Tags: laravel, database
Published: 2020-05-16 12:57:20

Recently I had to implement Universally unique identifiers (UUIDs) in Laravel 7, and ran into some problems that I hope this post clears up for others who are doing the same.

### High-level reasons for using UUIDs
A) They remove numbered IDs from your URLs, so users cannot see how many of a certain object your app has created. E.g.
```
https://myapp.com/api/users/5 
```
vs.
``` 
https://myapp.com/api/users/0892b118-856e-4a15-af0c-66a3a4a28eed
```
B) They make IDs a lot harder to guess. This is good for security, but we probably should be implementing other techniques to guard against this.

# Implementing UUIDs as primary keys
### How to change your database migrations
Firstly, you replace your current auto-incrementing integer ID fields with UUIDs in your database migrations. You could also follow the route of keeping auto-incrementing IDs and implementing UUIDs as an additional field on your tables that will be used when exposing URLs to users (in which case you make the ID field hidden in your models), but that is not what we are doing here. Let's see what this would look like for a hypothetical employees table.

```php
    public function up()
    {
        Schema::create('employees', function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('name');
            $table->string('email')->unique();
            $table->string('work_location')->nullable();
            $table->timestamps();
        });
    }
```

Here, notice we replaced the normal id(); declaration with uuid(); and made it the primary key.

### Let's make that a Trait
Next, we can implement a Laravel lifecycle hook to make sure that a UUID is assigned when a new instance of this model is created. We could code this directly in the model, but if you're going to use UUIDs on more than one model, I suggest using a Trait instead (Kudos to Wilbur Powery who I learned this from in [this](https://dev.to/wilburpowery/easily-use-uuids-in-laravel-45be) Dev post). A trait basically allows you to create functionality, and use that functionality in more than one model by calling it with the **use** keyword.

To create a new Trait, create a \App\Http\Traits\ folder (only my preference, you can put it somewhere else too), and also a new file for the Trait. We will call the file **UsesUuid.php**.

![FilePath](https://dev-to-uploads.s3.amazonaws.com/i/t4ficbg7guf2oonkwy4z.png)

Here is the code for the trait:

```php
<?php

namespace App\Http\Traits;

use Illuminate\Support\Str;

trait UsesUuid
{
  protected static function bootUsesUuid() {
    static::creating(function ($model) {
      if (! $model->getKey()) {
        $model->{$model->getKeyName()} = (string) Str::uuid();
      }
    });
  }

  public function getIncrementing()
  {
      return false;
  }

  public function getKeyType()
  {
      return 'string';
  }
}
```

Here, we use the \Illuminate\Support\Str class to easily generate UUIDs. The getIncrementing() method tells Laravel that the IDs on this model will not be incrementing (as we are returning false), and the getKeyType() method tells Laravel the primary key will be of type string. The bootUsesUuid() method allows us to tap the power of Laravel lifecycle hooks. You can learn more about those [here](https://laravel.com/docs/7.x/eloquent#events). Basically, our code is telling Laravel that when a new instance of this model is being created, generate a UUID primary key for it!

Now, we can easily implement this trait on a model with the **use** keyword.

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
...

class Employee extends Model
{
    ...
    use \App\Http\Traits\UsesUuid;
    ...
}
```
### Referencing UUIDs as foreign keys
To reference a UUID on a table as a foreign key, you simply change the type of the foreign key field on your table. For example, this...

```php
 Schema::create('another_table', function(Blueprint $table) {
        $table->id();
        $table->unsignedBigInteger('employee_id');
        $table->string('some_field');

        $table->foreign('employee_id')
            ->references('id')
            ->on('shifts')
            ->onDelete('cascade');
    });
```
... where we created a unsignedBigInteger to reference the employee_id foreign key, changes to this:

```php
 Schema::create('another_table', function(Blueprint $table) {
        $table->id();
        $table->uuid('employee_id');
        $table->string('some_field');

        $table->foreign('employee_id')
            ->references('id')
            ->on('shifts')
            ->onDelete('cascade');
    });
```

Easy as that! One more thing though...

### UUIDs and polymorphic relationships
You might find that your model gets referenced in polymorphic relationships, either by your own doing or by a package that you are pulling in. The table might look something like this in the migration:

```php
    public function up()
    {
        Schema::create('some_package_table', function (Blueprint $table) 
        {
            $table->bigIncrements('id');
            $table->morphs('model');
            ...
        }
    }
```
Here, the morphs() method is going to create two fields in your database, namely model_id of type unsignedBigInteger and model_type of type string. The problem is that our model is now using a UUID instead of the incrementing integer ID, so this is going to give you and error that says something like:

```error
Data truncated for column 'model_id' at row 1
```

We need the model_id field to now support our new UUID which is of type CHAR(36). Not to worry! Laravel makes this super easy, and you don't have to do that manually. Simply change the migration to this:

```php
    public function up()
    {
        Schema::create('some_package_table', function (Blueprint $table) 
        {
            $table->bigIncrements('id');
            $table->uuidMorphs('model');
            ...
        }
    }
```
Another reason to love Laravel! Happy coding!