Upgrade to Pro — share decks privately, control downloads, hide ads and more …

3 Steps to Symfony2 Form Mastery

3 Steps to Symfony2 Form Mastery

The Symfony2 Form component makes form processing a snap. It features a very flexible architecture and a myriad of configuration options that you can use to implement a wide variety from simple to very complex forms. However, knowing how to use this effectively can be a challenge. This session will give you a deeper understanding of the inner workings of the Form component and show how master the component in three simple steps.

Bernhard Schussek

November 23, 2012
Tweet

More Decks by Bernhard Schussek

Other Decks in Programming

Transcript

  1. 3 Steps 3 Steps to Symfony2 Form Mastery to Symfony2

    Form Mastery Bernhard Bernhard Schussek aka @webmozart Schussek aka @webmozart SymfonyDay Portugal SymfonyDay Portugal March 8th, 2014 March 8th, 2014
  2. Bernhard Schussek @webmozart 2/101 Bernhard Schussek Not: Bernard Bernhart Bernardt

    Bernharth Also Wrong: Schusseck Schusek Shusseck Scussek Shcusek Schuseck Shusek Schuhsek Shuhseck Shushek Shuseck Shusec Shushek Shushek Shushec Sussek Shussec Schußeck Scusek Shußec Schuhsec Shußec Schushek Schusheck
  3. Bernhard Schussek @webmozart 7/101 flag of Austria not the flag

    of Austria http://en.wikipedia.org/wiki/File:Flag_of_Austria.svg http://en.wikipedia.org/wiki/File:Zeichen_267.svg
  4. Bernhard Schussek @webmozart 12/101 Outline • Today's Example • Step

    1: Understand Data Formats • Step 2: Become Friends with Data Mappers • Step 3: Get Ready for the Event
  5. Bernhard Schussek @webmozart 15/101 Form Form Form Form Form Form

    Form Symfony's Perspective Form Form Form Form Form Form Form
  6. Bernhard Schussek @webmozart 16/101 Form Tree Form Form Form Form

    Form Form Form $factory = Forms::createFormFactory(); $form = $factory->createForm(...);
  7. Bernhard Schussek @webmozart 17/101 Forms Have Names month day year

    employee name salary birthDate $builder = $factory->createNamedBuilder('employee'); $builder->add('name'); $builder->add('salary'); $builder->add('birthDate'); $form = $builder->createForm(); 1
  8. Bernhard Schussek @webmozart 18/101 Forms Have Names month day year

    employee name salary birthDate $form->get('birthDate'); $form->get('birthDate')->get('year'); 1
  9. Bernhard Schussek @webmozart 19/101 Forms Have Types month [choice] day

    [choice] year [choice] employee [form] name [text] salary [money] birthDate [birthdate] $builder = $factory->createNamedBuilder('employee', 'form'); $builder->add('name', 'text'); $builder->add('salary', 'money'); $builder->add('birthDate', 'birthdate'); $form = $builder->createForm(); 2
  10. Bernhard Schussek @webmozart 20/101 Forms Have Data employee [form] data

    name [text] data salary [money] data birthDate [birthdate] data day [choice] data month [choice] data year [choice] data 3
  11. Bernhard Schussek @webmozart 21/101 Form Lifecycle New Prepopulated Bound setData($data)

    submit($data) submit($data) / setData(null) implicitly called
  12. Bernhard Schussek @webmozart 22/101 Prepopulation employee [form] data name [text]

    data salary [money] data birthDate [birthdate] data year [choice] data month [choice] data day [choice] data $employee = new Employee(); $employee->name = 'Jane'; $employee->salary = 110000.0; $employee->birthDate = new DateTime('1941-09-09'); $form->setData($employee);
  13. Bernhard Schussek @webmozart 23/101 Prepopulation employee [form] Employee Object name

    [text] salary [money] birthDate [birthdate] year [choice] month [choice] day [choice] data data data "Jane" 110000.0 DateTime Object data data data 1941 9 9
  14. Bernhard Schussek @webmozart 24/101 employee [form] object (Employee) name [text]

    "Jane" salary [money] 110000.0 birthDate [birthdate] object (DateTime) day [choice] 1941 month [choice] 9 year [choice] 9 $form->handleRequest($request);
  15. Bernhard Schussek @webmozart 25/101 ? employee [form] Employee Object name

    [text] "Jane" salary [money] 110000.0 birthDate [birthdate] DateTime Object year [choice] 1941 month [choice] 9 day [choice] 9 "1962" "95000.00" "5" "26" "John"
  16. Bernhard Schussek @webmozart 31/101 Examples [date] string integer array DateTime

    Object string array $builder->add('createdAt', 'date', array( 'input' => 'array', 'widget' => 'single_text', ));
  17. Bernhard Schussek @webmozart 32/101 Which format do I get? class

    MyDateType extends AbstractType { public function getParent() { return 'date'; } public function buildForm(FormBuilderInterface $builder) { $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { $data = $event->getForm()->getData(); } ); } }
  18. Bernhard Schussek @webmozart 33/101 Normalized Format [date] string integer array

    DateTime Object string array DateTime Object $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { $data = $event->getForm()->getNormData(); } );
  19. Bernhard Schussek @webmozart 34/101 Tip • Whenever you create a

    custom type, decide: • Model format? • Normalized format? • View format? http://www.flickr.com/photos/xverges/4622486563/
  20. Bernhard Schussek @webmozart 36/101 Data Transformation Form model data view

    data normalized data model transformers view transformers setData($data)
  21. Bernhard Schussek @webmozart 37/101 Data Transformation Form model data view

    data normalized data model transformers view transformers submit($data) submit fields and map to view data
  22. Bernhard Schussek @webmozart 40/101 Example: ChoiceOrTextType [choiceortext] string array string

    ValueToChoiceOrTextTransformer Was the value submitted through the choice or the text field?
  23. Bernhard Schussek @webmozart 44/101 Case 1: Existing Team Selected [choiceortext]

    model data view data normalized data ValueToChoiceOrTextTransformer submit(array( 'choice' => 'Team 1', 'text' => '', )); "Team 1" array( "choice" => "Team 1", "text" => "", ) array( "choice" => "Team 1", "text" => "", )
  24. Bernhard Schussek @webmozart 46/101 Case 2: New Team Created [choiceortext]

    model data view data normalized data ValueToChoiceOrTextTransformer submit(array( 'choice' => 'Other', 'text' => 'New Team', )); "New Team" array( "choice" => "Other", "text" => "New Team", ) array( "choice" => "Other", "text" => "New Team", )
  25. Bernhard Schussek @webmozart 47/101 use Symfony\Component\Form\DataTransformerInterface; class ValueToChoiceOrTextTransformer implements DataTransformerInterface

    { public function transform($data) { ... } public function reverseTransform($data) { ... } } Transformer Implementation
  26. Bernhard Schussek @webmozart 52/101 Tip http://www.flickr.com/photos/xverges/4622486563/ Data transformers must always

    be bijective. A == reverseTransform(transform(A)) In practice: changing the representation string ↔ integer array ↔ DateTime
  27. Bernhard Schussek @webmozart 58/101 Prepopulation employee [form] data name [text]

    data salary [money] data birthDate [birthdate] data year [choice] data month [choice] data day [choice] data $employee = new Employee(); $employee->name = 'Jane'; $employee->salary = 110000.0; $employee->birthDate = new DateTime('1941-09-09'); $form->setData($employee);
  28. Bernhard Schussek @webmozart 59/101 Prepopulation employee [form] Employee Object name

    [text] model data salary [money] model data birthDate [birthdate] model data year [choice] model data month [choice] model data day [choice] model data ?
  29. Bernhard Schussek @webmozart 60/101 Submission employee [form] Employee Object name

    [text] "John" salary [money] 95000.0 birthDate [birthdate] DateTime Object year [choice] 1962 month [choice] 5 day [choice] 18 ?
  30. Bernhard Schussek @webmozart 63/101 Data Mapping Data Mapping is the

    exchange of information between a form's data and its fields.
  31. Bernhard Schussek @webmozart 64/101 employee [form] model data name [text]

    model data salary [money] model data birthDate [birthdate] model data setData($empl->getSalary()) 110000.0 setData($empl->getBirthDate()) DateTime Object setData($empl) Employee Object setData($empl->getName()) "Jane"
  32. Bernhard Schussek @webmozart 65/101 employee [form] Employee Object name [text]

    salary [money] birthDate [birthdate] model data model data model data $empl->setBirthDate($form->getData()) $empl->setSalary($form->getData()) $empl->setName($form->getData()) handleRequest($request) "John" 95000.0 DateTime Object
  33. Bernhard Schussek @webmozart 66/101 Data Mapping • Symfony\Component\Form\DataMapperInterface • bijective

    • mapFormsToData(array $forms, $data) • mapDataToForms($data, array $forms)
  34. Bernhard Schussek @webmozart 67/101 Implementations • In Symfony: • PropertyPathMapper

    (getters/setters, arrays) • Ideas: • DomDocumentMapper (nodes of a DomDocument instance) • EAVMapper (attributes of EAV instances)
  35. Bernhard Schussek @webmozart 70/101 Terms and Conditions $builder->add('terms', 'checkbox', array(

    'mapped' => false, )); order [form] Order Object terms [checkbox] model data $form->get('terms')->setData(true); $form->get('terms')->getData(); $builder->add('terms', 'checkbox', array( 'mapped' => false, 'data' => true, )); true
  36. Bernhard Schussek @webmozart 72/101 Customized Getters/Setters $builder->add('name', 'text', array( 'property_path'

    => 'fullName', )); employee [form] Employee Object name [text] "Jane" $form->setData($empl->getFullName()) $empl->setFullName($form->getData())
  37. Bernhard Schussek @webmozart 73/101 Customized Getters/Setters $builder->add('name', 'text', array( 'property_path'

    => 'fullName', )); employee [form] Employee Object name [text] "Jane" $form->setData($empl->fullName) $empl->fullName = $form->getData()
  38. Bernhard Schussek @webmozart 74/101 Array Access $builder->add('name', 'text', array( 'property_path'

    => '[fullName]', )); employee [form] Employee Object name [text] "Jane" $form->setData($empl['fullName']) $empl['fullName'] = $form->getData()
  39. Bernhard Schussek @webmozart 75/101 Chained Getters/Setters $builder->add('name', 'text', array( 'property_path'

    => 'personalDetails.fullName', )); employee [form] Employee Object name [text] "Jane" $form->setData($empl->getPersonalDetails()->getFullName()) $empl->getPersonalDetails()->setFullName($form->getData())
  40. Bernhard Schussek @webmozart 76/101 Mixed Chaining $builder->add('name', 'text', array( 'property_path'

    => '[personalDetails].fullName', )); employee [form] Employee Object name [text] "Jane" $form->setData($empl['personalDetails']->getFullName()) $empl['personalDetails']->setFullName($form->getData())
  41. Bernhard Schussek @webmozart 78/101 By-Reference Handling employee [form] Employee Object

    address [form] Address Object street [text] "4th Street" city [text] "San Francisco" $address->setCity($form->getData()) $address->setStreet($form->getData()) $empl->setAddress($address)
  42. Bernhard Schussek @webmozart 79/101 By-Reference Handling employee [form] Employee Object

    address [form] Address Object street [text] "4th Street" city [text] "San Francisco" $empl->setAddress($address) $builder->add('address', 'form', array( 'by_reference' => false, ));
  43. Bernhard Schussek @webmozart 81/101 publishedAt [date] year [choice] month [choice]

    day [choice] DateTime Object model data model data model data setData($date->getYear()) setData($date)
  44. Bernhard Schussek @webmozart 82/101 publishedAt [date] year [choice] month [choice]

    day [choice] DateTime Object model data model data model data array array setData($date['year']) 2012 setData($date['month']) 9 setData($date['day']) 27 setData($date)
  45. Bernhard Schussek @webmozart 84/101 Recap • Data Transformers • change

    the data's representation • Data Mappers • exchange data between forms and fields • Missing Piece: Events • change the data's content • dynamic forms!
  46. Bernhard Schussek @webmozart 88/101 projectName [text] string string string Data

    Filtering submit('Symfony'); "Symfony" "Symfony" "symfony" "symfony" SUBMIT
  47. Bernhard Schussek @webmozart 91/101 Data-Dependent Forms • Employee in a

    leading position: • Employee not in a leading position does not show this field.
  48. Bernhard Schussek @webmozart 92/101 class EmployeeType extends AbstractType { private

    $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function buildForm(FormBuilderInterface $builder, array $options) { if ($this->employee->isInLeadingPosition()) { $builder->add('department', 'text'); } } }
  49. Bernhard Schussek @webmozart 93/101 class EmployeeType extends AbstractType { private

    $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function buildForm(FormBuilderInterface $builder, array $options) { if ($this->employee->isInLeadingPosition()) { $builder->add('department', 'text'); } } }
  50. Bernhard Schussek @webmozart 94/101 Form Types vs. Instances • Form

    Types • exist once in your application • only dependencies that are the same for every form instance (e.g. EntityManager) • Form Instances • exist multiple times with different data • data can (and often will) change after construction!!!
  51. Bernhard Schussek @webmozart 95/101 employee [form] object array object array

    object array setData($empl) PRE_SET_DATA Employee Object Employee Object department Employee Object setData($empl->getDept())
  52. Bernhard Schussek @webmozart 96/101 public function buildForm(FormBuilderInterface $builder, array $options)

    { $builder->addEventListener( FormEvents::PRE_SET_DATA, function (FormEvent $event) { if ($event->getData()->isInLeadingPosition()) { $event->getForm()->add('department', 'text'); } } ); }
  53. Bernhard Schussek @webmozart 98/101 employee [form] model data norm data

    view data setData($empl) PRE_SET_DATA Employee Employee Employee POST_SET_DATA