Laravel Eloquent の主キー(ID)を UUID に変更する

:::note info
2024.10.7 追記 Laravel11でUUIDに変更する記事を書きました。

https://qiita.com/ucan-lab/items/7e1edfb9c2b6aba13a15
:::

## UUID とは

Universally Unique Identifierの略。
重複する可能性が無視できるほど小さく、一意な識別子として扱えるIDです。

生成方法によりバージョン1からバージョン5までの種類があり、今回使用するのはバージョン4になります。

## UUID のメリット

- IDにID以外の意味を持たせないため
- 複数のデータベースで分散してデータを持つ場合に有用
- データを移行の際にもプライマリーキーの重複を避けられる
- URLからIDの予測が付きにくい

## 利用するパッケージ

- https://github.com/goldspecdigital/laravel-eloquent-uuid

今回はこちらのパッケージを利用してUUIDを使えるようにします。

## 環境

- PHP: 7.4.6
- Laravel: 7.25.0
- goldspecdigital/laravel-eloquent-uuid: 7.0.0

## 導入手順

```shell-session
$ composer require goldspecdigital/laravel-eloquent-uuid:^7.0
```

`goldspecdigital/laravel-eloquent-uuid` の `7.x` 系はLaravelのバージョン '7.x' に対応しています。

## 使い方

### users マイグレーションを修正する

```php:database/migrations/2014_10_12_000000_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            // Primary key.
            $table->uuid('id')->primary();
            
            // ...
        });
    }

    public function down()
    {
        Schema::dropIfExists('users');
    }
}

```

### posts マイグレーションを作成する

```shell-session
$ php artisan make:migration create_posts_table
```

```php
<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table): void {
            // Primary key.
            $table->uuid('id')->primary();
            
            // Foreign key.
            $table->uuid('user_id');
            $table->foreign('user_id')->references('id')->on('users');

            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
```

## マイグレーションを実行する

```shell-session
$ php artisan migrate
```

## User モデルを修正する

継承するクラスを `Illuminate\Foundation\Auth\User` から `GoldSpecDigital\LaravelEloquentUUID\Foundation\Auth\User` に変更します。

```php:app/User.php
<?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use GoldSpecDigital\LaravelEloquentUUID\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    // ...
}
```

## Post モデルを作成する

make コマンドが用意されているので利用します。

```
$ php artisan uuid:make:model Post
```

`GoldSpecDigital\LaravelEloquentUUID\Database\Eloquent\Model` を継承してモデルクラスを作成する方法が推奨されています。

```php:app/Post.php
<?php

namespace App;

use GoldSpecDigital\LaravelEloquentUUID\Database\Eloquent\Model;

class Post extends Model
{
}
```

## Post モデルファクトリーを作成

```shell-session
$ php artisan make:factory PostFactory
```

```php:database/factories/PostFactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Model;
use Faker\Generator as Faker;

$factory->define(Model::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence,
        'body' => $faker->text,
    ];
});
```

## tinkerでUUIDを生成する

```shell-session
$ php artisan tinker

$user = factory(App\User::class)->create();
=> App\User {#3197
     name: "Terrell Stroman",
     email: "ulehner@example.com",
     email_verified_at: "2020-08-19 13:30:17",
     id: "5e4e81de-45ae-468b-b33c-4c6109ded561",
     updated_at: "2020-08-19 13:30:17",
     created_at: "2020-08-19 13:30:17",
   }

$post = factory(App\Post::class)->create(['user_id' => $user->id]);
=> App\Post {#4124
     title: "Sed quisquam dignissimos facilis distinctio.",
     body: "Adipisci placeat sit ut nam sunt. Magni harum non sed eveniet tempore consequatur. Magnam quod qui omnis ducimus. Ex nulla occaecati adipisci. Itaque quas eum similique porro delectus accusantium.",
     user_id: "5e4e81de-45ae-468b-b33c-4c6109ded561",
     id: "7b19a935-357b-40f7-9be7-f69eb8eb6e30",
     updated_at: "2020-08-19 13:31:28",
     created_at: "2020-08-19 13:31:28",
   }
```

User, Post モデルのidにUUIDがセットされていることが確認できればokです。

## 参考

- https://laravel-news.com/eloquent-uuid
- https://ja.wikipedia.org/wiki/UUID
- http://nippondanji.blogspot.com/2013/12/id.html
- https://www.hypertextcandy.com/laravel-primary-key-uuid