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.
Installation
composer require symfonycasts/dynamic-forms
Ensure your project uses Symfony 6.4+ (or Laravel via Symfony bridge if applicable).
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',
],
]);
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']],
],
],
],
]);
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'
],
],
],
]);
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)
]);
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']
}
}
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']`.
Field Visibility Logic
visible for specific values may not update if the parent field is modified via JS (e.g., AJAX).field_options['js_options']['update_on'] to specify which events trigger updates:
'js_options' => ['update_on' => ['change', 'blur']],
Data Binding Quirks
old() data includes dynamic fields:
$form = $this->createForm(DynamicFormType::class, $user, [
'data' => $request->all(), // Include existing dynamic data
]);
Prototype Fields
prototype: true may cause duplicate field names in submitted data.field_options['name_property_path'] to customize naming:
'prototype' => true,
'name_property_path' => 'addresses', // Ensures unique keys like `addresses[0][street]`
Validation
'field_options' => [
'constraints' => [new NotBlank()],
],
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);
});
}
Performance
'enable_javascript' => false,
'field_options' => [
'js_options' => ['load_on_demand' => true],
],
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']);
}
}
Laravel-Specific
symfony/form bridge and wrap forms in FormComponent:
use Symfony\Component\Form\FormComponent;
$form = FormComponent::createFormBuilder()
->add('dynamic_fields', DynamicFormType::class, [...])
->getForm();
dynamic_data in DB) and hydrate manually if needed.How can I help you explore Laravel packages today?