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

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.

Bernhard Schussek

February 09, 2013
Tweet

More Decks by Bernhard Schussek

Other Decks in Programming

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 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
  3. Bernhard Schussek @webmozart 9/96 Ninja … a covert agent in

    feudal Japan who specialized in unorthodox warfare … source: Wikipedia
  4. Bernhard Schussek @webmozart 21/96 Choices Example: Choice Field "1" "0"

    "" true false null User HTML/HTTP Application Yes No Don't know
  5. 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!
  6. 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
  7. 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/...
  8. 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!
  9. Bernhard Schussek @webmozart 32/96 Example: DB Choice Field Specialized choice

    field that loads choices from a DB DB choice field db choice field
  10. 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 +
  11. Bernhard Schussek @webmozart 37/96 Example 1: Field Dependencies Render field

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

    of values by allowing to add/remove fields remove row add row
  13. 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 — …
  14. 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…
  15. 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
  16. 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();
  17. 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();
  18. 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();
  19. 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 // )
  20. 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>
  21. Bernhard Schussek @webmozart 63/96 Composition assassinations number skill_level choice name

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

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

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

    extends AbstractType { public function getName() { return 'ninja'; } ... }
  25. 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'); } }
  26. 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');
  27. Bernhard Schussek @webmozart 71/96 Data transformation date field DateTime DateTime

    form array/object array/object checkbox field boolean boolean
  28. 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 // )
  29. 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 // )
  30. 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
  31. Bernhard Schussek @webmozart 76/96 Form theming form text choice url

    number … Form Logic View main template theme A include theme B
  32. 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 %}
  33. 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)
  34. 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 %}
  35. Bernhard Schussek @webmozart 81/96 Extension concepts A B A A

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

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

    class TextTypeStripHtmlExtension extends AbstractTypeExtension { public function getExtendedType() { return 'form'; } } "mix in"
  38. 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 +
  39. 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
  40. 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();
  41. 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
  42. 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