Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

shammy_dummy's avatar

Filament FormsBuilder

Hi, everyone. I need some help with Filament form i got my head cracked in finding solution.

In Short :

  1. I have Laravel Livewire project with filament
  2. I have Blocks table and Model
  3. Also i have block_translations table and BlockTranslations Model -> using Astrotomic / laravel-translatable ( with foreign_key connected ) 4)Structure of DB tables :

CREATE TABLE block_translations ( id bigint unsigned NOT NULL AUTO_INCREMENT, block_id bigint unsigned NOT NULL, locale varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, data json DEFAULT NULL, flag_autofilled TINYINT DEFAULT 0, flag_verified INTEGER DEFAULT NULL, created_at timestamp NULL DEFAULT NULL, updated_at timestamp NULL DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY block_translations_block_id_locale_unique (block_id,locale), KEY block_translations_locale_index (locale), CONSTRAINT block_translations_block_id_foreign FOREIGN KEY (block_id) REFERENCES blocks (id) ON DELETE CASCADE )

CREATE TABLE blocks ( id bigint unsigned NOT NULL AUTO_INCREMENT, name varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, title varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, type varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, is_system_managed tinyint(1) NOT NULL DEFAULT '0', created_at timestamp NULL DEFAULT NULL, updated_at timestamp NULL DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY blocks_name_unique (name) )

*For example i have one block row and 3 rows of block_translations table

and everything works fine with it except one issue:

These data json column could contain different info and arrays of data ->by column type my code should recognize what "inputs and selects" should be printed in Filament

I have now created BlockTranslationsResource : and created edit form function like this:

public static function form(Form $form): Form { $locales = config('app.available_locales'); $tabs = []; $blockTypes = BlockManager::getBlockTypes();

    foreach ($locales as $label=>$locale) {
        $tabs[] = Forms\Components\Tabs\Tab::make(strtoupper($locale))
            ->schema([

                FormsBuilder::make("translations.{$locale}.blocks")
                ->label("Block ({$locale})")
                ->blocks(fn () =>[
                    $blockTypes[$form->getRecord()?->type ?? ''] ?? null, // только нужный тип блока!
                ])
                ->default([
                    [
                        'type' => $form->getRecord()?->type ?? '',
                        // остальные поля будут подставлены из mutateFormDataBeforeFill
                    ]
                ]),

                Forms\Components\Placeholder::make("translations.{$locale}.flag_verified")
                    ->label(__('Reviewed By').':')
                    ->content(fn ($get) => $get("translations.{$locale}.flag_verified") ? __('User')." ID: " . $get("translations.{$locale}.flag_verified") : __('Not reviewed'))
                    ->disabled(),

            ]);
    }

    return $form->schema([
        Forms\Components\Tabs::make('Translations')->tabs($tabs),
    ]);
}

IN BlockTranslationResource/Pages/EditBlockTranslation.php

i have this function : protected function mutateFormDataBeforeFill(array $data): array { foreach (config('app.available_locales') as $label=>$locale) { $translation = $this->record->translate($locale, false);

         if ($translation) {
            $decoded = is_string($translation->data) ? json_decode($translation->data, true) : ($translation->data ?? []);
            $data['translations'][$locale] = [
                'blocks' => [
                    0=>[
                        'type' => $this->record->type,
                        'data'=>[...($decoded ?? [])],
                    ],

                ],
                'flag_autofilled' => $translation->flag_autofilled,
                'flag_verified' => $translation->flag_verified,
            ];
        }



    }
    //dd($data);
    return $data;

And next i enter my form and what i see i have first form working correctly, next tab have problems:

on element filament uses: "data.translations.en.blocks" but suddenly after that on element uses "data.translations.lv.blocks" ???? why???

<!--[if BLOCK]><![endif]-->            
<ul x-sortable="" data-sortable-animation-duration="300" wire:end.stop="mountFormComponentAction('data.translations.en.blocks', 'reorder', { items: $event.target.sortable.toArray() })" class="space-y-4">
        
        <!--[if BLOCK]><![endif]-->                    
            <li wire:key="wJI4bzL5A2p91EPBBlGN.data.translations.lv.blocks.531eb540-8acc-4347-b35e-f04ba7693060.data.Filament\Forms\Components\Builder.item" x-data="{
                    isCollapsed: false,
                }" x-on:builder-expand.window="$event.detail === 'data.translations.en.blocks' &amp;&amp; (isCollapsed = false)" x-on:builder-collapse.window="$event.detail === 'data.translations.en.blocks' &amp;&amp; (isCollapsed = true)" x-on:expand="isCollapsed = false" x-sortable-item="531eb540-8acc-4347-b35e-f04ba7693060" class="fi-fo-builder-item rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-white/5 dark:ring-white/10" x-bind:class="{ 'fi-collapsed': isCollapsed }">

P.S. IF i mannualy change lv.-> to en then values in forms start to show up and works as it should ?

P.S.S. Maybe it is due to Filament form cache? how can i prevent it to happen?

0 likes
3 replies
jaseofspades88's avatar

Start simple, understand the mechanics of how the forms are supposed to work. You don't need all this html fluff and for loops, you just need to understand how to use the filament form builder according to the documentation.

1 like
kivenemi's avatar

Absolutely right. Starting simple is key. Focus on getting a single form working using Filament’s basic form components from the documentation no loops or complex layouts yet. Once you understand the structure and how data flows, scaling up becomes much easier. Keep it clean and clear to build confidence. 💡

shammy_dummy's avatar

I find the reason why this bug appered -> when you made blocks and there is same name to block in different locales cause my BlockType::ABOUT_US->value returned same value for all locales as i change it to (BlockType::ABOUT_US->value.'_'.$locale) -> everything start to work properly

class AboutUsBlock extends BaseBlock {

public static function build(): static
{
    return static::make(BlockType::ABOUT_US->value)
        ->schema([
            Forms\Components\TextInput::make('first_title'),
            Forms\Components\RichEditor::make('first_text')
                ->fileAttachmentsDisk('s3_assets')
                ->fileAttachmentsDirectory('rich_edit')
                ->fileAttachmentsVisibility('public'),

            Forms\Components\TextInput::make('second_title'),

            Forms\Components\Repeater::make('partners')
                ->columns(2)
                ->schema([
                    Forms\Components\TextInput::make('link'),
                    FileManagerField::make('image')->image(),
                ]),

            Forms\Components\TextInput::make('third_title'),
            Forms\Components\RichEditor::make('third_text')
                ->fileAttachmentsDisk('s3_assets')
                ->fileAttachmentsDirectory('rich_edit')
                ->fileAttachmentsVisibility('public'),
            Forms\Components\TextInput::make('third_link'),
            FileManagerField::make('third_image')->image(),
        ]);
}

}

Please or to participate in this conversation.