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.

24d20907afea0f684e62d620b886af16?s=128

Bernhard Schussek

March 03, 2011
Tweet

Transcript

  1. Bernhard Schussek Leveraging Symfony2 Forms Symfony Live Conference, March 03rd

    2011
  2. About myself Software Architect in Vienna Student of Software Engineering

    Symfony since 2006 Outdoor and music junkie
  3. Agenda Introductory example The Form Config class Form processing Fields

    Useful features
  4. The Symfony2 Form component

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

    development Fixes most of its problems Reusable widgets Embedded forms
  6. Why 2.5 years?

  7. "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
  8. Service Oriented Architecture Applications provide services Services are exchangable These

    services can be consumed by different actors Humans Machines
  9. Forms don't contain business logic

  10. Services do

  11. Example: Online sausage shop

  12. Example: Online sausage shop You Request Response

  13. Example: Online sausage shop Request Response You Consumer Service

  14. Example: Online sausage shop Request Response Consumer Service

  15. Example: Online sausage shop Request Response Consumer Service

  16. Service implementation class Order { function setName($name); function setAddress($address); function

    setSausage($sausage); function setAmount($amount); }
  17. Flow of information I am Max!

  18. Flow of information I am Max! $order->setName('Max')

  19. Flow of information Sensio Labs, Paris Address: Max Name: Bratwurst

    Sausage: 5 Amount: Order form
  20. 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
  21. Flow of information $order ->getName() ->getAddress() ->getSausage() ->getAmount(); Liip Office

    Zurich Address: Bob Name: Cervelat Sausage: 10 Amount: Existing order
  22. The Form Config class

  23. class OrderFormConfig extends AbstractConfig { public function configure( FieldInterface $form,

    array $options) { } } PHP class Form definition
  24. 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
  25. class OrderFormConfig extends AbstractConfig { ... public function getIdentifier() {

    return 'form.order'; } } PHP class Form definition
  26. Why not a simple OrderForm class?

  27. Why we need Config classes OrderForm cannot be put into

    the Dependency Injection Container ... ... but OrderFormConfig can!
  28. 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
  29. Form processing

  30. 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
  31. 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
  32. 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
  33. 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!
  34. Form rendering In the action return $this->render( 'HelloBundle:Hello:index.twig.html', array('form' =>

    $form->getRenderer())); Contains view variables and methods
  35. Form rendering In the template <form action="#" method="post"> {{ form.widget

    }} </form> "widget" is a view method
  36. 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
  37. Form rendering <form action="#" method="post"> {{ form.errors }} {{ form.name.errors

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

    fields that you forgot to render manually ... hidden fields {{ form.rest }}
  39. What about validation?

  40. Form validation uses the Symfony2 Validator "Constraints" are put onto

    your PHP classes (services, entities etc.)
  41. Validation constraints class Order { /** * @check:NotNull * @check:AssertType("string")

    * @check:MaxLength(50, message= * "Long name, dude...") */ private $name; }
  42. Fields

  43. Text input $form->add('text', 'title'); Title:

  44. Textarea $form->add('textarea', 'content'); Content:

  45. Date selector $form->add('date', 'publishAt'); Publish at: Mmmh localized!

  46. Country selector $form->add('country', 'nationality'); Nationality: Localized too! Yummy! Localized too!

    Yummy!
  47. File upload $form->add('file', 'profilePicture'); Profile picture: Remembers uploaded files on

    errors!
  48. Repeated input $form->add('repeated', 'email'); Email: Email (again):

  49. Core fields birthday checkbox choice collection country date datetime entity

    file hidden integer language locale money number password percent repeated textarea text timezone url
  50. Field architecture Filters Value transformers

  51. Filters modify a value uni­directional Example: FixUrlProtocolFilter symfony-project.com http://symfony-project.com

  52. Value Transformers convert values between two representations bi­directional Example: DateTimeToArrayTransformer

    array( 'year' => 2011, 'month' => 5, 'day' => 1, ) object(DateTime)
  53. Example: Entity field The user sees: Entity field Checkbox "0"

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

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

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

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

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

    true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag) ArrayToEntitiesTransformer
  59. 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
  60. CSRF protection app/config/config.yml framework: csrf_protection: enabled: true secret: 30665e19ef0010d5620553

  61. 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
  62. 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!
  63. Automatic field creation public function configure( FieldInterface $form, array $options)

    { $form->setDataClass('Order'); $form->add('sausage'); }
  64. Automatic with options overriding public function configure( FieldInterface $form, array

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

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

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

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

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

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

    type {% block textarea__widget %} <textarea {{ block('attributes') }}> {{ value }} </textarea> {% endblock textarea__widget %}
  71. 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 %}
  72. 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
  73. Questions? Thanks for listening! Code can currently be found on

    https://github.com/bschussek/symfony/tree/experimental Bernhard Schussek Twitter: @webmozart
  74. The End Copyright "dog window" by Larry Wentzel http://www.flickr.com/photos/wentzelepsy "Symfony

    Live 2011 Logo" by Sensio Labs http://www.sensiolabs.com