I’m building a dynamic form in FilamentPHP where ContentType stores field definitions in JSON. I created a trait to map each field into a Filament component like this:
trait BuildsDynamicFields
{
protected static function mapField(array $field): Component
{
$path = "body." . $field['name'];
$label = $field['label'] ?? Str::headline($field['name']);
$rules = $field['rules'] ?? null;
$applyRules = function ($component) use ($rules) {
if ($rules) {
foreach (explode('|', $rules) as $rule) {
$component = $component->rule($rule);
}
}
return $component;
};
return match ($field['type']) {
'richtext' => $applyRules(
RichEditor::make($path)
->label($label)
->statePath($path) // already added
),
'markdown' => $applyRules(
MarkdownEditor::make($path)
->label($label)
->statePath($path) // already added
),
default => TextInput::make($path)->label($label),
};
}
protected static function buildDynamicSchema(?ContentType $type): array
{
if (!$type) {
return [];
}
return collect($type->fields ?? [])
->map(fn ($field) => self::mapField($field))
->toArray();
}
}
Then I use this trait inside my ContentResource:
class ContentResource extends Resource
{
use BuildsDynamicFields;
public static function form(Form $form): Form
{
return $form->schema([
Forms\Components\Section::make('Information')
->schema([
Forms\Components\Select::make('content_type_id')
->options(ContentType::query()->pluck('name', 'id'))
->required()
->reactive()
->afterStateUpdated(fn ($state, callable $set) => $set('body', [])),
]),
Forms\Components\Section::make('Body')
->schema(fn (Forms\Get $get) => self::buildDynamicSchema(
ContentType::find($get('content_type_id'))
))
->hidden(fn (Forms\Get $get) => empty($get('content_type_id'))),
]);
}
}
Example ContentType->fields JSON:
[
{
"name": "content",
"type": "richtext",
"label": "Content",
"rules": null,
"default": null,
"placeholder": "the body of the article"
}
]
- When creating a new record, the
RichEditorcomponent renders but only shows"undefined". Any input I type is not saved (alwaysnull). - When editing a record, the same form works correctly, and the text is saved without issues.
I already tried adding .statePath($path) on the RichEditor and MarkdownEditor, but the problem remains.
How can I make the RichEditor or MarkdownEditor not show "undefined" on create, and ensure the value is properly stored inside the body column?