sonata-project/form-extensions
Additional form types, data transformers, and utilities for Symfony Form via Sonata. Includes date/time and choice helpers, improved widgets, and integration-friendly extensions to speed up building consistent, reusable form components across projects.
## Getting Started
### Minimal Setup
1. **Installation**
Add the package via Composer (ensure compatibility with Symfony 8+ and PHP 8.2+):
```bash
composer require sonata-project/form-extensions:^2.7.0
Register the service provider in config/app.php (Laravel-Symfony bridge required):
'providers' => [
// ...
SonataProject\Form\Extensions\Provider\SonataFormExtensionsServiceProvider::class,
],
Basic Usage
Extend Symfony form types with Sonata’s extensions (e.g., CollectionType):
use Sonata\Form\Extensions\Type\CollectionType;
use Sonata\Form\Extensions\Type\ChoiceType;
$builder
->add('tags', CollectionType::class, [
'entry_type' => ChoiceType::class,
'entry_options' => ['choices' => $tagChoices],
]);
First Use Case
Replace nested forms with Sonata’s CollectionType for dynamic inline editing:
$builder->add('items', CollectionType::class, [
'prototype' => true, // Enables inline editing
'allow_add' => true,
'allow_delete' => true,
'entry_type' => TextType::class, // Symfony 8+ compatible
]);
Dynamic Collections with Symfony 8
Use CollectionType for variable-length arrays (e.g., user roles) with Symfony 8’s form components:
$builder->add('roles', CollectionType::class, [
'entry_type' => EntityType::class,
'entry_options' => ['class' => UserRole::class],
'by_reference' => false, // Critical for detached entities
'prototype' => true,
]);
Conditional Fields
Leverage ConditionalFieldExtension to toggle fields dynamically:
$builder->add('shipping', TextType::class, [
'conditionally_enabled' => [
'field' => 'needs_shipping',
'value' => true,
],
]);
Grouped Fields
Organize fields into collapsible sections with GroupType:
$builder->add('billing', GroupType::class, [
'fields' => ['address', 'city', 'zip'],
'label' => 'Billing Information',
'attr' => ['class' => 'collapsible'], // Symfony 8+ supports `attr`
]);
Symfony 8 Form Factory
Use Laravel’s service container to instantiate Symfony 8’s FormFactory:
$formFactory = app(Symfony\Component\Form\FormFactoryInterface::class);
$form = $formFactory->createNamedBuilder('dynamic_form', null, null)->getForm();
Twig Integration Extend Twig templates with Sonata’s themes (ensure compatibility with Symfony 8’s Twig bundle):
{% extends 'SonataForm::standard_layout.html.twig' %}
{# Override in resources/views/vendor/sonata_form/ #}
Validation with Symfony 8
Combine with Symfony 8’s validators (e.g., Callback, Expression):
use Symfony\Component\Validator\Constraints\Callback;
$builder->add('age', IntegerType::class, [
'constraints' => [new Callback([$validator, 'validateAge'])],
]);
PHP/Symfony Version Mismatch
composer.json:
{
"require": {
"php": "^8.2",
"symfony/form": "^8.0",
"symfony/twig-bundle": "^8.0"
}
}
Prototype Cloning in Symfony 8
prototype: true may cause duplicate IDs in Symfony 8’s stricter DOM handling.prototype_name with a unique prefix:
'prototype_name' => '__item__', // Avoids ID collisions
CSRF in AJAX (Symfony 8)
_token in AJAX requests or use Symfony’s CsrfTokenManager:
{{ form_start(form, {'attr': {'id': 'dynamic-form'}}) }}
{{ form_hidden(form.csrf_token) }} {# Explicit token #}
By-Reference in Collections
by_reference: true can detach entities in Symfony 8’s ORM integration.by_reference: false or use Symfony\Bridge\Doctrine\Form\Type\EntityType:
'entry_options' => ['class' => User::class, 'by_reference' => false],
Symfony 8 Debug Tools
Use Symfony’s DebugForm component to inspect form data:
use Symfony\Component\Form\Extension\Core\Type\FormType;
$form = $formFactory->create(FormType::class);
$form->submit($data);
dump($form->getNormData()); // Debug normalized data
Event Listeners (Symfony 8) Debug form events with Symfony 8’s updated event system:
use Symfony\Component\Form\FormEvents;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function ($event) {
dump($event->getData()); // Symfony 8+ event object
});
Custom Types for Symfony 8
Extend AbstractType with Symfony 8’s form components:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CustomCollectionType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('items', CollectionType::class, [
'entry_type' => $options['entry_type'],
'prototype' => true,
]);
}
}
Override Templates (Symfony 8)
Override Sonata’s Twig templates in resources/views/vendor/sonata_form/:
{# resources/views/vendor/sonata_form/field/collection.html.twig #}
{% block sonata_form_collection_prototype %}
<div class="collection-prototype symfony-8-compatible">
{{ form_row(form) }}
</div>
{% endblock %}
Configuration Quirks
'allow_add' => true, // Symfony 8 defaults to `false`
'allow_delete' => true,
'error_bubbling' => true, // Symfony 8+ best practice
error_mapping).
---
How can I help you explore Laravel packages today?