LoginSignup
0
0

More than 3 years have passed since last update.

Laravel5.7-複合キーの罠と解決策

Last updated at Posted at 2019-11-19

はじめに

Laravel5.7のEloquentを使ってDB(MySQL)の更新をするときに、複合キーが原因で詰まりまくったので解決法を書きます。

何が起きた

弊プロジェクトではデータの更新の際は

FooController.php
// リクエストされたIDの行のインスタンスを取得
$table = TableA::where('ID', $request['id'])->lockForUpdate()->first();

// プロパティに値をセット
$table->NAME = 'taro';
$table->TEL = '00-0000-0000';

// 保存
$table->save();

上記のように、
- 1行に特定してインスタンスを取得
- プロパティに値をセット
- saveメソッドで保存
という順序で保存するようにしています。

しかしこれが有効なのは主キーが1つの場合のみです。

TableAIDDEMO_IDからなる複合キーの場合・・・

FooController.php
// リクエストされたIDとDEMO_IDの行のインスタンスを取得
$table = TableA::where('ID', $request['id'])
    ->where('DEMO_ID', $request['demo_id'])
    ->lockForUpdate()
    ->first();
    ///// 以下略/////

上記の方法で保存すると、モデルの$primaryKeyに設定したキーが優先されます。
どういうことかというと、$primaryKey = 'ID'としていた場合は
IDとDEMO_IDを指定してインスタンスを取得したにも関わらず、IDが一致する行を全て更新する
処理が走ります。不思議なのは、$tableにはちゃんと指定した1行のみが取得されていることです。よくわからなくてつらいです。

解決策

この問題を解決するには、インスタンスを一旦変数にセットするのではなく、
where句で条件を指定してそのまま更新する」必要があります。
(...ググりまくった結果、トレイトなるものを使用して解決できるようですが、設定をいじくる勇気はないので保留です)

FooController.php
TableA::where('ID', $request['id'])
    ->where('DEMO_ID', $request['demo_id'])
    ->lockForUpdate()
    ->update([
        'NAME' => 'taro',
        'TEL'  => '00-0000-0000',
    ]);

このようにwhere句で行を特定し、そのままupdateメソッドで更新すればちゃんと1行のみ更新できます。

まとめ

複合キーの一番手軽な解決策だと思います。
ちなみに…モデルの$primaryKeyにキーを配列でセットしてみたけどダメでした。

0
0
0

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
0
0