02, nov 2022

UUID como primary key em Laravel

Uma das coisas que normalmente temos problemas quando uma api é consumida por muitos lados ou quando é preciso garantir que dados não sejam reescritos de diversas formas ou quando precisamos trabalhar com “offline-first” apps é garantir que as primary keys não sejam duplicadas.

Tem várias formas de fazer isso, garantindo que os mesmo dados não estão já lá no banco de dados antes de inseri-los novamente, ter ids separadas em cada aplicação sem distinção, usar bancos diferentes para diversos clientes, etc.

Mas uma delas, que é muito boa, é fazer o uso de unique identifiers. E usando o Laravel, isso se torna ainda mais fácil!

Para começar, crie uma Trait em um lugar oportuno (por exemplo, dentro de app/Models/Traits. No nosso caso vamos chama-la de HasUuid.

Após isso adicione o seguinte código dentro dela.

<?php

namespace App\Models\Traits;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

trait HasUuid
{
    public function getIncrementing()
    {
        return false;
    }

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

    protected static function boot()
    {
        parent::boot();

        static::creating(
            function (Model $model) {
                if (!$model->getKey()) {
                    $model->setAttribute($model->getKeyName(), Str::uuid()->toString());
                }
            }
        );

        static::saving(
            function ($model) {
                $primaryKey = $model->getOriginal('id');

                if ($primaryKey !== $model->id) {
                    $model->id = $primaryKey;
                }
            }
        );
    }
}

Agora, vamos criar um novo Model para usar nossa Trait.

php artisan make:model Product -m

o -m é para criarmos também uma migration para a tabela no banco de dados. Na migration vamos apenas criar um campo para o nome do produto e alterar o tipo da primary key.

<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            // $table->id();
            $table->uuid('id')->primary();
            $table->string('name', 200);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
};

Agora, tudo que precisamos fazer é alterar nosso model para usar a Trait que criamos. Lembre de rodar php artisan migrate!

<?php

namespace App\Models;

use App\Models\Traits\HasUuid;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory, HasUuid; // <-- adicione a Trait!
}

Agora, se você criar um novo produto usando a model que criamos vai ter um resultado parecido com o debaixo:

array:4 [▼ // routes/web.php:24
  "name" => "Product Amazing!"
  "id" => "4e9aa680-00f7-4408-aadb-71a94b047097"
  "updated_at" => "2022-11-02T10:06:07.000000Z"
  "created_at" => "2022-11-02T10:06:07.000000Z"
]

E é isso gente! Espero que isso tenha ajudado de alguma forma! Se desejar, pode fazer o download direto do meu gist no github. Sucesso sempre e good coding! 😉