Reputation: 1852
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 this:
abstract class InventoryItemState extends State implements Wireable {
public static function config(): StateConfig
{
return parent::config()
->default(Received::class)
->allowTransition(Received::class, Sanitized::class)
->allowTransition(Sanitized::class, Sold::class)
->allowTransition(Sold::class, Sanitized::class);
}
public function toLivewire() {
return [
'name' => $this->getValue()
];
}
public static function fromLivewire($value) {
return new static($value);
}
}
An example state looks like this:
class Received extends InventoryItemState {
public static $name = 'received';
}
Within the form I show the available states that this particular item can transition to:
namespace App\Filament\Resources;
class InventoryItemResource extends Resource
{
protected static ?string $model = InventoryItem::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
$potential_states = $form->getRecord()->state->transitionableStates();
// Some other stuff not relevant
$formSchema = [
// other form options
];
if (!empty($potential_states)) {
$options = collect($potential_states)
->keyBy(fn ($item) => $item)
->toArray();
$formSchema[] = Forms\Components\Radio::make('state')
->options($options);
}
$schema[] =
Section::make()
->compact()
->columns(2)
->schema($formSchema);
return $form
->schema($schema);
}
// Other stuff removed
}
This works to show the transitionable state as a radio button:
However, upon saving I get this error:
foreach() argument must be of type array|object, string given
This is coming from Livewire\Features\SupportWireables\WireableSynth.php
:
function hydrate($value, $meta, $hydrateChild) {
foreach ($value as $key => $child) {
$value[$key] = $hydrateChild($key, $child);
}
return $meta['class']::fromLivewire($value);
}
It's easy to see that the original value is an array:
And that the value the form is setting it to is not:
So WireableSynth::hydrate
clearly expects an array that it's not getting when the value is changed and the form is submitted. What is the correct way to do this?
Upvotes: 0
Views: 1148
Reputation: 1
I'm currently having a similar flow at my project and am also stuck on saving step. First of all, I think your implementation of 'fromLivewire' method is not good because constructor of State class expects model. There is a 'make' method which can be usefull here. Here is my state class. But here is another problem with calling 'make' method using 'static' keyword, code falls in error because of the check if state class is and subclass of parent state class and in this case both values are same. (if condition on screenshot)
<?php
namespace App\Models\States\Project;
use App\Models\Project;
use App\Models\States\Project\Transitions\ProjectCompleted;
use Livewire\Wireable;
use Spatie\ModelStates\Attributes\AllowTransition;
use Spatie\ModelStates\Attributes\DefaultState;
use Spatie\ModelStates\State;
#[
DefaultState(ToDo::class),
AllowTransition(from: ToDo::class, to: InProgress::class),
AllowTransition(from: InProgress::class, to: ToDo::class),
AllowTransition(from: InProgress::class, to: Done::class, transition: ProjectCompleted::class),
]
abstract class ProjectState extends State implements Wireable
{
protected static string $modelClass = Project::class;
public function label(): string
{
return ucwords($this->getValue());
}
public function toLivewire(): array
{
return [
'model' => $this->getModel()->id,
'value' => $this->getValue()
];
}
public static function fromLivewire($value): static
{
return self::make($value['value'], static::$modelClass::find($value['model']));
}
}
State class 'make' method code
Upvotes: 0