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

Intégrer les formulaires et la validation Symfony dans vos applications PHP.

Intégrer les formulaires et la validation Symfony dans vos applications PHP.

Les formulaires sont partout et sont aussi des composants importants d'une application. Ils permettent en effet aux utilisateurs d'interagir avec votre système d'informations. Cependant, la conception et la validation des formulaires ne sont pas des tâches si aisées qu'elles n'y paraissent. Le but de cette conférence est de vous aider à tirer profit des composants de formulaire et de validation du framework Symfony. Nous aborderons les aspects d'architecture des formulaires, les concepts fondamentaux ainsi que quelques usages avancés comme les formulaires imbriqués et les collections. Nous passerons aussi en revue les différentes manières de valider des données avec le composant de validation. Vous serez ainsi en mesure d'intégrer ces deux composants majeurs du framework Symfony dans vos propres applications PHP.

Hugo Hamon

April 05, 2013
Tweet

More Decks by Hugo Hamon

Other Decks in Technology

Transcript

  1. Intégrer les formulaires et
    la validation Symfony
    dans vos applications PHP
    .
    Hugo HAMON – SymfonyLive - Paris 2013

    View Slide

  2. Biographie…
    Hugo HAMON
    Responsable des formations SensioLabs
    Auteur chez Eyrolles
    Conférencier
    Contributeur à Symfony
    @hhamon

    View Slide

  3. SensioLabs
    A la recherche
    d’un job ?

    View Slide

  4. Introduction
    Formulaires & Validation

    View Slide

  5. Pourquoi la
    gestion des
    formulaires
    est-elle si
    compliquée ?

    View Slide

  6. Formulaires
    Architecture

    View Slide

  7. View Slide

  8. Cœur du système de formulaire
    Core CSRF DI
    Doctrine Propel
    Twig PHP Smarty …
    Zend …
    Rendu
    Extensions
    Fondation

    View Slide

  9. Démarrage
    {
    "require": {
    "doctrine/common": "2.*",
    "symfony/form": "2.2.*",
    "symfony/yaml": "2.2.*",
    "symfony/http-foundation": "2.2.*",
    "symfony/validator": "2.2.*",
    "symfony/config": "2.2.*",
    "symfony/translation": "2.2.*",
    "symfony/twig-bridge": "2.2.*"
    }
    }

    View Slide

  10. use Symfony\Bridge\Twig\Form\TwigRendererEngine;
    use Symfony\Component\Form\Forms;
    use Symfony\Bridge\Twig\Extension\FormExtension;
    use Symfony\Bridge\Twig\Form\TwigRenderer;
    // Define some constants to the main resources
    define('VENDOR_DIR', realpath(__DIR__ . '/../vendor'));
    define('DEFAULT_FORM_THEME', 'form_div_layout.html.twig');
    define('VENDOR_TWIG_BRIDGE_DIR', VENDOR_DIR . '/symfony/twig-bridge/Symfony/Bridge/Twig');
    define('VIEWS_DIR', realpath(__DIR__ . '/../views'));
    // Initialize a Twig compatible rendering engine
    $twig = new Twig_Environment(new Twig_Loader_Filesystem(array(
    VIEWS_DIR,
    VENDOR_TWIG_BRIDGE_DIR . '/Resources/views/Form',
    )));
    $formEngine = new TwigRendererEngine(array(DEFAULT_FORM_THEME));
    $formEngine->setEnvironment($twig);
    // Register the Twig Form extension
    $twig->addExtension(new FormExtension(new TwigRenderer($formEngine)));
    // Set up the Form component
    $formFactoryBuilder = Forms::createFormFactoryBuilder();
    $formFactory = $formFactoryBuilder->getFormFactory();

    View Slide

  11. Formulaires
    Les fondamentaux

    View Slide

  12. Créer un formulaire simple
    $form = $formFactory
    ->createBuilder()
    ->add('name')
    ->add('bio', 'textarea')
    ->add('gender', 'choice', array(
    'choices' => array(
    'm' => 'Male',
    'f' => 'Female'
    ),
    ))
    ->getForm();

    View Slide

  13. Créer un formulaire simple

    View Slide

  14. Vue arborescente du formulaire
    form   form bio   textarea
    name   text
    gender   choice
    Nom du champ   Type de champ  

    View Slide

  15. Traitement du formulaire
    $name = $form->getName();
    if (!empty($_POST[$name])) {
    $form->bind($_POST[$name]);
    $data = $form->getData();
    print_r($data);
    }

    View Slide

  16. Association avec un tableau
    $data['name']
    $data['bio']
    $data['gender']

    View Slide

  17. Préremplissage du formulaire
    $form->setData(array(
    'name' => 'Jane Smith',
    'bio' => 'I do great things!',
    'gender' => 'f',
    ));

    View Slide

  18. Préremplissage du formulaire
    Jane Smith
    I do great things!
    Female

    View Slide

  19. Formulaires
    Types de champ natifs

    View Slide

  20. Types de champ natifs
    §  Birthday
    §  Checkbox
    §  Choice
    §  Collection*
    §  Country
    §  Date
    §  DateTime
    §  File
    §  Hidden
    §  Integer
    §  Language
    §  Locale
    §  Money
    §  Number
    §  Password
    §  Percent
    §  Radio
    §  Repeated*
    §  Search
    §  Textarea
    §  Text
    §  Time
    §  Timezone
    §  Url

    View Slide

  21. Types de champ natifs
    Text Choice Password File
    Form
    Date
    Country Language Timezone Birthday DateTime

    View Slide

  22. Le champ répété
    $builder
    ->add('name')
    ->add('password', 'repeated', array(
    'type' => 'password',
    'invalid_message' => 'Passwords do not match.',
    'first_options' => array('label' => 'Password'),
    'second_options' => array('label' => 'Confirmation'),
    ))
    ->add('bio', 'textarea')
    // [...]
    ->getForm()
    ;
     

    View Slide

  23. Le type « répété » est
    rendu sous la forme de
    deux champs de saisie de
    mot de passe.

    View Slide

  24. Formulaires
    Rendu des formulaires

    View Slide

  25. Afficher un formulaire
    // PHP rendering
    echo $engine->render('profile.php', array(
    'form' => $form->createView(),
    ));
    // Twig rendering
    echo $twig->render('profile.twig', array(
    'form' => $form->createView(),
    ));

    View Slide

  26. Prototypage du rendu (PHP)


    Your profile
    widget($form) ?>


    Save changes
    Cancel


    View Slide

  27. Prototypage du rendu (Twig)


    Your profile
    {{ form_widget(form) }}


    Save changes
    Cancel


    View Slide


  28. class="required">Name
    name="form[name]"
    required="required"
    value="Jane Smith" />

    Prototypage du rendu

    View Slide

  29. // Form rendering
    enctype($form) ?>
    widget($form) ?>
    errors($form) ?>
    rest($form) ?>
    // Field rendering
    row($form['bio']) ?>
    errors($form['bio']) ?>
    label($form['bio']) ?>
    widget($form['bio'], array(
    'attr' => array('class' => 'editor'),
    )) ?>
    Rendu personnalisé (PHP)

    View Slide

  30. {# General rendering #}
    {{ form_enctype(form) }}
    {{ form_widget(form) }}
    {{ form_errors(form) }}
    {{ form_rest(form) }}
    {# Field rendering #}
    {{ form_row(form.bio) }}
    {{ form_errors(form.bio) }}
    {{ form_label(form.bio, 'Biography') }}
    {{ form_widget(form.bio, { 'attr': { 'class': 'editor' }}) }}
    Rendu personnalisé (Twig)

    View Slide

  31. Formulaires
    Association avec des objets

    View Slide

  32. Association des propriétés publiques
    class Person
    {
    public $name;
    public $password;
    public $bio;
    public $gender;
    }

    View Slide

  33. Association des propriétés publiques
    $person = new Person();
    $person->name = 'John Doe';
    $person->password = 'S3cR3T$1337';
    $person->bio = 'Born in 1970...';
    $person->gender = 'm';
    $person->active = true;
    // The form reads & writes
    // the person object
    $form->setData($person);

    View Slide

  34. Association des propriétés privées
    class Person
    {
    private $name;
    // ...
    function setName($name) { $this->name = $name; }
    function getName() { return $this->name; }
    }

    View Slide

  35. Association des propriétés privées
    class Person
    {
    private $active;
    // ...
    public function isActive()
    {
    return $this->active;
    }
    }

    View Slide

  36. Association des propriétés privées
    $person = new Person();
    $person->setName('John Doe');
    $person->setPassword('S3cR3T$1337');
    $person->setBio('Born in 1970...');
    $person->setGender('m');
    $person->setActive(true);
    // The form reads & writes
    // the person object
    $form->setData($person);

    View Slide

  37. Association du type de données
    $form = $formFactory
    ->createBuilder('form', $person, array(
    'data_class' => 'Person',
    ))
    // ...
    ->getForm()
    ;

    View Slide

  38. Formulaires
    Protection CSRF

    View Slide

  39. Activer la protection CSRF
    # bootstrap.php
    // ...
    use Symfony\Component\Form\Extension\Csrf\CsrfExtension;
    use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider;
    use Symfony\Bridge\Twig\Form\TwigRenderer;
    define('CSRF_SECRET', 'c2ioeEU1n48QF2WsHGWd2HmiuUUT6dxr');
    // Set up the CSRF provider
    $csrfProvider = new DefaultCsrfProvider(CSRF_SECRET);
    $renderer = new TwigRenderer($formEngine, $csrfProvider);
    // ...
    $formFactory = $formFactoryBuilder
    ->addExtension(new CsrfExtension($csrfProvider))
    ->getFormFactory();

    View Slide

  40. Activer la protection CSRF


    Your profile






    Save changes
    Cancel


    View Slide

  41. Formulaires
    Type de formulaire
    personnalisé

    View Slide

  42. Créer un type personnalisé
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\OptionsResolver\OptionsResolverInterface;
    class PersonType extends AbstractType
    {
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array('data_class' => 'Person'));
    }
    public function getName()
    {
    return 'person';
    }
    }

    View Slide

  43. // ...
    use Symfony\Component\Form\FormBuilderInterface;
    class PersonType extends AbstractType
    {
    public function buildForm(
    FormBuilderInterface $builder,
    array $options
    )
    {
    $builder
    ->add('name')
    ->add('password', 'repeated', array(...))
    ->add('bio', 'textarea')
    ->add('gender', 'choice', array(...))
    ->add('active', 'checkbox')
    ;
    }
    }

    View Slide

  44. Créer un type personnalisé
    $person = new Person();
    $person->setName('John Doe');
    // ...
    $options = array('trim' => true);
    $form = $formFactory
    ->create(new PersonType(), $person, $options)
    ;

    View Slide

  45. Enregistrer un nouveau type
    # in your code...
    $form = $formFactory->create('person', $person);
    # bootstrap.php
    $formFactory = Forms::createFormFactoryBuilder()
    // [...]
    ->addType(new PersonType(), 'person')
    ->getFormFactory()
    ;

    View Slide

  46. Formulaires
    Gestion des fichiers

    View Slide

  47. Gérer l’envoi de fichiers
    # bootstrap.php
    // ...
    use Symfony\…\HttpFoundation\HttpFoundationExtension;
    $formFactoryBuilder = Forms::createFormFactoryBuilder()
    $formFactory = $formFactoryBuilder
    ->addExtension(new HttpFoundationExtension())
    // [...]
    ->getFormFactory()
    ;

    View Slide

  48. Gérer l’envoi de fichiers
    class PersonType extends AbstractType
    {
    function buildForm(FormBuilderInterface $builder, …)
    {
    $builder
    // […]
    ->add('picture', 'file', array(
    'required' => false,
    ))
    ->add('active', 'checkbox')
    ;
    }
    }

    View Slide

  49. Générer l’attribut « enctype »



    View Slide

  50. Traiter le formulaire
    use Symfony\Component\HttpFoundation\Request;
    $request = Request::createFromGlobals()
    $request->overrideGlobals();
    if ($request->isMethod('POST')) {
    $form->bind($request);
    var_dump($form->getData());
    }

    View Slide

  51. Traiter le formulaire
    object(Person)
    private 'name' => 'John Doe'
    private 'picture' =>
    object(Symfony\Component\HttpFoundation\File\UploadedFile)
    private 'test' => false
    private 'originalName' => '445.jpg'
    private 'mimeType' => 'image/jpeg'
    private 'size' => 21645
    private 'error' => 0
    private 'password' => 'secret'
    private 'bio' => 'Famous actor!'
    private 'gender' => 'm'
    private 'active' => true

    View Slide

  52. Traiter le formulaire
    $file = $form->get('picture')->getData();
    $target = __DIR__. '/uploads';
    if ($file->isValid()) {
    $new = $file->move($target, 'jdoe.jpg');
    }

    View Slide

  53. Formulaires
    Champs non associés

    View Slide

  54. Définir un champ non associé
    $builder
    ->add('rules', 'checkbox', array(
    'mapped' => false,
    ))
    ;

    View Slide

  55. array(
    'name' => 'John Doe'
    'password' => 'secret'
    'bio' => 'Actor!'
    'gender' => 'm'
    'picture' => null
    'active' => true
    )
    Champ non associé
    Pas de donnée
    « rules »

    View Slide

  56. Formulaires
    Collections de champs

    View Slide

  57. Définir une collection de champs
    $builder
    // ...
    ->add('hobbies', 'collection', array(
    'allow_add' => true,
    'allow_delete' => true,
    ))
    // ...
    ;

    View Slide

  58. Définir une collection de champs
    $person = new Person();
    $person->setName('John Doe');
    $person->addHobby('music');
    $person->addHobby('movies');
    $person->addHobby('travels');

    View Slide

  59. Définir une collection de champs

    View Slide

  60. Conventions de nommage
    class Person
    {
    public function addHobby($hobby)
    {
    $this->hobbies[] = $hobby;
    }
    public function removeHobby($hobby)
    {
    $key = array_search($hobby, $this->hobbies);
    if (false !== $key) {
    unset($this->hobbies[$key]);
    }
    }
    }

    View Slide

  61. Prototype HTML d’ajout de champ

    View Slide

  62. Formulaires
    Formulaires imbriqués

    View Slide

  63. Imbriquer les formulaires
    class Address
    {
    private $street;
    private $zipCode;
    private $city;
    private $state;
    private $country;
    // ...
    }

    View Slide

  64. class AddressType extends AbstractType
    {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $builder
    ->add('street', 'textarea')
    ->add('zipCode')
    ->add('city')
    ->add('state')
    ->add('country', 'country')
    ;
    }
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array('data_class' => 'Address'));
    }
    public function getName()
    {
    return 'address';
    }
    }

    View Slide

  65. Imbriquer les formulaires
    class PersonType extends AbstractType
    {
    function buildForm(FormBuilderInterface $builder, …)
    {
    $builder
    // ...
    ->add('address', new AddressType())
    ;
    }
    }

    View Slide

  66. Imbriquer les formulaires
    class PersonType extends AbstractType
    {
    function buildForm(FormBuilderInterface $builder, …)
    {
    $builder
    // ...
    ->add('address', 'address')
    ;
    }
    }

    View Slide

  67. Imbriquer les formulaires
    $formBuilder = Forms::createFormFactoryBuilder();
    $formFactory = $formBuilder
    // ...
    ->addType(new AddressType(), 'address')
    ->addType(new PersonType(), 'person')
    ->getFormFactory()
    ;

    View Slide

  68. Vue arborescente du formulaire
    form   form bio   textarea
    name   text
    …   ...
    Champ   Type  
    address   Address
    zipCode   text
    address   textarea
    city   text
    country   country
    state   text

    View Slide

  69. Person Object
    (
    [name] => Hugo Hamon
    [picture] =>
    [username] => hhamon
    [password] => secret
    [address] => Address Object
    (
    [street] => 42 Sunshine Street
    [zipCode] => 12345
    [city] => Miami
    [state] => Florida
    [country] => US
    )
    [bio] => Speaker at conference
    [gender] => m
    [active] => 1
    [hobbies] => Array
    (
    [1] => movies
    [2] => travels
    [3] => conferences
    )
    )

    View Slide

  70. Formulaires
    I18N & L10N

    View Slide

  71. $builder
    ->add('birthdate', 'birthday', array('format' => 'd/M/y'))
    ->add('salary', 'money', array('currency' => 'EUR'))
    ->add('language', 'language', array(
    'preferred_choices' => array('fr'),
    )
    ->add('country', 'country', array(
    'preferred_choices' => array('FR'),
    )
    ->add('timezone', 'timezone', array(
    'preferred_choices' => array('Europe/Paris')
    )
    ;
    Types de champs régionalisés

    View Slide

  72. Locale: fr_FR
    Locale: en_US

    View Slide

  73. Formulaires
    Gestion des thèmes

    View Slide

  74. {% form_theme form _self %}
    {% block password_widget %}

    {{ block('field_widget') }}




    {% endblock password_widget %}
    Changer le rendu de tous les champs de
    saisie de mot de passe

    View Slide

  75. {% form_theme form _self %}
    {% block _person_username_widget %}

    {{ block('field_widget') }}




    {% endblock _person_username_widget %}
    Changer le rendu d’un champ précis

    View Slide

  76. Rendu personnalisé de champs

    View Slide

  77. Validation
    Le validateur

    View Slide

  78. // ...
    use Symfony\Component\Validator\Validation;
    use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
    $validator = Validation::createValidatorBuilder()
    ->getValidator()
    ;
    $formFactoryBuilder = Forms::createFormFactoryBuilder()
    $formFactory = $formFactoryBuilder
    // ...
    ->addExtension(new ValidatorExtension($validator))
    ->getFormFactory()
    ;
    Configurer le validateur

    View Slide

  79. Contraintes & Validateurs
    Pour chaque règle de validation, le composant
    embarque une classe de type Constraint et sa
    classe Validator associée.
    L’objet Constraint décrit la règle à valider et
    l’objet Validator est l’implementation de la
    logique métier associée à cette règle.

    View Slide

  80. // ...
    use Symfony\Component\Validator\Constraints\NotBlank as Assert;
    class PersonType extends AbstractType
    {
    public function buildForm(...)
    {
    $builder->add('name', 'text', array(
    'constraints' => array(
    new Assert\NotBlank(),
    new Assert\Length(array('min' => 5, 'max' => 40)),
    ),
    ));
    // ...
    }
    }
    Association des contraintes aux champs

    View Slide

  81. $genders = array('m' => 'Male', 'f' => 'Female');
    $builder->add('gender', 'choice', array(
    'choices' => $genders,
    'constraints' => array(
    new Assert\NotBlank(),
    new Assert\Choice(array(
    'choices' => array_keys($genders),
    'message' => 'Alien genders are not supported.',
    )),
    ),
    ));
    Association des contraintes aux champs

    View Slide

  82. Validation du formulaire
    $request = Request::createFromGlobals();
    $request->overrideGlobals();
    if ($request->isMethod('POST')) {
    $form->bind($request);
    if ($form->isValid()) {
    $data = $form->getData();
    // ... handle sanitized data
    }
    }

    View Slide

  83. Validation du formulaire

    View Slide

  84. Classes de contraintes natives
    §  All
    §  Blank
    §  Callback
    §  Choice
    §  Collection
    §  Count
    §  Country
    §  Date
    §  DateTime
    §  Email
    §  False
    §  File
    §  Image
    §  Ip
    §  Language
    §  Length
    §  Locale
    §  NotBlank
    §  NotNull
    §  Null
    §  Range
    §  Regex
    §  Time
    §  True
    §  Type
    §  Url
    §  Valid…

    View Slide

  85. Validation
    Formats de configuration

    View Slide

  86. # bootstrap.php
    // ...
    $validator = Validation::createValidatorBuilder()
    ->addMethodMapping('loadValidatorMetadata')
    ->addXmlMapping(__DIR__.'/config/validation.xml')
    ->addYamlMapping(__DIR__.'/config/validation.yml')
    ->enableAnnotationMapping()
    ->getValidator()
    ;
    Configurer le validateur

    View Slide

  87. use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Constraints\NotBlank;
    use Symfony\Component\Validator\Constraints\Length;
    class Person
    {
    private $name;
    // ...
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('name', new NotBlank());
    $metadata->addPropertyConstraint('name', new Length(array(
    'min' => 5,
    'max' => 40,
    )));
    // ...
    }
    }
    Configuration en PHP

    View Slide

  88. # src/config/validation.yml
    Person:
    properties:
    name:
    - NotBlank: ~
    - Length: { 'min': 5, 'max': 40 }
    # ...
    getters:
    # ...
    constraints:
    # ...
    Configuration en YAML

    View Slide








  89. 5
    40




    Configuration en XML

    View Slide

  90. use Symfony\Component\Validator\Constraints as Assert;
    class Person
    {
    /**
    * @Assert\NotBlank()
    * @Assert\Length(min = 5, max = 40)
    */
    private $name;
    // ...
    }
    Configuration en annotations

    View Slide

  91. Validation
    Ajouter des contraintes

    View Slide

  92. Contraintes de propriétés
    use Symfony\Component\Validator\Constraints\Image;
    class Person
    {
    private $picture;
    // ...
    static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('picture', new Image(array(
    'minWidth' => 100,
    'maxWidth' => 150,
    'minHeight' => 100,
    'maxHeight' => 150,
    )));
    // ...
    }
    }

    View Slide

  93. class Person
    {
    //...
    static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addConstraint(new Unique(array(
    'field' => 'username',
    'message' => 'This username already exist.',
    )));
    //...
    }
    }
    Contraintes de classe

    View Slide

  94. use Symfony\Component\Validator\Constraints\True;
    class Person
    {
    private $username;
    private $password;
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addGetterConstraint('passwordValid', new True(array(
    'message' => 'Password and username cannot be same.',
    )));
    // ...
    }
    public function isPasswordValid()
    {
    return strtolower($this->username) !== strtolower($this->password);
    }
    }
    Contraintes de méthodes

    View Slide

  95. Contraintes de méthodes
    Le message d’erreur
    remonte comme une
    erreur globale du
    formulaire.

    View Slide

  96. class PersonType extends AbstractType
    {
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
    'data_class' => 'Person',
    'error_mapping' => array('passwordValid' => 'password'),
    ));
    }
    }
    Associer une erreur globale à un champ

    View Slide

  97. Associer une erreur globale à un champ
    Le message d’erreur est
    désormais réaffecté au
    champ « password ».

    View Slide

  98. Validation
    Traduction des messages

    View Slide

  99. use Symfony\Component\Translation\Translator;
    use Symfony\Component\Translation\Loader\XliffFileLoader;
    use Symfony\Bridge\Twig\Extension\TranslationExtension;
    $t = new Translator('fr');
    $t->addLoader('xlf', new XliffFileLoader());
    // Built in translations
    $t->addResource('xlf', FORM_DIR . '/Resources/translations/validators.fr.xlf', 'fr', 'validators');
    $t->addResource('xlf', VALIDATOR_DIR . '/Resources/translations/validators.fr.xlf', 'fr', 'validators');
    // Your translations
    $t->addResource('xlf', __DIR__. '/translations/validators.fr.xlf', 'fr', 'validators');
    // ...
    $twig->addExtension(new TranslationExtension($t));
    // ...
    Activer le support des traductions

    View Slide



  100. original="file.ext">


    Password and username cannot be same.
    Le mot de passe et le nom d'utilisateur doivent
    être différents.




    Traduire les messages d’erreur

    View Slide

  101. Validation
    Configuration avancée

    View Slide

  102. Groupes de validation
    class Person
    {
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
    $metadata->addPropertyConstraint('password', new NotBlank(array(
    'groups' => array('Signup'),
    )));
    $metadata->addGetterConstraint('passwordValid', new True(array(
    'message' => 'Password and username cannot be same.',
    'groups' => array('Signup', 'Profile'),
    )));
    // ...
    }
    }

    View Slide

  103. class RegistrationType extends AbstractType
    {
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
    // ...
    'validation_groups' => array('Signup'),
    ));
    }
    }
    class EditAccountType extends AbstractType
    {
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    $resolver->setDefaults(array(
    // ...
    'validation_groups' => array('Profile'),
    ));
    }
    }

    View Slide

  104. Conclusion

    View Slide

  105. Ressources officielles
    §  http://symfony.com/doc/current/book/forms.html
    §  http://symfony.com/doc/current/reference/forms/types.html
    §  http://symfony.com/doc/current/book/validation.html
    §  http://symfony.com/doc/current/reference/constraints.html
    §  http://symfony.com/doc/current/reference/forms/twig_reference.html
    §  https://github.com/bschussek/standalone-forms
    Ressources utiles

    View Slide

  106. Merci !
    Hugo Hamon
    [email protected]
    @hhamon

    View Slide