【Laravel livewire】使う変数をプロパティで宣言するかViewに渡すか問題🔍

# 結論
『値が変動する物』はプロパティに宣言
『値が変動しない物』はviewに渡す

## そもそもの違い

プロパティで宣言してようがViewに渡そうがBlade内では同じように扱えますが全然違います。

### プロパティ
プロパティで宣言した変数はJsonでフロントエンドとバックエンドで同期を行います。

```php
<?php

namespace App\Http\Livewire;

use Livewire\Component;

class Sample0907 extends Component
{
    public $hoge = 'hogeValue';
    public $huga = 'hugaValue';
.......
```

```mermaid
sequenceDiagram
 participant View as フロント
 participant PHP as PHP
PHP ->> PHP: $hoge = 'hogeValue'<br/>$huga = 'hugaValue'
PHP ->> PHP: Jsonに変換
PHP ->> View:{"hoge" : "hogeValue", "huga" : "hugaValue"},
View ->> View:modelとして扱えるよう変換(仮説)
View ->>View:wire:model="hoge" <br/> wire:model="huga"
View ->> View:入力などで値を変更
View ->>PHP:{"hoge" : "hogeValue2", "huga" : "hugaValue2"},
PHP ->> PHP: PHP変数に変換
PHP ->> PHP:$hoge = 'hogeValue2'<br/>$huga = 'hugaValue2'
```
### Render内でviewに渡す

HTMLを生成する時にviewに渡しているだけです。
コントローラーなどで変数をbladeに渡しているのと同じです。
```php
return view('hoge',compact('hoge'))
```

## プロパティで宣言する時の注意点

### 配列が挿入順に回らない場合がある

例えば、このように配列をプロパティとして宣言するとします
```php
class Sample0907 extends Component
{
    public $array = [];

    public function mount()
    {
        $array = [
            2 => '1番目',
            0 => '2番目',
            1 => '3番目'
        ];

        $this->array = $array;
    }
```
Bladeで``dd()``してみます。
![スクリーンショット 2022-09-07 18.31.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/4647301b-675a-2057-f26f-40dcb0e8e502.png)
はい、勝手にソートされてます。

Livewireのソースを調べてみると、ソートしている箇所を見つけました。

```php:[Laravelプロジェクト]/vendor/livewire/livewire/src/HydrationMiddleware/NormalizeDataForJavaScript.php
        $itemsWithNumericKeys = array_filter($value, function ($key) {
            return is_numeric($key);
        }, ARRAY_FILTER_USE_KEY);
        ksort($itemsWithNumericKeys);
```

ですので、セレクトボックスの項目などを
```html
<select name="name" id="name">
@foreach($array as $key => $value)
  <option value="{{ $key }}">{{ $value }}</option>
@endforeach
</select>
```
のように書きたい場合、DBから取得したものを並べたい順にして生成しても
キーの連番に並び替えられます。

### オブジェクトは配列に変換される

このようにオブジェクト群を内包した配列と、入力用の変数を1つ用意します。

```php
class Sample0907 extends Component
{
    public $array = [];
    
    public $text;
    
    public function mount()
    {
        $array = [
            (object)['id' => 1, 'name' => '田中'],
            (object)['id' => 2, 'name' => '源田']
        ];

        $this->array = $array;
    }
```
Bladeも書きます
```html
<div>
    @foreach($array as $key => $value)
        {{ $value->name }}<br/>
    @endforeach
    <input type="text" wire:model="text" />
</div>
```

![スクリーンショット 2022-09-07 18.48.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/441ffd75-501f-76e9-5829-d7266a511584.png)

初回アクセスは問題ありません。

入力エリアに何か文字を入れてみましょう。
![スクリーンショット 2022-09-07 18.49.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/42b34b8b-0eb1-7064-4150-960f0bdb8c73.png)

はい、死にました。

どうやらLaravelのRequestの中でjsonが強制的に配列になるっぽいです

https://github.com/livewire/livewire/issues/2728
>@hulkurLivewire は stdObjects を再水和できないため、そのような動作が発生することを期待しています。そのため、それらは配列として終了します。その理由は、Laravel リクエスト オブジェクトです。json_decode を実行すると、json オブジェクトが強制的に配列になります。この問題を参照してくださいlaravel/framework#443

詳しくコードを追えてる訳では無いのですが

Modelオブジェクトや、Model群を内包しているコレクションは特別扱いされてるみたいです。
以下のコードには記載されてませんがcarbonオブジェクトもです。

<details><summary>src/HydrationMiddleware/HydratePublicProperties.php</summary><div>

```php
    protected static function dehydrateModel($value, $property, $response, $instance)
    {
        $serializedModel = $value instanceof QueueableEntity && ! $value->exists
            ? ['class' => get_class($value)]
            : (array) (new static)->getSerializedPropertyValue($value);

        // Deserialize the models into the "meta" bag.
        data_set($response, 'memo.dataMeta.models.'.$property, $serializedModel);

        $filteredModelData = static::filterData($instance, $property);

        // Only include the allowed data (defined by rules) in the response payload
        data_set($response, 'memo.data.'.$property, $filteredModelData);
    }

    protected static function dehydrateModels($value, $property, $response, $instance)
    {
        $serializedModel = (array) (new static)->getSerializedPropertyValue($value);

        // Deserialize the models into the "meta" bag.
        data_set($response, 'memo.dataMeta.modelCollections.'.$property, $serializedModel);

        $filteredModelData = static::filterData($instance, $property);

        // Only include the allowed data (defined by rules) in the response payload
        data_set($response, 'memo.data.'.$property, $filteredModelData);
    }

```

</div></details>


ですので、livewireでstdclassをプロパティとして扱うのはやめておきましょう。


### プロパティをカスタムしたい場合
ドキュメントに記載されてます。

https://laravel-livewire.com/docs/2.x/properties#wireable-properties