2018.04.03

LaravelでプライマリーキーをUUIDにする


Laravelでは各テーブルのプライマリーキーがAUTO_INCREMENT、つまり自動連番であることを前提にModelクラスがデフォルト設定されていますが、この設定は簡単にカスタマイズすることができます。

UUIDのメリット

はじめに、自動連番ではなくUUIDをプライマリーキーとして使う理由について簡単にコメントします。

自動連番の代わりにUUIDを使うと、複数のデータベースで分散してデータを持つ場合やデータを移行する場合にもプライマリーキーの重複を避けられます。

また、多くの場合URLの一部にIDを使用しますが、私が担当した案件でクライアント様から「IDの予測がつきにくくしてほしい」という要望を受けたこともありますし(/users/123 があるなら122番ユーザーもいるとバレる)、別の案件では新規サービス開発時に「投稿IDが3とかだったらまだ使われてない感が出て格好悪い」と言われたこともあります(確かに…)。

逆にどうしても連番でなくてはならないという要件には出会ったことがありません。

そんなわけで私は新規開発の場面では特別指定がなければ大体UUIDで作るようにしています。

参考:https://siguniang.wordpress.com/2013/03/17/stop-using-numbers-as-ids-just-use-uuids/

本記事ではLaravelでプライマリーキーをUUIDにする方法を紹介します。

前提

マシン Homestead Laravel PHP
macOS 10.12.6 7.3.0 5.6.12 7.2

実装

マイグレーション

マイグレーションファイルでは以下のように記述します。

$table->string('uuid', 36)->primary();

「uuid」という文字列型36桁でプライマリーキーが設定されたカラムを作成する記述です。UUID(v.4)はハイフン含めて必ず36桁なので桁数を制限しています。

Modelクラス

続いてModelクラスを作成します(編集内容はどのModelクラスでも同じです)。

Post.php(架空のモデル)
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Ramsey\Uuid\Uuid;

class Post extends Model
{
    // プライマリーキーのカラム名
    protected $primaryKey = 'uuid';

    // プライマリーキーの型
    protected $keyType = 'string';

    // プライマリーキーは自動連番か?
    public $incrementing = false;

    // コンストラクタを追加
    public function __construct(array $attributes = [])
    {
        parent::__construct($attributes);

        // newした時に自動的にuuidを設定する。
        $this->attributes['uuid'] = Uuid::uuid4()->toString();
    }
}

まず、3つのプロパティを設定します。

  • $primaryKey:プライマリーキーのカラム名
  • $keyType:プライマリーキーの型
  • $incrementing:プライマリーキーは自動連番か?

それぞれ、上記マイグレーションファイルで指定した通りに値を定義します。

ちなみにデフォルト値は以下の通りです。

/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
    /**
     * The primary key for the model.
     *
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * The "type" of the auto-incrementing ID.
     *
     * @var string
     */
    protected $keyType = 'int';

    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = true;

ここまでのプロパティの記述は必須ですが、コンストラクタの追加は任意です。ただ、いちいちModelのインスタンスを作るたびにuuidをセットするのは面倒ですよね?

私は UuidModel のようなベースクラス(またはトレイト)を作成してプロパティの設定とコンストラクタをまとめてしまって、各Model側でプライマリーキーがUUIDであればそれを継承(または使用)するという作りにします。

UuidModel.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;


abstract class UuidModel extends Model
{
    // プライマリーキーのカラム名
    protected $primaryKey = 'uuid';

    // プライマリーキーの型
    protected $keyType = 'string';

    // プライマリーキーは自動連番か?
    public $incrementing = false;


    public function __construct(array $attributes = [])
    {
        parent::__construct($attributes);

        // newした時に自動的にuuidを設定する。
        $this->attributes['uuid'] = Uuid::uuid4()->toString();
    }
}
Post.php(架空のモデル)
<?php

namespace App\Models;


class Post extends UuidModel
{
    // スッキリ!
}

以上、LaravelでプライマリーキーをUUIDにする方法を紹介しました。