概要
Laravelのモデルでリレーションを定義した場合、一度リレーション先のモデルを参照するとその参照が保持される。
このことはデータを参照するだけであれば問題ないが、データを更新した場合、古いリレーション先を参照することになってしまう。
※マニュアルに書かれていることではないため、もし詳しい方がいらっしゃいましたらコメントください
サンプル
例えば以下のようにusersテーブルとitemsテーブルがbelongsTo, hasOneの関係にある場合、$user->item->name
で「机」を取得することができる。
しかし、その後に$user->item_id = 2; $user->save();
としても$user->item->name
は「机」のままとなってしまう。
usersテーブル
id | item_id | name |
---|---|---|
1 | 1 | Taro |
2 | 2 | Jiro |
itemsテーブル
id | name |
---|---|
1 | 机 |
2 | イス |
サンプル1
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class IndexController extends Controller
{
public function index(Request $request): View
{
$user = User::find(1);
echo $user->item->name; // 机
$user->item_id = 2;
$user->save();
echo $user->item->name; // 机 ※イスではない
return view('index.index');
}
}
サンプル2(条件式で参照)
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class IndexController extends Controller
{
public function index(Request $request): View
{
$user = User::find(1);
if ($user->item->name !== 2) { // ここで机を参照しているので、保持される
$user->item_id = 2;
$user->save();
echo $user->item->name; // 机 ※イスではない
}
return view('index.index');
}
}
解決方法
以下の方法が考えられる。
- モデルのloadメソッドでリレーションをEagerロードする。
- リレーション先のモデルを参照するのではなく、外部キーとなっているカラムの値を参照する。
サンプル1
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class IndexController extends Controller
{
public function index(Request $request): View
{
$user = User::find(1);
echo $user->item->name; // 机
$user->item_id = 2;
$user->save();
$user->load('item'); // Eagerロード
echo $user->item->name; // イス
return view('index.index');
}
}
サンプル2
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class IndexController extends Controller
{
public function index(Request $request): View
{
$user = User::find(1);
if ($user->item_id !== 2) { // 外部キーを参照
$user->item_id = 2;
$user->save();
echo $user->item->name; // イス
}
return view('index.index');
}
}
参考サイト
Attention Required! | Cloudflare
コメント