Slide 1

Slide 1 text

Making the most out of Symfony Forms Mihai Nica @redecs

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

I’m going to talk about • Custom Form Types • Data Transformers • Form Events • Validation Groups • Collections and Form Themes 3

Slide 4

Slide 4 text

Custom Form Types Reuse common parts of your forms 4

Slide 5

Slide 5 text

objectManager = $objectManager; } public function buildForm(FormBuilderInterface $builder, array $options) { $transformer = new EntityToIdTransformer($this->objectManager, $options['class']); $builder->addModelTransformer($transformer); } public function setDefaultOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'class' => null, 'invalid_message' => 'The entity does not exist.', )); } } 5

Slide 6

Slide 6 text

Form Events Dynamically change your form depending on data 6

Slide 7

Slide 7 text

add('email', 'email', array( 'required' => false, 'translation_domain' => 'FOSUserBundle' )) ->add('plainPassword', 'repeated', array( 'required' => false, 'type' => 'password', 'options' => ['translation_domain' => 'FOSUserBundle'], )) ->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) { $data = $event->getData()->getData(); $form = $event->getForm(); $object = $form->getData(); if($object) { if($data['plainPassword'] && $object->getId()) { $formData->setUpdatedAt(new \DateTime()); } } }) ; } } 7

Slide 8

Slide 8 text

addEventListener(FormEvents::POST_SET_DATA, function(FormEvent $event) { /** @var Company $company */ $company = $event->getData(); $form = $event->getForm(); if(empty($company->getId())) { $form->add('user', UserForm::class, ['label' => false]); } }); } } 8

Slide 9

Slide 9 text

Validation Groups Callback Validation FTW! 9

Slide 10

Slide 10 text

setDefaults(array( 'data_class' => Company::class, 'membershipTypes' => array(), 'validation_groups' =>function(FormInterface $form) { $data = $form->getData(); $validationGroups = ['Default', 'registrationCompany', 'registrationUser']; if(!$data->getId()) { $validationGroups[] = 'registrationUser'; } return $validationGroups; }, )); } } 10

Slide 11

Slide 11 text

Data Transformers View or Model Transformer 11

Slide 12

Slide 12 text

objectManager = $objectManager; $this->class = $class; } public function transform($entity) { if (null === $entity) { return null; } return $entity->getId(); } public function reverseTransform($id) { if (!$id) { return null; } $entity = $this->objectManager->getRepository($this->class)->find($id); if (null === $entity) { throw new TransformationFailedException(); } return $entity; } } 12

Slide 13

Slide 13 text

add('title', TextType::class) ->add('user', HiddenType::class) ; $builder->get('user')->addModelTransformer(new CallbackTransformer( function ($user) { return $user instanceof User ? $user->getId() : 0; }, function ($id) use ($em){ return $em->find(User::class, $id); } )) ; } } 13

Slide 14

Slide 14 text

Collection “magic” 14

Slide 15

Slide 15 text

add('shootContacts', 'collection', array( 'type' => new ShootContactType(), 'allow_add' => true, 'allow_delete' => true, 'prototype_name' => '__KEY__', 'label' => false, )); } } 15

Slide 16

Slide 16 text

add('shoot', 'entity_hidden', array( 'class' => Shoot::class )) ->add('contact', 'entity_hidden', array( 'class' => Contact::class )) ->add('isMainContact', 'checkbox', array( 'required' => false, )) ->add('role', 'choice', array( 'empty_value' => 'none', 'choices' => Contact::getRolesList(), 'required' => false, )); } public function setDefaultOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => ShootContact::class, 'csrf_protection' => false, )); } } 16

Slide 17

Slide 17 text

{% form_theme form _self %} {% block _frontendbundle_shoot_shootContacts_row %} {{ form_widget(form) }} {% endblock %} {% block _frontendbundle_shoot_shootContacts_widget %} {% spaceless %} {% for collection_form in form %} {{ block('contact_collection_item') }} {% endfor %} {% endspaceless %} {% endblock %} {% block contact_collection_item %} {% spaceless %} {% set shoot = collection_form is defined ? collection_form.vars.value.shoot : null %} {% set contact = collection_form is defined ? collection_form.vars.value.contact : null %} {% set form = collection_form is defined ? collection_form : form.shootContacts.vars.prototype %} {{ contact ? contact.nameFull : '__NAME_FULL__' }} {{ form_widget(form.children.isMainContact) }} {{ form_widget(form.children.role) }} {{ form_widget(form.shoot, {'value': shoot ? shoot.id : '__SHOOT_ID__'}) }} {{ form_widget(form.contact, {'value': contact ? contact.id : '__CONTACT_ID__'}) }} {{ 'form.label.edit'| trans }}  {{ 'form.label.remove'|trans }} {% endspaceless %} {% endblock %} 17

Slide 18

Slide 18 text

18

Slide 19

Slide 19 text

Take-away • Symfony Forms are very powerful and flexible • Form Events, Data Transformers and Validation Groups enable you do amazing things • Form themes and block overriding can be customised any way you want. • Don’t be discourage by the learning curve. The results are well worth the effort 19

Slide 20

Slide 20 text

Questions? Feedback is welcome! https://joind.in/talk/8148e 20

Slide 21

Slide 21 text

Thank you! Mihai Nica mihai@wisesystems.co https://twitter.com/redecs https://www.linkedin.com/in/redecs

Slide 22

Slide 22 text

Reference • https://symfony.com/doc/current/form/data_transformers.html • https://symfony.com/doc/current/form/dynamic_form_modification.html • https://symfony.com/doc/current/form/data_based_validation.html • https://symfony.com/doc/current/form/form_themes.html#form-fragment-naming • https://symfony.com/doc/current/components/form.html#learn-more • https://github.com/Glifery/EntityHiddenTypeBundle