LoginSignup
23
32

More than 5 years have passed since last update.

Laravel5.7における複合プライマリキーを持つテーブルへの挿入と更新

Last updated at Posted at 2018-11-12

Laravelと複合プライマリキー

既存の複合プライマリキーを持つMySQLのテーブルに対する操作で混乱したので備忘録。
複合キーはLaravelでは有名なネタのようですが、幸いにもこれまで対応する機会はありませんでした。
事情によりサロゲートキーは導入できない前提です。

mpywさんよりコメントで便利なトレイトを教えていただいたので、公開半日もせず大幅に改稿しました。

HasCompositePrimaryKey トレイトの導入

mpywさんより教えていただいたトレイトです。
https://github.com/mopo922/LaravelTreats/blob/master/src/Model/Traits/HasCompositePrimaryKey.php

含まれている LaravelTreats をまるごとインストールします。

$ composer require mopo922/laravel-treats

Model にトレイトを挿入します。

KeywordItem.php
class KeywordItem extends Model
{
    use \LaravelTreats\Model\Traits\HasCompositePrimaryKey;

    /**
     * モデル主キー
     * @var int
     */
    protected $primaryKey = [ 'keyword_id', 'item_id' ];

    /**
     * 複数代入する属性 (データの代入が必須)
     *
     * @var array
     */
    protected $fillable = [ 'keyword_id', 'item_id' ];
}

これで動いてくれたら嬉しいコードで問題なく挿入・更新ができるようになりました。

// new OK, update OK
$new = KeywordItem::firstOrNew([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ]);
$new->status = $status;
$new->save();

わーい!!

追記。
https://github.com/mopo922/LaravelTreats/issues
現在、chunkまわりで不具合もあるようなので利用時は気をつけてください。

HasCompositePrimaryKey トレイト導入前の解決法

以下は、トレイト導入前の投稿となります。

Modelの定義は以下の通り。

KeywordItem.php
class KeywordItem extends Model
{
    /**
     * モデル主キー
     * @var int
     */
    protected $primaryKey = [ 'keyword_id', 'item_id' ];

    /**
     * increment無効化
     */
    public $incrementing = false;

    /**
     * 複数代入する属性 (データの代入が必須)
     *
     * @var array
     */
    protected $fillable = [ 'keyword_id', 'item_id' ];
}

新規、更新ともに成功。

// new OK, update OK
$new = KeywordItem::where([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ]);
if ($new->count() === 0) {
    $new = new KeywordItem([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ]);
    $new->status = $status;
    $new->save();
}
else {
    $new->update(['status' => $status]);
}

新規はうまくいくものの、更新した値が反映されない。

// new OK, update NoUpdate
$new = KeywordItem::where([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ])->first();
if (is_null($new)) {
    $new = new KeywordItem([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ]);
    $new->status = $status;
    $new->save();
}
else {
    $new->update(['status' => $status]);
}

新規はうまくいくものの、更新で例外エラーが発生。

// new OK, update Error
$new = KeywordItem::firstOrNew([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ]);
$new->status = $status;
$new->save();

新規、更新ともに例外エラーが発生。ただし新規の例外はINSERT後となる。

// new Error(but after insert), update Error
$new = KeywordItem::firstOrCreate([ 'keyword_id' => $keyword_id, 'item_id' => $item_id ]);
$new->status = $status;
$new->save();

新規は挿入されないし、更新も値が反映されない。

// new NotInsert, update NoUpdate
KeywordItem::updateOrCreate(
    ['keyword_id' => $keyword_id, 'item_id' => $item_id],
    ['status' => $status]
);

以上です。
なんだか、直接クエリ書く方がシンプルな気がしてきます。

23
32
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
32