Laravelで複合キーに対応する方法
■はじめに
確か、以前やってた時は複合キーって扱えてたと思う
だけど・・・
ErrorException in Model.php line XXX: Illegal offset type in isset or empty
とか
Illegal offset type
が発生して、度々調べて
『複合キーはサポートされていない』
『サロゲートキーを使用しましょう』など
否定的な情報が多くて肝心な情報が埋もれていたので
備忘録として残しておきます。
■環境情報
Laravel 5.5
PHP 7.1
■Laravelで複合キー対応方法
基本対応
Modelに$fillableと$primaryKeyと$incrementingを設定
1 2 3 |
protected $fillable = ['id', 'sub_id', 'field1']; protected $primaryKey = ['id', 'sub_id']; public $incrementing = false; |
対応方法その1(検索条件を配列に纏める)
1 |
Model::where([ ['id', $id], ['sub_id', $sub_id] ])->update($data); |
この方法でも数が少なければ良いと思いますが
私は下記トレイトを使ったその2をお勧めします。
対応方法その2(トレイトを使ってオーバーライド)
元ネタはstackoverflowです。
下記メソッドをHasCompositePrimaryKeyなどの名前でTraitを作りましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?php namespace App; use \Illuminate\Database\Eloquent\Builder; trait HasCompositePrimaryKey { /** * Set the keys for a save update query. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ protected function setKeysForSaveQuery(Builder $query) { $keys = $this->getKeyName(); if(!is_array($keys)){ return parent::setKeysForSaveQuery($query); } foreach($keys as $keyName){ $query->where($keyName, '=', $this->getKeyForSaveQuery($keyName)); } return $query; } /** * Get the primary key value for a save query. * * @param mixed $keyName * @return mixed */ protected function getKeyForSaveQuery($keyName = null) { if(is_null($keyName)){ $keyName = $this->getKeyName(); } if (isset($this->original[$keyName])) { return $this->original[$keyName]; } return $this->getAttribute($keyName); } } |
作成したHasCompositePrimaryKeyを
複合キーのModelにuseする
1 2 3 4 5 6 7 8 9 10 11 |
use Illuminate\Database\Eloquent\Model; class ExampleModel extends Model { use HasCompositePrimaryKey; protected $fillable = ['id', 'sub_id', 'field1']; protected $primaryKey = ['id', 'sub_id']; public $incrementing = false; ... } |
これで、モデル以外は複合キーを意識しないでも操作出来るようになりました。
■終わりに
フレームワークのバージョンが変わると同じ方法が使えなくなったりするので
拡張やカスタマイズってあまりやりたくないんですよね
でも、今回の対応はかなり調査工数削減につながるのでお勧めです。
この情報が少しでも役立つと嬉しいです