00:00:00

Introduction à Zend\Form

Michaël Gallego

Octobre 2012

Code source en ligne : https://github.com/bakura10/ZendFormWebinar

Notes

Michaël Gallego

  • Etudiant en école d'ingénieur (5ème année)
  • Contributeur ZF 2 depuis 2012…
  • … et surtout de Zend\Form
  • Contributeur de DoctrineModule/DoctrineORMModule

Notes

Le problème de Zend_Form

Un composant "fourre-tout" et complexe :

  • validation/filtrage attachés aux éléments
  • compliqué à réutiliser
  • de merveilleux souvenirs avec les décorateurs...

Notes

De nouveaux composants sous ZF 2

  • Réécriture de Zend\Form
  • Nouveau composant Zend\InputFilter
  • Des aides de vue
  • Utilisation des hydrateurs pour transférer les données

Notes

Éléments disponibles

Zend\Form fournit de nombreux éléments par défaut :

  • Button
  • Captcha
  • Checkbox
  • Collection
  • Color
  • Csrf
  • Date
  • DateSelect (ZF 2.1)
  • DateTime
  • DateTimeLocal
  • Email
  • File
  • Hidden
  • Image
  • Month
  • MonthSelect (ZF 2.1)
  • MultiCheckbox
  • Number
  • Password
  • Radio
  • Range
  • Select
  • Submit
  • Text
  • Textarea
  • Time
  • Url
  • Week

Notes

Validation des données

Zend\InputFilter permet de :

  • Filtrer les données
  • Valider les données

Pour créer un InputFilter :

  • Ajouter manuellement des objets Input
  • Utiliser la fabrique avec un tableau PHP

Notes

Transfert des données

Utilisation des hydrateurs :

  • Les données de l'objet sont extraites pour pré-remplir les champs.
  • Si le formulaire est valide, les données sont envoyées à l'objet

Plusieurs stratégies :

  • ArraySerializable, ClassMethods, ObjectProperty
  • ...

Notes

Transfert des données : hydrateurs

interface HydratorInterface
{
    /**
     * Extrait les valeurs de l'objet
     */
    public function extract($object);

    /**
     * Hydrate $object avec les valeurs dans $data
     */
    public function hydrate(array $data, $object);
}

Notes

Exemple des hydrateurs : extraction

class Foo
{
    public $a = 'bar';
}

$hydrator = new \Zend\Stdlib\Hydrator\ObjectProperty();
var_dump($hydrator->extract(new Foo()));

// Affiche : array('a' => 'bar');

Notes

Exemple des hydrateurs : modification

class Foo
{
    public $a = 'bar';
}

$hydrator = new \Zend\Stdlib\Hydrator\ObjectProperty();
$object   = $hydrator->hydrate(array('a' => 'baz'), new Foo());
var_dump($object);

// Affiche object(Foo)[201]
//             public 'a' => string 'baz' (length=3)

Notes

Rendu du formulaire

Utilisation d'aides de vue dédiées :

  • Bye bye $form->render() !
  • formSubmit(), formEmail(), formSelect()…
  • Meilleur contrôle !

Notes

Perdu(e) ?

Voyons un exemple…

Notes

Objectif

Notes

Exemple

Nous souhaitons créer un formulaire pour ajouter un concert :

  • Entité Groupe (nom, site web)
  • Entité Concert (nombre de sièges, groupe)
  • Relation 1--1 entre Concert et Groupe

Notes

Les entités

namespace Application\Entity;

class Groupe
{
    /** @var string */
    public $nom;

    /** @var string */
    public $site;
}

Notes

Les entités

namespace Application\Entity;

class Concert
{
    /** @var Groupe */
    public $groupe;

    /** @var int */
    public $nombreSieges;
}

Notes

Création des fieldsets

Les entités "correspondent" à un fieldset :

  • Réutilisation des règles de validation
  • Réutilisation dans différents formulaires

Notes

Création des fieldsets

namespace Application\Form;

use Application\Entity\Groupe;
use Zend\Form\Fieldset;
use Zend\Stdlib\Hydrator\ObjectProperty;

class GroupeFieldset extends Fieldset
{
    public function __construct()
    {
        parent::__construct('groupe');

        $this->setObject(new Groupe())
             ->setHydrator(new ObjectProperty());

        // Ajout des éléments...
        []
    }
}

Notes

Création des fieldsets

// Dans GroupeFieldset::__construct
$this->add(array(
    'name'    => 'nom',
    'options' => array(
        'label' => 'Nom'
    )
));

$this->add(array(
    'type'    => 'Zend\Form\Element\Url',
    'name'    => 'site',
    'options' => array(
        'label' => 'Site web'
    )
));

Notes

Un peu plus de HTML5 ?

$this->add(array(
    'name' => 'site',
    'options' => array(
        'label' => 'Site web'
    ),
    'attributes' => array(
        'required'   => 'required',
        'placeholder => 'http://'
    )       
));

Notes

Validation / filtrage

use Zend\InputFilter\InputFilterProviderInterface;

