symfony/form
Symfony Form Component helps you build, validate, and process reusable HTML forms with rich field types, data mapping, and CSRF protection. Integrates cleanly with HttpFoundation, Validator, and Twig, but can be used standalone in any PHP app.
Installation:
composer require symfony/form
Laravel developers typically use this via the Symfony Form component (often bundled with Laravel's built-in form helpers or via laravelcollective/html).
First Use Case: Create a simple form in a Laravel controller:
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
public function createForm(FormBuilderInterface $builder)
{
return $builder
->setAction($this->generateUrl('submit_form'))
->setMethod('POST')
->add('name', TextType::class, [
'label' => 'Full Name',
'attr' => ['class' => 'form-control'],
])
->getForm();
}
Where to Look First:
Form facade (if using laravelcollective/html):
use Collective\Html\FormFacade as Form;
FormBuilderInterface to create reusable form types:
$form = $this->createFormBuilder($user)
->add('email', EmailType::class)
->add('save', SubmitType::class)
->getForm();
FormRequest for validation:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\FormFactoryInterface;
public function __construct(Request $request, FormFactoryInterface $formFactory) {
$this->form = $formFactory->createNamedBuilder('', null, null)
->add('field', TextType::class)
->getForm();
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData(); // Bound to your entity
}
$builder->add('tags', CollectionType::class, [
'entry_type' => TextType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
]);
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CustomType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('custom_field', TextType::class);
}
}
AppServiceProvider:
public function boot() {
FormTypeExtension::defaultFormTypeExtensions([
new CustomTypeExtension(),
]);
}
use Symfony\Component\Validator\Constraints as Assert;
$builder->add('age', IntegerType::class, [
'constraints' => [new Assert\GreaterThan(18)],
]);
$builder->add('email', EmailType::class, [
'error_bubbling' => true,
'constraints' => [
new Assert\NotBlank(['message' => 'Email is required']),
],
]);
use Symfony\Component\Form\FormFlow;
$flow = new FormFlow();
$flow->addStep('step1', $step1Form);
$flow->addStep('step2', $step2Form);
$flow->setCurrentStep('step1');
Session Contamination:
handle_missing_data:
$form = $formFactory->createBuilder()->handleRequest($request, true)->getForm();
Collection Index Mismatches:
items[0] vs items[1]) cause data loss.by_reference: false or allow_add/allow_delete: true:
$builder->add('items', CollectionType::class, [
'by_reference' => false,
'allow_add' => true,
]);
Validator Duplication:
ValidatorExtension multiple times causes duplicate errors.Validator service handles this).Textarea Newlines:
TextType with attr: ['rows' => 5] or custom transformers.dd($form->getData(), $form->getErrors(true));
$form->submit($request->request->all());
Custom Data Transformers:
use Symfony\Component\Form\DataTransformerInterface;
class CustomTransformer implements DataTransformerInterface {
public function transform($value) { /* ... */ }
public function reverseTransform($value) { /* ... */ }
}
Register via FormTypeExtension:
$builder->addModelTransformer(new CustomTransformer());
Event Listeners:
PRE_SET_DATA, POST_SUBMIT, or PRE_SUBMIT events:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$event->setData($event->getData() ?? new YourEntity());
});
Override Twig Templates:
vendor/symfony/form/Resources/views/ to resources/views/form/ and customize.csrf_token() must be included in forms:
$builder->add('csrf_token', HiddenType::class, [
'mapped' => false,
'data' => csrf_token(),
]);
Symfony\Component\HttpFoundation\Request directly or Laravel's FormRequest:
$form->handleRequest($request); // Laravel's Request extends Symfony's
$formFactory->getFormType('your_type')->getConfig()->setAttribute('cache_key', 'unique_key');
How can I help you explore Laravel packages today?