LaravelはWeb職人好みの使いやすいフレームワークです。

Laravelが初期装備しているEloquent ORMはコレクション機能と親和性が高く、組み合わせて利用するメリットは大きいのですが、苦手なものも当然もあります。

その一つが「主キーの変更が苦手」という点です。

Eloquentで開発した結果、主キーは変更せず規約に従うのがベター という結論に至りましたので、主キー周りについて私が理解したことをまとめます。

環境は以下の通りです。

  • PHP 7
  • Laravel 5.3

Eloquent ORMの主キー規約

Eloquent ORMでは次のように主キーに対する規約を設けております。

  • 符号なしINT (unsigned int)
  • フィールド名はid
  • オートインクリメント

この規約に従う場合に限り、とてもシンプルにコードを書くことができます。

スキーマビルダー

規約に従った主キーを、マイグレーションのスキーマビルダーで定義する場合は次のように指定します。

Schema::table('users', function ($table) {
    $table->increments('id');
});

モデル

モデルの生成後にすることは、テーブル名の定義くらいですね。

class Book extends Model
{
    protected $table = 'users';
}

主キーが文字列のケース

主キーを[code varchar(32)]に変更します。

スキーマビルダー

Schema::table('books', function ($table) {
    $table->string('code');
    $table->primary('code');
});

普通にフィールドを定義しprimary()で主キーとして定義します。

モデル

class Book extends Model
{
    protected $table      = 'books';
    protected $primaryKey = 'code';
    public $incrementing  = false;
    protected $casts      = [
        'code' => 'string',
    ];
}

主キーのフィールド名がidと異なる場合、$primaryKeyプロパティをオーバーライドしてそれをEloquentに教えてあげます。

また、オートインクリメントを利用しない場合は$incrementingプロパティをfalseにします。
このプロパティがpublicなことはちょっとした落とし穴です。

さらに、文字列が主キーの場合は$castsを定義します。

これは親クラス内のgetCasts()で明示的にキャストを行っているためです。

return array_merge([
  $this->getKeyName() => $this->keyType,
], $this->casts);

このケースで$castsを明示しなかった場合、DBからSelectした値がintにキャストされ、0になってしまう という異常事態が発生してしまいますよ。
(まさにPHPという感じですね。)

複合主キーを利用する

定義はきますが、save()できず実用性はありません

スキーマビルダー

Schema::table('user_cards', function ($table) {
    $table->integer('user_id')->unsigned();
    $table->integer('card_no')->unsigned();
    $table->primary(['user_id', 'card_no']);
});

復号キーのため、配列をprimary()に与えます。

モデル

class UserCard extends Model
{
    protected $table      = 'user_cards';
    protected $primaryKey = ['user_id', 'card_no'];
    public $incrementing  = false;
}

今までと同様、$primaryKeyに配列を与え、$incrementingをfalseに設定します。

save()できない

こうして複合主キーを持つテーブルを作ることは、一応可能です。
しかしながら、save()の実行時に次のようなエラーが発生してしまうため実用には堪えません。

ErrorException in Model.php line 1702: Illegal offset type in isset or empty

INSERTは問題ありませんが、UPDATEを実行する場合エラーが発生します。

どうやら、UPDATEのWHEREではリテラルを期待しているにもかかわらず、配列が渡されるためエラーになってしまうようです。

回避方法は一応あるようですが、根っこに近いっ所にパッチを当てるのは避けたいところです。

サロゲートキーを使うのが一番

複合主キーを利用する方法は一応あるようですが、ここはおとなしくサロゲートキー(代理キー)を利用するのが一番です。

規約に乗っかるためにサロゲートキーは用意し、復号主キーに充てるつもりだったナチュラルキーはユニークに設定する感じです。
テーブル上ではキーが一つ増えますが、勝手に生成されて勝手にUPDATEのキーに利用されるため、コード上でこのサロゲートキーを意識することはそれほどないと思います。

おわりに

Eloquent ORMは十分使えるORMですが、アクセス数が膨大になることが予測されるプロジェクトは場合は別途手段を考えたほうが良いかなという印象を受けました。
それを考えてもLaravelを使うメリットは十分にありますよ。

この記事はtomita@atuwebがお届けしました。

参考

Laravel 5.3 Eloquent
https://readouble.com/laravel/5.3/ja/eloquent.html

Laravel 5.3 マイグレーション
https://readouble.com/laravel/5.3/ja/migrations.html


スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
スポンサーリンク
ad_336
コメントの入力は終了しました。