0

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 RichEditor component renders but only shows "undefined". Any input I type is not saved (always null).
  • 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?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.