F
Filament2y ago
flex

How to handle records of different "type" and different fields depending on the type?

Hey! I have a simple model called "Visual". It has the fields name (string), type (enum) and settings (json). In a CMS the record should represent any type of visual objects like an image, a slider, a video, a chart, a pdf, whatever.. Each type has completely different fields which should be stored into settings column. So for example image just have a FileUpload. A slider has multiple FileUploads, a chart may have just a Codefield to insert a iframe-code or whatever... How can I use different fields depending on the selected type in the Select for "type"? To may make it little bit more challenging (for me): For each of those visual types we have packages to only install only the types we need for the website. The packages register them self in a VisualTypeRegistry. A VisualType object has a getKey() ``getLabel() and getSchema() function. Building the type Select works well depending on the key and the label. But now I don't know how to tell filament to use the getSchema() for the selected type.
12 Replies
Dan Harrin
Dan Harrin2y ago
I would use a Builder field for this it uses 1 json object instead of multiple visual models
flex
flexOP2y ago
But at the end if have may 20 images, 5 sliders and 7 youtube videos. So I would need 32 visual records or do you mean adding a builder field with maxItems(1) and minItems(1) and each visual type will represent one block?
Dan Harrin
Dan Harrin2y ago
a block is Image, Slider, YoutubeVideo your JSON contains an array of x items of these types
flex
flexOP2y ago
1 record need to represent 1 visual so I could solve this problem with a builder with using min/max Items 1 but feels like a workaround
Dan Harrin
Dan Harrin2y ago
does it need to though? im just talking about the UI here really, the builder was made for this
flex
flexOP2y ago
The problem is, that I want to re-use for example one visual on may 5 different pages. They should be re-usable. I love the builder. The whole CMS and the building of the pages itself depend on the builder.
Dan Harrin
Dan Harrin2y ago
so you were planning on using a repeater or something to handle the different reusable blocks? i understand what you mean by maxItems now you could use a Select and then a Grid/Group/Fieldset or something to dynamically generate field based on that
Group::make()->schema(function ($get) {
$select = $get('select name');

return array of fields based on the select.
})->statePath('settings')
Group::make()->schema(function ($get) {
$select = $get('select name');

return array of fields based on the select.
})->statePath('settings')
on the Select, you need ->reactive()->afterStateUpdated(fn ($component) => $component->getContainer()->getComponents()[1]->getChildComponentContainer()->fill()) to ensure that the group is reinitialised when it changes (assuming the Group is the second field in the array, you might need to change 1 to a different index)
flex
flexOP2y ago
@Dan Harrin I think thats definitely the direction I need to go. Unfortunately Group complains about the Closure in the make(). I'm doing something wrong here? return $form ->schema( [ Forms\Components\TextInput::make('name') ->label('Name') ->required(), Forms\Components\Select::make('type') ->options($visualTypeRegistry->getOptions() ) ->reactive() ->afterStateUpdated( fn ($component) => $component->getContainer()->getComponents()[2]->getChildComponentContainer()->fill() ), Forms\Components\Grid::make(function ($get) { $select = $get('type'); return [ Forms\Components\TextInput::make('a field for '.$select) ]; })->statePath('settings'), ] );
flex
flexOP2y ago
Dan Harrin
Dan Harrin2y ago
Try Group::make()->schema([
flex
flexOP2y ago
awesome meci 🙂 For editing I wouldn't show the option to change the type at all. But for creation it would be pretty nice to put those fields into a two steps wizard. First for name and type. Second for the settings. Is it possible to adjust this code that it triggers the first components on the second wizard page? $component->getContainer()->getComponents()[2]->getChildComponentContainer()->fill()

Did you find this page helpful?