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

Leveraging Symfony2 Forms

Leveraging Symfony2 Forms

Held at the Symfony Live San Francisco 2011 conference in Paris, France.

This session will introduce you to the new Form component in Symfony2. With the new domain-driven paradigma and its flexible design, the component opens a door to a wide range of possibilities. The brand new architecture makes creating complex forms easier and faster than ever before. This talk will teach you today what you need to know to build powerful forms tomorrow.

Bernhard Schussek

March 03, 2011
Tweet

More Decks by Bernhard Schussek

Other Decks in Programming

Transcript

  1. The Symfony2 Form component Evolution of symfony1 sfForm 2.5 years

    development Fixes most of its problems Reusable widgets Embedded forms
  2. "It takes a long time to make somthing complicated simple,

    but if you do, it will work without problems for a long time." – F. Andy Seidl, http://faseidl.com
  3. Service Oriented Architecture Applications provide services Services are exchangable These

    services can be consumed by different actors Humans Machines
  4. Flow of information Sensio Labs, Paris Address: Max Name: Bratwurst

    Sausage: 5 Amount: $order ->setName('Max') ->setAddress( 'Sensio Labs, Paris') ->setSausage( 'Bratwurst') ->setAmount(5); Order form
  5. Flow of information $order ->getName() ->getAddress() ->getSausage() ->getAmount(); Liip Office

    Zurich Address: Bob Name: Cervelat Sausage: 10 Amount: Existing order
  6. class OrderFormConfig extends AbstractConfig { public function configure( FieldInterface $form,

    array $options) { $form->add('text', 'name') ->add('text', 'address') ->add('text', 'sausage') ->add('integer', 'amount'); } } PHP class Form definition
  7. Why we need Config classes OrderForm cannot be put into

    the Dependency Injection Container ... ... but OrderFormConfig can!
  8. Dependency Injection Container <service id="form.order" class="OrderFormConfig"> <tag name="form.config" alias="form.order" />

    <argument type="service" id="form.factory" /> </service> public function getIdentifier() { return 'form.order'; } Tag alias == form identifier
  9. Form processing $factory = $this->get('form.factory'); $form = $factory->getInstance('form.order'); $form->setData($order); if

    ($request->getMethod() === 'POST') { $form->bindRequest($request); if ($form->isValid()) { $order->send(); return new RedirectResponse(...); } } Form identifier
  10. Form processing $factory = $this->get('form.factory'); $form = $factory->getInstance('form.order'); $form->setData($order); if

    ($request->getMethod() === 'POST') { $form->bindRequest($request); if ($form->isValid()) { $order->send(); return new RedirectResponse(...); } } Service object
  11. Form processing $factory = $this->get('form.factory'); $form = $factory->getInstance('form.order'); $form->setData($order); if

    ($request->getMethod() === 'POST') { $form->bindRequest($request); if ($form->isValid()) { $order->send(); return new RedirectResponse(...); } } Calls setters
  12. Form processing $factory = $this->get('form.factory'); $form = $factory->getInstance('form.order'); $form->setData($order); if

    ($request->getMethod() === 'POST') { $form->bindRequest($request); if ($form->isValid()) { $order->send(); return new RedirectResponse(...); } } $order now contains submitted data!
  13. Form rendering <form action="#" method="post"> {{ form.errors }} {% for

    field in form.vars.fields %} {{ field.errors }} {{ field.label }} {{ field.widget }} {% endfor %} {{ form.rest }} </form> "fields" is a view variable
  14. Form rendering <form action="#" method="post"> {{ form.errors }} {{ form.name.errors

    }} {{ form.name.label }} {{ form.name.widget }} {{ form.rest }} </form>
  15. Form rendering Renders all fields that weren't rendered before ...

    fields that you forgot to render manually ... hidden fields {{ form.rest }}
  16. Validation constraints class Order { /** * @check:NotNull * @check:AssertType("string")

    * @check:MaxLength(50, message= * "Long name, dude...") */ private $name; }
  17. Core fields birthday checkbox choice collection country date datetime entity

    file hidden integer language locale money number password percent repeated textarea text timezone url
  18. Example: Entity field The user sees: Entity field Checkbox "0"

    Checkbox "1" Checkbox "2" Checkbox "3" Symfony sees: Tag IDs
  19. Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" array("0" =>

    "0", "1" => "1", "2" => "1", ...) "0" "1" "0" "0" Tag IDs
  20. Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" array("0" =>

    "0", "1" => "1", "2" => "1", ...) "0" "1" "0" "0" Tag IDs
  21. Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" false true

    true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag)
  22. Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" false true

    true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag) ChoicesToArrayTransformer
  23. Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" false true

    true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag) ArrayToEntitiesTransformer
  24. CSRF protection Cross­Site Request Forgery A form is submitted using

    the session of another person All kinds of misuse Built­in protection in Symfony2
  25. Field creation Manual Automatic Symfony2 looks at metadata of the

    domain class to "guess" the correct field type and settings E.g. Validator metadata, Doctrine2 metadata
  26. Manual field creation public function configure( FieldInterface $form, array $options)

    { $form->add('entity', 'sausage', array( 'class' => 'Sausage', )); } but Doctrine already knows, that "sausage" is a To­One relationship to the Sausage class!
  27. Automatic with options overriding public function configure( FieldInterface $form, array

    $options) { $form->setDataClass('Order') ->add('sausage', array( 'required' => false, )); }
  28. Embedded forms Symfony2 allows to embed forms into another very

    easily Fields and forms implement FieldInterface "A form is a field"
  29. Embedded to­one forms public function configure( FieldInterface $form, array $options)

    { $form->add('form.sausage', 'sausage'); } Identifier of SausageFormConfig
  30. Embedded to­many forms public function configure( FieldInterface $form, array $options)

    { $form->add('collection', 'sausages', array( 'identifier' => 'form.sausage' )); } Identifier of SausageFormConfig
  31. Config options class ConcealedFieldConfig extends Abstract.. { public function getDefaultOptions($options)

    { return array( 'concealed' => true, ); } } Options for influencing the field's/form's creation
  32. Config inheritance class ConcealedFieldConfig extends Abstract.. { public function getParent(array

    $options) { return $options['concealed'] ? 'password' : 'text'; } } Dynamic inheritance from other forms/fields
  33. Form themes Are normal Twig templates Blocks for each field

    type {% block textarea__widget %} <textarea {{ block('attributes') }}> {{ value }} </textarea> {% endblock textarea__widget %}
  34. Form themes Can specify widget, errors, label and row templates

    for specific field types {% block textarea__row %} <tr><td colspan="2"> {{ this.errors }} {{ this.widget }} </td></tr> {% endblock textarea__row %}
  35. Form themes Core themes: TwigBundle::div_layout.html.twig TwigBundle::table_layout.html.twig Configuration in the DI

    parameter "form.theme.template" More flexible configuration options coming soon
  36. Questions? Thanks for listening! Code can currently be found on

    https://github.com/bschussek/symfony/tree/experimental Bernhard Schussek Twitter: @webmozart