Handling Forms Like a Ninja with Symfony2

Handling Forms Like a Ninja with Symfony2

Forms are the key elements of web applications, but they are also a major source of frustration. Again and again you find yourself writing the same code for validation, date handling or complex form widgets. Isn’t there a way to simplify this? If I did it once, why isn’t there a simple way to do it again? Enter Symfony2′s form component, your master of reusability. This talk will show how to use its powerful architecture and its stunning simplicity to stop the frustration and start handling forms like a ninja.

24d20907afea0f684e62d620b886af16?s=128

Bernhard Schussek

February 09, 2013
Tweet

Transcript

  1. Bernhard Schussek @webmozart 1/96 Handling forms like a ninja with

    Symfony2 http://sanaril.deviantart.com/art/Unstealthiest-Ninja-276289913
  2. Bernhard Schussek @webmozart 2/96 Bernhard Schussek

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

  4. Bernhard Schussek @webmozart 4/96 Austria

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

  6. http://collider.com/wp-content/uploads/Michael-Flatley-LORD-OF-THE-DANCE-1.jpg

  7. Bernhard Schussek @webmozart 7/96 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
  8. Bernhard Schussek @webmozart 8/96 Ninjas http://askaninja.com

  9. Bernhard Schussek @webmozart 9/96 Ninja … a covert agent in

    feudal Japan who specialized in unorthodox warfare … source: Wikipedia
  10. Bernhard Schussek @webmozart 10/96 invisible

  11. Bernhard Schussek @webmozart 11/96 super-human

  12. Bernhard Schussek @webmozart 12/96 control the 5 elements

  13. Bernhard Schussek @webmozart 13/96 tactics

  14. Bernhard Schussek @webmozart 14/96 prepare innovate team up adapt

  15. Bernhard Schussek @webmozart 15/96 2009 in a dark, dark age…

  16. Bernhard Schussek @webmozart 16/96 I set out to become a

    ninja…
  17. Bernhard Schussek @webmozart 17/96 prepare innovate team up adapt

  18. Bernhard Schussek @webmozart 18/96 Step 1: Forge your weapons prepare

    innovate team up adapt
  19. Bernhard Schussek @webmozart 19/96 Abstraction/Reuse application code 1. Abstract 2.

    Reuse A A A A
  20. Bernhard Schussek @webmozart 20/96 Example: Choice Field

  21. Bernhard Schussek @webmozart 21/96 Choices Example: Choice Field "1" "0"

    "" true false null User HTML/HTTP Application Yes No Don't know
  22. Bernhard Schussek @webmozart 22/96 Application Form Logic View form submission

    string model type → render model type string → process/filter validate true "1" "0" false false false false "0" true default value submitted value lots of logic, lots of duplication!
  23. Bernhard Schussek @webmozart 23/96 Application Form Logic View true choice

    field string model type → render model type string → process/filter validate true false default value submitted value create once, use often
  24. Bernhard Schussek @webmozart 24/96 Coming from Rails, this is absurdly

    complicated. Why not leave this to what it is – a template level problem. Why do I need to create a new model just to render a form?! – Dave Pendejo source: http://webmozarts.com/2012/03/06/...
  25. Bernhard Schussek @webmozart 25/96 Application Form Logic View form submission

    true "0" false true default value submitted value form helper render model type string → active record process/filter string model type → validate Rails dependency!
  26. Bernhard Schussek @webmozart 26/96 Step 2: Combine forces abstract innovate

    team up adapt
  27. Bernhard Schussek @webmozart 27/96 Composition 1. Compose 2. Reuse C

    C C A B C
  28. Bernhard Schussek @webmozart 28/96 Example: Date Field date field choice

    field choice field choice field
  29. Bernhard Schussek @webmozart 29/96 Recursive Composition choice field choice field

    choice field date field date field range field …
  30. Bernhard Schussek @webmozart 30/96 Step 3: Expect innovation abstract innovate

    compose adapt
  31. Bernhard Schussek @webmozart 31/96 Vertical Extension 1. Extend 2. Reuse

    B B B A B
  32. Bernhard Schussek @webmozart 32/96 Example: DB Choice Field Specialized choice

    field that loads choices from a DB DB choice field db choice field
  33. Bernhard Schussek @webmozart 33/96 Horizontal Extension 1. Reuse 2. Mix

    in A A A A A A A A + +
  34. Bernhard Schussek @webmozart 34/96 Example: Mark Required Fields Add an

    asterisk to the label of all required fields base field add asterisk when required +
  35. Bernhard Schussek @webmozart 35/96 Step 4: Change when needed abstract

    extend compose adapt
  36. Bernhard Schussek @webmozart 36/96 Adaptiveness Server Browser form input/ DOM

    manipulation render submit adapt
  37. Bernhard Schussek @webmozart 37/96 Example 1: Field Dependencies Render field

    depending on another field's selected value depends on
  38. Bernhard Schussek @webmozart 38/96 Example 2: Collection Fields Submit lists

    of values by allowing to add/remove fields remove row add row
  39. Bernhard Schussek @webmozart 39/96 four core principles abstract compose extend

    adapt
  40. Bernhard Schussek @webmozart 40/96 2009 in a dark, dark age…

  41. Bernhard Schussek @webmozart 41/96 In a dark, dark age… •

    2009 — symfony 1.2 forms — Zend_Form 1.10 — Zebra_Form 1.2 — HTML_QuickForm 3.2 — …
  42. Bernhard Schussek @webmozart 42/96 abstract compose extend adapt ✓ ✗

    ✗ ✗
  43. Bernhard Schussek @webmozart 43/96 The master plan… • The plan…

    — rebuild the symfony 1.2 forms • The schedule… — a matter of months • but the struggle should last for years…
  44. Bernhard Schussek @webmozart 44/96 Symfony2 Form Component

  45. Bernhard Schussek @webmozart 45/96 3+ years of development

  46. Bernhard Schussek @webmozart 46/96 144+ contributors

  47. Bernhard Schussek @webmozart 47/96 100+ open tickets

  48. Bernhard Schussek @webmozart 48/96 500+ closed tickets

  49. Bernhard Schussek @webmozart 49/96 Installation

  50. Bernhard Schussek @webmozart 50/96 Installation via Composer { "require": {

    "symfony/form": "2.1.*", "symfony/validator": "2.1.*", "symfony/config": "2.1.*", "symfony/translation": "2.1.*", "symfony/twig-bridge": "2.1.*" } } composer.json https://github.com/bschussek/standalone-forms
  51. Bernhard Schussek @webmozart 51/96 Bootstrapping • Form Factory — creates

    forms — central object for registering types and extensions use Symfony\Component\Form\Forms; $factory = Forms::createFormFactoryBuilder() ->addType(...) ->addExtension(...) ->getFormFactory();
  52. Bernhard Schussek @webmozart 52/96 Creating a Form

  53. Bernhard Schussek @webmozart 53/96 Create a form

  54. Bernhard Schussek @webmozart 54/96 Create a form $form = $factory->createBuilder()

    ->add('name', 'text') ->add('skill_level', 'choice', array( 'choices' => array(...) )) ->add('assassinations', 'number') ->getForm();
  55. Bernhard Schussek @webmozart 55/96 Add validation constraints use Symfony\Component\Validator\Constraints as

    Assert; $form = $factory->createBuilder() ->add('name', 'text', array( 'constraints' => array( new Assert\NotBlank(), new Assert\MinLength(4), ) )) ... ->getForm();
  56. Bernhard Schussek @webmozart 56/96 Use a form in the controller

    if (isset($_POST[$form->getName()])) { $form->bind($_POST[$form->getName()]); if ($form->isValid()) { print_r($form->getData()); } } // Array ( // [name] => Mei-Chi Lu // [skill_level] => elite // [assassinations] => 5 // )
  57. Bernhard Schussek @webmozart 57/96 Render the form echo $twig->render('index.html.twig', array(

    'form' => $form->createView(), )); index.html.twig <form action="#" method="post"> {{ form_widget(form) }} <input type="submit" /> </form>
  58. Bernhard Schussek @webmozart 58/96 Render the form

  59. Bernhard Schussek @webmozart 59/96 The form tree name text skill_level

    choice assassin form assassinations number
  60. Bernhard Schussek @webmozart 60/96 Composition assassinations number skill_level choice name

    text assassin form incident form …
  61. Bernhard Schussek @webmozart 61/96 Composition assassinations number skill_level choice name

    text assassin form incident form … dynasty form …
  62. Bernhard Schussek @webmozart 62/96 abstract compose extend adapt ✓

  63. Bernhard Schussek @webmozart 63/96 Composition assassinations number skill_level choice name

    text assassin form incident form … form types $builder->add('name', 'text');
  64. Bernhard Schussek @webmozart 64/96 Native types form checkbox file date

    hidden choice country language locale … …
  65. Bernhard Schussek @webmozart 65/96 Creating a form type assassinations number

    skill_level choice name text assassin form incident form … ninja
  66. Bernhard Schussek @webmozart 66/96 Creating a form type class NinjaType

    extends AbstractType { public function getName() { return 'ninja'; } ... }
  67. Bernhard Schussek @webmozart 67/96 Creating a form type class NinjaType

    extends AbstractType { ... public function buildForm($builder, $options) { $builder->add('name', 'text') ->add('skill_level', 'choice', array( 'choices' => array(...) )) ->add('assassinations', 'number'); } }
  68. Bernhard Schussek @webmozart 68/96 Registering a form type $factory =

    Forms::createFormFactoryBuilder() ->addType(new NinjaType()) ->getFormFactory(); $form = $factory->createBuilder() ->add('assassin', 'ninja') ... ->getForm(); Usage: $form = $factory->create('ninja');
  69. Bernhard Schussek @webmozart 69/96 abstract compose extend adapt ✓ ✓

  70. Bernhard Schussek @webmozart 70/96 Data transformation

  71. Bernhard Schussek @webmozart 71/96 Data transformation date field DateTime DateTime

    form array/object array/object checkbox field boolean boolean
  72. Bernhard Schussek @webmozart 72/96 Getting data into and out of

    the form $form->setData(array( 'name' => 'Mei-Chi Lu', 'skill_level' => 'elite', 'assassinations' => 5, )); print_r($form->getData()); // Array ( // [name] => Mei-Chi Lu // [skill_level] => elite // [assassinations] => 5 // )
  73. Bernhard Schussek @webmozart 73/96 Getting data into and out of

    the form $form->setData(new Ninja('Mei-Chi Lu', 'elite', 5)); print_r($form->getData()); // Ninja Object ( // [name] => Mei-Chi Lu // [skill_level] => elite // [assassinations] => 5 // )
  74. Bernhard Schussek @webmozart 74/96 Getting data into and out of

    the form $form->get('name')->setData('Mei-Chi Lu'); print_r($form->get('name')->getData()); // Mei-Chi Lu name text ninja form
  75. Bernhard Schussek @webmozart 75/96 Theming

  76. Bernhard Schussek @webmozart 76/96 Form theming form text choice url

    number … Form Logic View main template theme A include theme B
  77. Bernhard Schussek @webmozart 77/96 Theming in the main template {%

    extends '::base.html.twig' %} {% form_theme form _self %} {% block content %} {{ form_widget(form.avg_kills) }} {% endblock %} {% block percent_widget %} <div class="input-append"> {{ parent() }}<span class="add-on">%</span> </div> {% endblock %}
  78. Bernhard Schussek @webmozart 78/96 Block names percent Type Template Section

    _widget _label _row Template Helper form_widget(form.field) form_label(form.field) form_row(form.field)
  79. Bernhard Schussek @webmozart 79/96 Theming in separate templates {% extends

    '::base.html.twig' %} {% form_theme form 'NinjaBundle::theme.html.twig' %} {% block content %} {# render form... #} {% endblock %} {% block percent_widget %} <div class="input-append"> {{ parent() }}<span class="add-on">%</span> </div> {% endblock %}
  80. Bernhard Schussek @webmozart 80/96 abstract compose extend adapt ✓ ✓

  81. Bernhard Schussek @webmozart 81/96 Extension concepts A B A A

    + vertical type inheritance horizontal type extensions packaging component extensions ext A B C + + …
  82. Bernhard Schussek @webmozart 82/96 Vertical extension class NinjaType extends AbstractType

    { public function getParent() { return 'form'; } } form ninja "is a"
  83. Bernhard Schussek @webmozart 83/96 Horizontal extension text strip HTML +

    class TextTypeStripHtmlExtension extends AbstractTypeExtension { public function getExtendedType() { return 'form'; } } "mix in"
  84. Bernhard Schussek @webmozart 84/96 Mixed vertical/horizontal extension text strip HTML

    + search extensions are inherited
  85. Bernhard Schussek @webmozart 85/96 Packaging class NinjaExtension extends AbstractExtension {

    protected function loadTypes() { return array(new NinjaType()); } protected function loadTypeExtensions() { return array(new TextTypeStripHtmlExtension()); } } ext ninja strip HTML +
  86. Bernhard Schussek @webmozart 86/96 Extension based architecture Form Engine Foundation

    … … Extensions Templating Core ext Validation ext DI ext CSRF ext Doctrine ext Propel ext Zend_Filter ext jQuery ext Bootstrap ext Zend_Validator ext Twig ext PHP Templating ext Smarty ext
  87. Bernhard Schussek @webmozart 87/96 Bootstrapping continued $formFactory = Forms::createFormFactoryBuilder() //

    extensions ->addExtension(new TwigExtension(...)) ->addExtension(new CsrfExtension(...)) ->addExtension(new ValidatorExtension(...)) // custom types ->addType(new NinjaType()) // custom type extensions ->addTypeExtension(new TextTypeStripHtmlExtension()) ->getFormFactory();
  88. Bernhard Schussek @webmozart 88/96 abstract compose extend adapt ✓ ✓

  89. Bernhard Schussek @webmozart 89/96 abstract compose extend adapt ✓ ✓

  90. Bernhard Schussek @webmozart 90/96 Field dependencies depends on work in

    progress, not yet solved
  91. Bernhard Schussek @webmozart 91/96 Collection fields $builder->add('victims', 'collection', array( 'type'

    => 'text', ));
  92. Bernhard Schussek @webmozart 92/96 abstract compose extend adapt ✓ ✓

    ✓ ~
  93. Bernhard Schussek @webmozart 93/96 Bad News Form handling is incredibly

    complex
  94. Bernhard Schussek @webmozart 94/96 Good News We do it for

    you! ;-)
  95. Bernhard Schussek @webmozart 95/96 Where to get • Symfony Form

    component: — https://github.com/symfony/Form • Documentation: — http://symfony.com/doc/current/book/forms.html • Standalone-usage sample: — https://github.com/bschussek/standalone-forms
  96. Bernhard Schussek @webmozart 96/96 Questions? http://joind.in/talk/view/8023 Thank you! Bernhard Schussek

    @webmozart http://sanaril.deviantart.com/art/Unstealthiest-Ninja-III-309883920