1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

livewireのpublic変数にModelを入れるとIDが照合される問題

Last updated at Posted at 2022-10-04

タイトルは厳密には間違っています。
どちらかというと、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はそれぞれ↓のように作成しました。

Item.php(Component)
<?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');
    }
}
product.blade.php(blade)
<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>

これで、下記のような動作が実装できました。
Image from Gyazo

↑の問題点

これだけ見ると問題のないようにも思えますが、上記の場合、フォームに「3」を入力した以降にフォームの値が更新されると404が返ってくるようになってしまいます。
Image from Gyazo

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 ?? []);
    }
1
0
3

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?