F
Filament15mo ago
ChrisR

Updating state for laravel-model-states from a Filament form

(I created an SO post yesterday, which includes additional details: https://stackoverflow.com/questions/77233881/updating-model-state-laravel-model-state-from-a-filamentphp-livewire-form) If it helps, here is the error: https://flareapp.io/share/95JDOL4P I'm trying to allow changing state (implemented with spatie/laravel-model-states) from a FilamentPHP Resource. In order to get the state to integrate correctly, I had to implement Wireable. What that brings with it is expecting the saved data to be an array, as can be seen in Livewire\Features\SupportWireables\WireablyeSync.php -> hyrdate:
function hydrate($value, $meta, $hydrateChild) {
foreach ($value as $key => $child) {
$value[$key] = $hydrateChild($key, $child);
}

return $meta['class']::fromLivewire($value);
}
function hydrate($value, $meta, $hydrateChild) {
foreach ($value as $key => $child) {
$value[$key] = $hydrateChild($key, $child);
}

return $meta['class']::fromLivewire($value);
}
However, the value of $value received there is a string. I'm not sure if I'm implemented something incorrectly, or if I need to do additional work. I've tried looking for something that lets me manipulate the data which I haven't found. At this point I'm tossing around a couple ideas: - Do I need a custom Livewire component that displays the FilamentPHP form and has a method for changing state. - Should I try creating a custom action, and within EditInventoryItem add it to getHeaderActions - [open to suggestions]
Stack Overflow
Updating Model state (laravel-model-state) from a FilamentPHP/Livew...
I am using using spatie/laravel-model-states in a FilamentPHP (v3) project. In order to get the state to show correctly in the form, I had to implement Wireable in the state class, that looks like ...
Flare
foreach() argument must be of type array|object, string given - The error occurred at https://asset-tracker.lndo.site/dashboard/inventory-items/2/edit
14 Replies
awcodes
awcodes15mo ago
Without reading all this it sounds like you’re missing a cast somewhere and that value is coming out of the db as a string instead of an array.
ChrisR
ChrisROP15mo ago
Hi awcodes...the value is stored as a string (to my knowledge, that's how the laravel-model-states package does it out of the box, but I can investigate this) When the hydrate method is called, the existing value is sent as an array; however, the new value is sent as a string. I believe the cast to an array happens as a result of the implementation of Wireable:
public function toLivewire() {
return [
'name' => $this->getValue()
];
}

public static function fromLivewire($value) {
return new static($value);
}
public function toLivewire() {
return [
'name' => $this->getValue()
];
}

public static function fromLivewire($value) {
return new static($value);
}
I have been trying to attack this problem from the implementation of Wireable as well as within FilamentPHP but haven't gotten very far
awcodes
awcodes15mo ago
Wireable doesn’t cast. You tell it explicitly how to convert the data. Think of it as an attribute on a model. With getters and setters. When reading in convert this way. When reading out convert this other way. But the data from the db layer determines what data it has to mutate into the appropriate data structure.
ChrisR
ChrisROP15mo ago
Just noting here that this is what led me to trying Wireable: https://github.com/filamentphp/filament/issues/7465.
The implementation of the laravel-model-states calls for this cast on the Model:
protected $casts = [
'state' => InventoryItemState::class
];
protected $casts = [
'state' => InventoryItemState::class
];
If I remove Wireable, I get the error:
Property type not supported in Livewire for property: ["sanitized"]
But if I'm understanding you correctly I need to be doing something both before Filament gets the data, and before the Model tries to save. I've looked at the Wireable and now looking at Synthesizers
awcodes
awcodes15mo ago
Yea. Livewire needs to know how to handle the data. So you need to know how the data is coming out of the database. It may not even be a livewire issue. But could just be that the data coming out of the database isn’t a primitive type that livewire understands. But livewire understand arrays. So maybe take a step back, before getting into synths to make sure you even need one.
ChrisR
ChrisROP15mo ago
Ok, thanks awcodes! After poking around in the docs more, I found the available methods on EditRecord so I was able to do this:
class EditInventoryItem extends EditRecord
{
// ...
protected function handleRecordUpdate(Model $record, array $data): Model
{
if ($this->canTransitionState($record, $data)) {
$record->state->transitionTo($data['state']);
}
$record->update($data);

return $record;
}

protected function mutateFormDataBeforeFill(array $data): array {
if (!empty($data['state']) && $data['state'] instanceof State) {
$data['state'] = $data['state']->getValue();
}

return $data;
}
// ...
}
class EditInventoryItem extends EditRecord
{
// ...
protected function handleRecordUpdate(Model $record, array $data): Model
{
if ($this->canTransitionState($record, $data)) {
$record->state->transitionTo($data['state']);
}
$record->update($data);

return $record;
}

protected function mutateFormDataBeforeFill(array $data): array {
if (!empty($data['state']) && $data['state'] instanceof State) {
$data['state'] = $data['state']->getValue();
}

return $data;
}
// ...
}
I'm not convinced this is the/a proper way, but it works.
Thinking more...this almost certainly results in two db updates instead of one
djphinesse
djphinesse4mo ago
Hi, where do I put this? On the model?
Dennis Koch
Dennis Koch4mo ago
Yes, $casts belong to the model
djphinesse
djphinesse4mo ago
Sorry, I was referring to the wireable methods. Where do I put them since there’s no visible livewire class. The cast works in the table but when I try to edit I get a mismatch type error
Dennis Koch
Dennis Koch4mo ago
Laravel
Properties | Laravel
A full-stack framework for Laravel that takes the pain out of building dynamic UIs.
djphinesse
djphinesse4mo ago
I added this to my productresource:
public function toLivewire(): array
{
return [
'price' => Product::get('price'),
];
}

public static function fromLivewire($value): static
{
$price = $value['price'];

return new static($price);
}
public function toLivewire(): array
{
return [
'price' => Product::get('price'),
];
}

public static function fromLivewire($value): static
{
$price = $value['price'];

return new static($price);
}
djphinesse
djphinesse4mo ago
Still getting this when I click edit on my product form:
No description
Dennis Koch
Dennis Koch4mo ago
ProductResource is not the model. This is a Livewire thing, not Filament. Also please read our #✅┊rules and format your code properly
djphinesse
djphinesse4mo ago
I tried adding this code to my model but I still get the error.
Want results from more Discord servers?
Add your server