Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dynamic Forms Laravel Package

symfonycasts/dynamic-forms

Build dynamic, AJAX-friendly Symfony forms where fields appear, disappear, or change based on user input. Provides an event-driven approach to updating form options and validation, making complex dependent form flows easier to implement and maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require symfonycasts/dynamic-forms
    

    Ensure your project uses Symfony 6.4+ (or Laravel via Symfony bridge if applicable).

  2. Basic Integration Add the DynamicFormType to your form builder:

    use SymfonyCasts\DynamicForms\Form\DynamicFormType;
    
    $builder->add('dynamic_fields', DynamicFormType::class, [
        'fields' => [
            'name' => 'text',
            'email' => 'email',
        ],
    ]);
    
  3. First Use Case Create a dependent field (e.g., a dropdown that populates another field):

    $builder->add('country', ChoiceType::class, [
        'choices' => ['US' => 'United States', 'CA' => 'Canada'],
        'field_options' => [
            'dependent_fields' => [
                'state' => [
                    'type' => 'choice',
                    'choices' => ['CA' => ['ON', 'BC'], 'US' => ['CA', 'NY']],
                ],
            ],
        ],
    ]);
    

Implementation Patterns

Dynamic Field Workflows

  1. Conditional Rendering Use dependent_fields to show/hide fields based on parent selections:

    $builder->add('subscription_type', ChoiceType::class, [
        'choices' => ['monthly' => 'Monthly', 'yearly' => 'Yearly'],
        'field_options' => [
            'dependent_fields' => [
                'discount_code' => [
                    'type' => 'text',
                    'required' => false,
                    'visible' => ['yearly'], // Only show for 'yearly'
                ],
            ],
        ],
    ]);
    
  2. Nested Dynamic Forms Embed dynamic forms within other dynamic forms:

    $builder->add('addresses', DynamicFormType::class, [
        'fields' => [
            'street' => 'text',
            'city' => 'text',
            'dependent_fields' => [
                'zip' => [
                    'type' => 'text',
                    'visible' => ['city'], // Requires city to be set first
                ],
            ],
        ],
        'prototype' => true, // For repeatable sections (e.g., multiple addresses)
    ]);
    
  3. Data Binding Handle dynamic data in controllers:

    public function update(Request $request, User $user) {
        $form = $this->createForm(DynamicFormType::class, $user);
        $form->handleRequest($request);
    
        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            // Access dynamic fields via $data['dynamic_fields']['field_name']
        }
    }
    
  4. JavaScript Integration Use the included JS for client-side reactivity:

    // Auto-generated by the package; no manual JS needed for basic cases.
    // Customize via `field_options['js_options']`.
    

Gotchas and Tips

Common Pitfalls

  1. Field Visibility Logic

    • Issue: Fields marked as visible for specific values may not update if the parent field is modified via JS (e.g., AJAX).
    • Fix: Use field_options['js_options']['update_on'] to specify which events trigger updates:
      'js_options' => ['update_on' => ['change', 'blur']],
      
  2. Data Binding Quirks

    • Issue: Dynamic fields may not bind correctly if the form is re-rendered without the initial data.
    • Fix: Ensure old() data includes dynamic fields:
      $form = $this->createForm(DynamicFormType::class, $user, [
          'data' => $request->all(), // Include existing dynamic data
      ]);
      
  3. Prototype Fields

    • Issue: prototype: true may cause duplicate field names in submitted data.
    • Fix: Use field_options['name_property_path'] to customize naming:
      'prototype' => true,
      'name_property_path' => 'addresses', // Ensures unique keys like `addresses[0][street]`
      
  4. Validation

    • Issue: Dynamic fields may bypass validation if not properly configured.
    • Fix: Explicitly define constraints:
      'field_options' => [
          'constraints' => [new NotBlank()],
      ],
      

Advanced Tips

  1. Custom Field Types Extend the package to support non-Symfony form types (e.g., Laravel-specific components):

    // In a custom DynamicFormExtension
    public function loadFieldExtensions(LoaderInterface $loader, FormInterface $form) {
        $loader->addFieldExtension('laravel_checkbox', function (FieldExtensionInterface $extension) {
            return new LaravelCheckboxExtension($extension);
        });
    }
    
  2. Performance

    • Disable JS for non-dynamic forms:
      'enable_javascript' => false,
      
    • Lazy-load dependent fields:
      'field_options' => [
          'js_options' => ['load_on_demand' => true],
      ],
      
  3. Testing Use the DynamicFormTestTrait for PHPUnit:

    use SymfonyCasts\DynamicForms\Tests\DynamicFormTestTrait;
    
    class MyFormTest {
        use DynamicFormTestTrait;
    
        public function testDynamicFieldVisibility() {
            $this->assertFieldVisible('discount_code', ['subscription_type' => 'yearly']);
        }
    }
    
  4. Laravel-Specific

    • For Laravel, use the symfony/form bridge and wrap forms in FormComponent:
      use Symfony\Component\Form\FormComponent;
      
      $form = FormComponent::createFormBuilder()
          ->add('dynamic_fields', DynamicFormType::class, [...])
          ->getForm();
      
    • Store dynamic data in a JSON column (e.g., dynamic_data in DB) and hydrate manually if needed.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings