🆔

Laravel でテーブルの ID を ULID に指定する方法

2023/11/22に公開

こんにちは。 SAW です。
最近急に寒くなってきましたね。季節の変わり目で体調を崩さないように、体調管理には気をつけてくださいね。

Laravel の migration でテーブルを作成する際に、 Illuminate\Database\Schema\Blueprint::id() を利用することで、 連番の ID を生成します。
しかし、 ID を連番にすると、レコードが削除された場合に ID が歯抜けになり得ます。
また、1 つ目の参考文献 に挙げられているデメリットもあります。

連番の ID を用いるかわりに、 ULID を利用することで、ソート可能なランダムな文字列 を ID として扱えます。

本記事では、 Laravel でテーブルの ID を ULID に指定する方法を紹介します。
なお、本記事では Laravel のバージョンが 10.x 系 を前提としています。

対象読者

本記事で想定する読者層は次の通りです。

  • Laravel の基本的な知識を有している
  • データベースについての基本的な知識を有している

ULID とは

本章では、 UUID について説明したあと、 UUID と ULID を比較しながら、 ULID はどのようなものかを説明します。

UUID とは

UUID (Universally Unique IDentifier) とは、世界中で一意かつランダムな ID です。
同じ UUID が生成される確率は非常に小さい ため、生成される UUID は世界中で一意になることが保証されています。

UUID には複数のバージョンがあります。

  • UUIDv1: 時刻と MAC アドレスに基づいて UUID を生成
  • UUIDv2: DCOM (Distributed Component Object Model) のために予約されている
    • 一般的に利用されていないのが実情
  • UUIDv3: 名前と名前空間を MD5 でハッシュ化した値に基づいて UUID を生成
    • 同じ名前空間上で同じ名前であれば同じ UUID が生成可能
  • UUIDv4: 乱数に基づいて UUID を生成
    • 最も広く利用されている UUID のバージョン
  • UUIDv5: UUIDv3 のハッシュ関数を SHA-1 にしたもの

UUID と ULID の違い

ULID (Universally Unique Lexicographically Sortable Identifier) も UUID と同様に、世界中で一意かつランダムな ID です。

ULID が UUID と異なる主な点は次の通りです。

  • 生成された順にソート可能 である
    • 一般的に利用されている UUIDv4 はソートすると生成順にならない
  • 英数字の 36 文字でエンコーディングされた文字列で表現される
    • UUID は 32 桁の 16 進数で表現される

UUID/ULID を ID にすることで、 ID がランダムな文字列になり、連番のような ID の歯抜けを気にする必要がなく、 ID が推測されづらくなります。
また、 ULID を利用することで、 ID 順でレコードの生成順にソートできます。

Laravel で primary key を ULID にする方法

本章では、 Laravel で migration を行う際に、テーブルの primary key を ULID に指定する方法を紹介します。

migration file で ULID を指定する方法

Laravel では、テーブルのカラムの型に ULID を指定するためのメソッドとして、 Illuminate\Database\Schema\Blueprint::ulid() が用意されています。

ulid() を利用した場合、デフォルトのカラム名は ulid になります。
ulid() の引数に文字列を指定することで、引数の文字列がカラム名になります。

id() と異なり、 ulid() では自動的に PRIMARY KEY 制約が設定されません。
別テーブルから外部キーで参照する場合、 primary() で PRIMARY KEY 制約を付与 します。

テーブルの primary key を ULID に指定するコード例は以下の通りです。

migration ファイルで ULID の primary key を定義するコード
Schema::create('users', function (Blueprint $table) {
    $table->ulid('id')->primary(); # カラム id を ULID の primary key に指定
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
});

ULID の値をテーブルに流し込む方法

ULID の値をテーブルに流し込むには、 Illuminate\Support\Str::ulid() を利用します。

Laravel で用意されている UserFactory を用いて User のインスタンスを生成する際に ULID の値を流し込む方法は次の通りです。

database/factories/UserFactory.php
public function definition(): array
{
    return [
        'id' => Str::ulid(), # id に ULID の値を登録
        'name' => fake()->name(),
        'email' => fake()->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => static::$password ??= Hash::make('password'),
        'remember_token' => Str::random(10),
    ];
}

Str::ulid() が実行されるたびに、異なる ULID が都度生成されます。
そのため、上記の factory から生成された User のインスタンスをテーブルに流し込むと、 ID はそれぞれ異なる ULID が格納されます。

まとめ

本記事のまとめは次の通りです。

  • ULID とは世界中で一意でランダムかつソート可能な ID である
  • migration file で ulid() を利用することで ULID のカラムを作成できる
  • Str::ulid() で ULID の値を生成できる
    • seeder や factory 経由でテーブルに ULID の値を格納できる

あらためて Laravel には様々な機能が準備されていて便利だと実感しました。

参考文献

https://zenn.dev/praha/articles/3c84e3818891c3
https://medium.com/@capitansec/uuid-specifications-978d71c383d4
https://datatracker.ietf.org/doc/html/rfc4122
https://github.com/ulid/spec
https://zenn.dev/nshiro/articles/07b1e4834b9214
https://laravel.com/docs/10.x/strings#method-str-ulid

Discussion