Laravel で主キー（プライマリキー）を ULID で登録する方法

# 環境

Laravel Framework 8.54.0
PHP 8.0.9
docker-compose version 1.29.2
rorecek/laravel-ulid:^2.0

# 手順

## `rorecek/laravel-ulid`

https://github.com/rorecek/laravel-ulid

まずは、`rorecek/laravel-ulid`のインストールを行います。
ターミナルを開き、以下のコマンドを入力します。

```sh:ターミナル（sail）
$ ./vendor/bin/sail composer require rorecek/laravel-ulid:^2.0
```

```sh:ターミナル（composer）
$ composer require rorecek/laravel-ulid:^2.0
```

これで、インストールは完了しました。

## マイグレーション

次に、マイグレーションから編集していきます。
今回はユーザに関しての変更を行おうと思うので、
デフォルトの`database/migrations/2014_10_12_000000_create_users_table.php`を編集します。

ULIDは、<font color="#f00">__26 文字の文字列__</font>になります。
そのため、以下のように編集します。

```php:database/migrations/2014_10_12_000000_create_users_table.php
// ...
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
-           $table->bigIncrements('id');
+           $table->char('id', 26)->primary();
            // ...
        });
    }

    // ...
}

```

## モデル

最後にモデルを編集します。

オートインクリメントを無効にして、
`id`を ULID で生成するように修正します。


`trait`を用意してくれているので、
以下のように、変更すれば大丈夫です。

```php:app/User.php
// ...

use Rorecek\Ulid\HasUlid;

class User extends Authenticatable
{
    // ...

    use HasUlid;

    /**
     * IDが自動増分されるか
     *
     * @var bool
     */
    public $incrementing = false;
    
    /**
     * 自動増分IDの「タイプ」
     *
     * @var string
     */
    protected $keyType = 'string';

    // ...
}
```

これで、`id`が ULID で生成されます。

# 注意点

上記までの手順で大枠は解決なのですが、
主キー（プライマリキー）を`id`以外に設定しているときは上手く動作しません。

理由は、モデルの修正で使用した`Rorecek\Ulid\HasUlid`が影響しています。
中身を見てみると原因がハッキリします。

```php:vendor/rorecek/laravel-ulid/src/HasUlid.php
<?php
namespace Rorecek\Ulid;

trait HasUlid
{
    protected static function bootHasUlid()
    {
        static::creating(function ($model) {
            if (! $model->id) {
                $model->id = \Ulid::generate();
            }
        });

        static::saving(function ($model) {
            $originalUlid = $model->getOriginal('id');
            if ($originalUlid !== $model->id) {
                $model->id = $originalUlid;
            }
        });
    }

    public function getIncrementing()
    {
        return false;
    }

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

そうなんです。
`id`を直接指定してしまっているため、他のときは動かないようになってます。

## 解決方法

解決方法は、言葉にしてしまえば簡単ですが、
<font color="#f00">__主キー（プライマリキー）が何のときでも__</font>動くようにしてあげればよいだけですね。

```php:trait/HasUlid.php
<?php
use Rorecek\Ulid\Facades\Ulid;

trait HasUlid
{
    protected static function bootHasUlid()
    {
        static::creating(function (Model $model) {
            if (! $model->getKey()) {
                $model->setAttribute($model->getKeyName(), Ulid::generate());
            }
        });

        static::saving(function (Model $model) {
            $originalUlid = $model->getOriginal($model->getKeyName());
            if ($originalUlid !== $model->getKey()) {
                $model->setAttribute($model->getKeyName(), $originalUlid);
            }
        });
    }

    public function getIncrementing()
    {
        return false;
    }

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

    /**
     * IDが自動増分されるか
     *
     * @var bool
     */
    public $incrementing = false;
    
    /**
     * 自動増分IDの「タイプ」
     *
     * @var string
     */
    protected $keyType = 'string';
}
```

こんな`trait`を作成してあげれば、汎用的に使えると思います。

# さいごに

ということで、`rorecek/laravel-ulid`は
`\Rorecek\Ulid\Facades\Ulid::generate()`とするだけで簡単にULIDを生成できるだけでなく、
主キー（プライマリキー）をULIDに変更する最低限の下地を既に作成してくれている訳です。

主キー（プライマリキー）を特別なものへと変更するにあたって、
`created`や`saving`などの`hook`に引っ掛けて特定の処理を行うことは常套手段でしたが、
毎回記述するのがとても煩わしかったのを覚えています。

なので、それも含めて準備してくれるのはとてもありがたい！！

あなたの実装ライフが少しでも楽になりますように～