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

    View Slide

  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

    View Slide

  3. Bernhard Schussek @webmozart 3/101
    Bernard
    @webmozart
    "that Forms guy"

    View Slide

  4. Bernhard Schussek @webmozart 4/101
    http://commons.wikimedia.org/wiki/File%3AAustralia-New_Guinea_(orthographic_projection).svg

    View Slide

  5. Bernhard Schussek @webmozart 5/101
    http://upload.wikimedia.org/wikipedia/commons/b/b3/Blank_map_of_Europe.svg

    View Slide

  6. Austria

    View Slide

  7. 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

    View Slide

  8. Bernhard Schussek @webmozart 8/101

    View Slide

  9. Bernhard Schussek @webmozart 9/101

    View Slide

  10. Symfony Form Guru
    in 30 minutes

    View Slide

  11. Bernhard Schussek @webmozart 11/101
    3 Simple Steps

    View Slide

  12. 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

    View Slide

  13. Bernhard Schussek @webmozart 13/101
    Example

    View Slide

  14. Bernhard Schussek @webmozart 14/101
    Today's Example

    View Slide

  15. Bernhard Schussek @webmozart 15/101
    Form
    Form
    Form
    Form
    Form
    Form
    Form
    Symfony's Perspective
    Form Form Form
    Form
    Form
    Form
    Form

    View Slide

  16. Bernhard Schussek @webmozart 16/101
    Form Tree
    Form
    Form
    Form
    Form
    Form
    Form
    Form
    $factory = Forms::createFormFactory();
    $form = $factory->createForm(...);

    View Slide

  17. 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

    View Slide

  18. Bernhard Schussek @webmozart 18/101
    Forms Have Names
    month
    day
    year
    employee
    name
    salary
    birthDate
    $form->get('birthDate');
    $form->get('birthDate')->get('year');
    1

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. Bernhard Schussek @webmozart 21/101
    Form Lifecycle
    New Prepopulated
    Bound
    setData($data)
    submit($data)
    submit($data) / setData(null)
    implicitly called

    View Slide

  22. 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);

    View Slide

  23. 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

    View Slide

  24. 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);

    View Slide

  25. 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"

    View Slide

  26. Bernhard Schussek @webmozart 26/101
    http://www.flickr.com/photos/yourdon/2573762303/
    Step 1:
    Understand Data Formats

    View Slide

  27. Bernhard Schussek @webmozart 27/101
    Formats who?

    View Slide

  28. Bernhard Schussek @webmozart 28/101
    Data Formats
    Form
    model
    data
    view
    data

    View Slide

  29. Bernhard Schussek @webmozart 29/101
    Examples
    [text]
    string string

    View Slide

  30. Bernhard Schussek @webmozart 30/101
    Examples
    [number]
    float localized string

    View Slide

  31. Bernhard Schussek @webmozart 31/101
    Examples
    [date]
    string
    integer
    array
    DateTime Object
    string
    array
    $builder->add('createdAt', 'date', array(
    'input' => 'array',
    'widget' => 'single_text',
    ));

    View Slide

  32. 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();
    }
    );
    }
    }

    View Slide

  33. 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();
    }
    );

    View Slide

  34. 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/

    View Slide

  35. Bernhard Schussek @webmozart 35/101
    In Practice

    View Slide

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

    View Slide

  37. 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

    View Slide

  38. Bernhard Schussek @webmozart 38/101
    Data Transformation

    Symfony\Component\Form\DataTransformerInterface

    bijective

    transform($data)

    reverseTransform($data)

    View Slide

  39. Bernhard Schussek @webmozart 39/101
    Example: ChoiceOrTextType

    View Slide

  40. Bernhard Schussek @webmozart 40/101
    Example: ChoiceOrTextType
    [choiceortext]
    string array
    string
    ValueToChoiceOrTextTransformer
    Was the value submitted
    through the choice
    or the text field?

    View Slide

  41. Bernhard Schussek @webmozart 41/101
    Example: ChoiceOrTextType
    [choiceortext]
    string array
    array
    ValueToChoiceOrTextTransformer

    View Slide

  42. Bernhard Schussek @webmozart 42/101
    Tip
    http://www.flickr.com/photos/xverges/4622486563/
    The normalized data should contain as much
    information as possible.

    View Slide

  43. Bernhard Schussek @webmozart 43/101
    Case 1: Existing Team Selected

    View Slide

  44. 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" => "",
    )

    View Slide

  45. Bernhard Schussek @webmozart 45/101
    Case 2: New Team Created

    View Slide

  46. 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",
    )

    View Slide

  47. Bernhard Schussek @webmozart 47/101
    use Symfony\Component\Form\DataTransformerInterface;
    class ValueToChoiceOrTextTransformer implements
    DataTransformerInterface
    {
    public function transform($data)
    {
    ...
    }
    public function reverseTransform($data)
    {
    ...
    }
    }
    Transformer Implementation

    View Slide

  48. Bernhard Schussek @webmozart 48/101
    Game:
    Right or Wrong?

    View Slide

  49. Bernhard Schussek @webmozart 49/101
    Right or Wrong?
    'Symfony'
    bijective
    'ynofmyS'

    View Slide

  50. Bernhard Schussek @webmozart 50/101
    Right or Wrong?
    'SYmfony' 'symfony'
    not bijective!
    Solution: Events

    View Slide

  51. Bernhard Schussek @webmozart 51/101
    Right or Wrong?
    ['S','Y','m','f',
    'o','n','y']
    'SYmfony'
    bijective

    View Slide

  52. 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

    View Slide

  53. Bernhard Schussek @webmozart 53/101
    Tip
    http://www.flickr.com/photos/xverges/4622486563/
    To make one-way changes, use events.
    In practice: changing the content
    filtering
    sanitization
    ...

    View Slide

  54. Bernhard Schussek @webmozart 54/101
    Transformers and Type Reuse

    View Slide

  55. Bernhard Schussek @webmozart 55/101
    Example: CustomMoneyType
    [custommoney]
    Money Object string
    float
    custom same as in MoneyType

    View Slide

  56. Bernhard Schussek @webmozart 56/101
    Money Object
    MoneyToFloatTransformer
    [custommoney]
    string
    float
    [money]
    float
    integer

    View Slide

  57. Bernhard Schussek @webmozart 57/101
    Back to the start...

    View Slide

  58. 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);

    View Slide

  59. 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
    ?

    View Slide

  60. 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
    ?

    View Slide

  61. Bernhard Schussek @webmozart 61/101
    http://www.flickr.com/photos/brandoncwarren/4164759025
    Step 2:
    Become Friends with Data Mappers

    View Slide

  62. Bernhard Schussek @webmozart 62/101
    Data what?

    View Slide

  63. Bernhard Schussek @webmozart 63/101
    Data Mapping
    Data Mapping is the exchange of information
    between a form's data and its fields.

    View Slide

  64. 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"

    View Slide

  65. 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

    View Slide

  66. Bernhard Schussek @webmozart 66/101
    Data Mapping

    Symfony\Component\Form\DataMapperInterface

    bijective

    mapFormsToData(array $forms, $data)

    mapDataToForms($data, array $forms)

    View Slide

  67. Bernhard Schussek @webmozart 67/101
    Implementations

    In Symfony:

    PropertyPathMapper (getters/setters, arrays)

    Ideas:

    DomDocumentMapper (nodes of a DomDocument instance)

    EAVMapper (attributes of EAV instances)

    View Slide

  68. Bernhard Schussek @webmozart 68/101
    Yeah, but...

    View Slide

  69. Bernhard Schussek @webmozart 69/101
    Yeah, but...
    Terms and Conditions
    1

    View Slide

  70. 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

    View Slide

  71. Bernhard Schussek @webmozart 71/101
    Yeah, but...
    Customized Getters/Setters
    2

    View Slide

  72. 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())

    View Slide

  73. 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()

    View Slide

  74. 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()

    View Slide

  75. 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())

    View Slide

  76. 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())

    View Slide

  77. Bernhard Schussek @webmozart 77/101
    Yeah, but...
    By-Reference Handling
    3

    View Slide

  78. 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)

    View Slide

  79. 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,
    ));

    View Slide

  80. Bernhard Schussek @webmozart 80/101
    Yeah, but...
    Non-Existent Accessors
    4

    View Slide

  81. 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)

    View Slide

  82. 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)

    View Slide

  83. Bernhard Schussek @webmozart 83/101
    Recap

    View Slide

  84. 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!

    View Slide

  85. Bernhard Schussek @webmozart 85/101
    Step 3:
    Events
    http://www.sxc.hu/photo/813792

    View Slide

  86. Bernhard Schussek @webmozart 86/101
    Data Filtering

    View Slide

  87. Bernhard Schussek @webmozart 87/101
    Data Filtering
    convert to lowercase on submit

    View Slide

  88. Bernhard Schussek @webmozart 88/101
    projectName
    [text]
    string string
    string
    Data Filtering
    submit('Symfony');
    "Symfony"
    "Symfony"
    "symfony" "symfony"
    SUBMIT

    View Slide

  89. Bernhard Schussek @webmozart 89/101
    Data Filtering
    $builder->get('projectName')->addEventListener(
    FormEvents::SUBMIT,
    function (FormEvent $event) {
    $event->setData(strtolower($event->getData()));
    }
    );

    View Slide

  90. Bernhard Schussek @webmozart 90/101
    Data-Dependent Forms

    View Slide

  91. Bernhard Schussek @webmozart 91/101
    Data-Dependent Forms

    Employee in a leading position:

    Employee not in a leading position does not show this field.

    View Slide

  92. 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');
    }
    }
    }

    View Slide

  93. 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');
    }
    }
    }

    View Slide

  94. 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!!!

    View Slide

  95. 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())

    View Slide

  96. 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');
    }
    }
    );
    }

    View Slide

  97. Bernhard Schussek @webmozart 97/101
    http://www.flickr.com/photos/[email protected]/5428693024/
    All together now

    View Slide

  98. Bernhard Schussek @webmozart 98/101
    employee
    [form]
    model data norm data view data
    setData($empl)
    PRE_SET_DATA
    Employee Employee Employee
    POST_SET_DATA

    View Slide

  99. Bernhard Schussek @webmozart 99/101
    employee
    [form]
    submit(array(...))
    PRE_SUBMIT
    Employee Employee Employee
    POST_SUBMIT
    submit(...)
    SUBMIT

    View Slide

  100. Bernhard Schussek @webmozart 100/101
    That's it. Have fun! :)

    View Slide

  101. Bernhard Schussek @webmozart 101/101
    Questions?
    http://joind.in/talk/view/10786
    Bernhard Schussek
    @webmozart
    Thank you!

    View Slide