タイトルは厳密には間違っています。
どちらかというと、ComponentがJSと裏側でやり取りしているデータをModelとしてリストアする際、「id」というキーを元にDBから再取得をしていることで生じる問題です。
例
例えばEC系のシステムで、商品規格IDをフォームに入力すると該当する情報を閲覧できる機能をLivewireで作ったとします。
テーブルと商品データはおおまかに下記のようなものとし、
products(商品テーブル)
id | name |
---|---|
1 | 商品1 |
2 | 商品2 |
product_classes(商品規格テーブル)
id | product_id | color | price |
---|---|---|---|
1 | 1 | black | 100 |
2 | 2 | black | 150 |
3 | 2 | white | 200 |
また、Componentとbladeはそれぞれ↓のように作成しました。
<?php
namespace App\Http\Livewire;
use App\Models\Product;
use App\Models\ProductClass;
class Item extends \Livewire\Component
{
public $product_class_id = ''; // 商品規格ID
public $product; // 商品データ
// product_class_id更新時の処理
public function updatedProductClassId()
{
$this->product = Product::join('product_classes','products.id','=','product_classes.product_id')
->where( 'product_classes.id', $this->product_class_id )
->first();
}
public function render()
{
return view('product');
}
}
<div>
<input wire:model="product_class_id" type="text">
<div>name : {{ $product?->name }}</div>
<div>color : {{ $product?->color }}</div>
<div>price : {{ $product?->price }}</div>
</div>
↑の問題点
これだけ見ると問題のないようにも思えますが、上記の場合、フォームに「3」を入力した以降にフォームの値が更新されると404が返ってくるようになってしまいます。
404の原因
これは冒頭で書いた、Component<->JS間でやり取りしているデータをLivewireがModelへリストアする際に発生します。
今回の場合、$product
に入っているModelはApp\Models\Product
なのに対し、
idに入っている値はjoinによって別テーブルから上書きされて3になっています。
App\Models\Product {#1334 ▼
~~~
#attributes: array:7 [▼
"id" => 3 // ← productsテーブルにidが3のレコードは存在しない
"name" => "商品2"
"product_id" => 2
"color" => "white"
"price" => 200
]
~~~
}
Livewireは上記データを元にproductsテーブルからidが3のデータをfirstOrFail()
で取得しようとするも、見つからずにModelNotFoundException
の例外が投げられるため404が返されることになります。
やり取りしているデータ形式の判別やModelへの復元は下記で行っているみたいです。(多分)
HydratePublicProperties::hydrate()
↓
HydratePublicProperties::hydrateModel()
↓
SerializesAndRestoresModelIdentifiers::getRestoredPropertyValue()
↓
SerializesAndRestoresModelIdentifiers::restoreModel() ←firstOrFail()
はこの中
回避策
コメントでいただきました。
感謝(*´∀人)
https://qiita.com/ast_and_kaya/items/519d4a43a769b9385b7b#comment-caea2287bfb3ef43a726
おまけ
104行目~の処理って↓みたいにしてnullを返すと何か問題あるんですかね?
わけのわからない404は回避出来ても、結局Modelへの復元は仕組みとして出来なさそう……?
public function restoreModel($value)
{
return $this->getQueryForModelRestoration(
(new $value->class)->setConnection($value->connection), $value->id
- )->useWritePdo()->firstOrFail()->load($value->relations ?? []);
+ )->useWritePdo()->first()?->load($value->relations ?? []);
}