I have a model with some typical columns and one json column. The Json is casted as array:
Model:
protected $casts = [
'client' => 'array'
];
In my Livewire component, I created the following validation rule
Livewire component:
protected $rules = [
'editing.name' => 'required',
...
'editing.client' => 'present|array',
'editing.client.*.tenant' => 'required',
];
I call the 'editModal' method where I type-hint the model and set a public property with it's attributes. Already filtered to the specific item.
Livewire component:
public function editModal(TokenCacheProvider $provider)
{
$this->editing = $provider;
$this->dispatchBrowserEvent('open-modal', ['modal' => 'edit']);
}
My blade is a simple boiler blade component:
Blade:
<div>
<x-input.group inline borderless for="name" label="Name" :error="$errors->first('editing.name')"/>
<x-input.text name="name" class="w-full" wire:model="editing.name" />
</div>
<div>
<x-input.group inline borderless for="name" label="Tenant" :error="$errors->first('editing.client.tenant')"/>
<x-input.text name="tenant" class="w-full" wire:model="editing.client.tenant" />
</div>
Once I load the page I get the following type exception
foreach() argument must be of type array|object, string given
This is because the client attribute is still a string as in the database. It should be an array as I casted it:
So, I don't understand why the client
attribute is still a string and not an array as casted.
Thank you
Well it's more a work-around than a solution but Daantje found an Livewire issue on Github which might explain this behavior.
I've changed the architecture from one to two public properties. One for the actual model and a second for the json column.
Livewire component (truncated)
public MyModel $editing; // the model
public array $client; // for the json attribute
protected $rules = [
'editing.name' => 'required',
...
'client.foo' => 'required',
'client.bar' => 'required',
'client.baz' => 'required',
...
];
public function editModal(MyModel $model)
{
$this->editing = $model;
$this->client = json_decode($model->client,true);
$this->dispatchBrowserEvent('open-modal', ['modal' => 'edit']);
}
public function save()
{
$this->validate();
$this->editing->client = json_encode($this->client);
$this->editing->save();
$this->dispatchBrowserEvent('close-modal', ['modal' => 'edit']);
$this->event('Saved', 'success');
}
Two blade input field examples:
<!-- ORM field(s) -->
<div>
<x-input.group inline borderless for="name" label="Name" :error="$errors->first('editing.name')"/>
<x-input.text name="name" wire:model="editing.name" />
</div>
<!-- Json field(s) -->
<div>
<x-input.group inline borderless for="foo" label="Foo" :error="$errors->first('client.foo')"/>
<x-input.text name="foo" wire:model="client.foo" />
</div>
Well, this works but as mentioned it's more a workaround