class GroupeFieldset extends Fieldset 
    implements InputFilterProviderInterface
{
    public function getInputFilterSpecification()
    {
        return array(
            'nom' => array(
                'required'   => true,
                'filters'    => array(
                    array('name' => 'StringTrim')
                ),
                'validators' => array(
                    array('name' => 'StringLength', array('max' => 48))
                )
            )
        );
    }
}

Notes

Création des fieldsets

class ConcertFieldset extends Fieldset
{
    public function __construct()
    {
        parent::__construct('concert');

        $this->setObject(new Concert())
             ->setHydrator(new ObjectProperty());

        // Ajout des éléments
        []
    }
}

Notes

Création des fieldsets

// Dans ConcertFieldset::construct__()
$this->add(array(
    'type'    => 'Application\Form\GroupeFieldset',
    'name'    => 'groupe',
    'options' => array(
        'label' => 'Groupe'
    )
));

$this->add(array(
    'type'    => 'Zend\Form\Element\Number',
    'name'    => 'nombreSieges',
    'options' => array(
        'label' => 'Nombre de sièges dans la salle'
    ),
    'attributes' => array(
        'min' => 50
    )
));

Notes

Création du formulaire (1/2)

namespace Application\Form;

use Zend\Form\Form;
use Zend\Stdlib\Hydrator\ObjectProperty;

class CreerConcert extends Form
{
    public function __construct()
    {
        parent::__construct('creer-concert-form');

        $this->setHydrator(new ObjectProperty());

        // Ajout des éléments
        []
    }
}

Notes

Création du formulaire (2/2)

// Dans Application\Form\CreerConcert::__construct
$this->add(array(
    'type'    => 'Application\Form\ConcertFieldset',
    'name'    => 'concert',
    'options' => array(
        'use_as_base_fieldset' => true
    )
));

$this->add(array(
    'type'       => 'Zend\Form\Element\Submit',
    'name'       => 'submit',
    'attributes' => array(
        'value' => 'Créer ce concert !',
        'class' => 'btn-success'
    )
));

Notes

Affichage

// dans application/create-concert.phtml
$form = $this->form;
$form->setAttribute('action', $this->url(null, array(), true))
     ->prepare();

$concert = $form->get('concert');
$groupe  = $concert->get('groupe');

echo $this->form()->openTag($form); // <form action="…">

echo $this->formLabel($concert->get('nombreSieges'));
echo $this->formNumber($concert->get('nombreSieges'));
echo $this->formElementErrors($concert->get('nombreSieges'));

echo $this->formRow($groupe->get('nom'));
echo $this->formRow($groupe->get('site'));

echo $this->formSubmit($form->get('submit'));

echo $this->form()->closeTag($form); // </form>

Notes

Contrôleur

public function createConcertAction()
{
    $form = new CreerConcert();

    if ($this->request->isPost()) {
        $concert = new Concert();
        $form->bind($concert);
        $form->setData($this->request->getPost());

        if ($form->isValid()) {
            var_dump($concert);
        }
    }

    return new ViewModel(array(
        'form' => $form
    ));
}

Notes

Et pour un formulaire de mise à jour ?

Formulaire de mise à jour : nous ne souhaitons pouvoir modifier que le nombre de places dans la salle :

  • Réutilisation des fieldsets (inutile donc de réécrire les règles de validation)
  • Utilisation des groupes de validation pour définir les champs à valider

Notes

Formulaire de mise à jour

class UpdateConcert extends Form
{
    public function __construct()
    {
        parent::__construct('update-concert-form');

        $this->setHydrator(new ObjectProperty());

        $this->add(array(
            'type'    => 'Application\Form\ConcertFieldset',
            'name'    => 'concert',
            'options' => array(
                'use_as_base_fieldset' => true
            )
        ));

        $this->setValidationGroup(array(
            'concert' => array( 'nombreSieges')
        ));

Notes

Contrôleur

public function updateConcertAction()
{
    $form = new UpdateConcert();
    $concert = [...]; // récupère de la base de données par exemple
    $form->bind($concert);

    if ($this->request->isPost()) {
        $form->setData($this->request->getPost());

        if ($form->isValid()) {
            var_dump($concert);
        }
    }

    return new ViewModel(array(
        'form' => $form
    ));
}

Notes

Contrôleur

Dans un formulaire de mise à jour, on bind l'objet AVANT d'utiliser le formulaire, afin de le pré-remplir avec les valeurs déjà existantes.

Notes

Ce n'est qu'un aperçu…

Les formulaires de Zend Framework 2 sont très puissants :

  • Gestion des relations "ManyToMany" et "OneToMany" avec l'élément Collection
  • Imbrication des fieldsets pour des hiérarchies complexes
  • Annotations
  • A partir de ZF 2.1 : un nouveau composant pour facilement envoyer des fichiers

Si vous utilisez Doctrine 2…

  • Jetez un oeil à l'hydrateur de DoctrineModule !

Notes

A lire !

Quelques liens utiles pour approfondir :

Demain : webinar sur les Modules à 14h30 (présenté par Vincent Blanchon) Formations ZF 2 (en classe ou en ligne) : http://bit.ly/R7PWzZ

Notes

Michaël Gallego (bakura)

  • @mic_gallego
  • http://www.michaelgallego.fr
  • http://www.z-f.fr

Merci à Rob Allen (@akrabat) pour son aide

Notes