Commit 012f7638 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Gestion de formulaire multipage (MultipageForm*) : déplacement dans la...

Gestion de formulaire multipage (MultipageForm*) : déplacement dans la nouvelle bibliothèque unicaen/multipage-form.
parent e764b575
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
CHANGELOG
=========

7.3.0
-----
- Gestion de formulaire multipage (MultipageForm*) : déplacement dans la nouvelle bibliothèque unicaen/multipage-form.

7.2.1
-----

+0 −4
Original line number Diff line number Diff line
@@ -354,10 +354,6 @@ return [
            'modalAjaxDialog'           => 'UnicaenApp\View\Helper\ModalAjaxDialog',
            'confirm'                   => 'UnicaenApp\View\Helper\ConfirmHelper',
            'toggleDetails'             => 'UnicaenApp\View\Helper\ToggleDetails',
            'multipageFormFieldset'     => 'UnicaenApp\Form\View\Helper\MultipageFormFieldset',
            'multipageFormNav'          => 'UnicaenApp\Form\View\Helper\MultipageFormNav',
            'multipageFormRow'          => 'UnicaenApp\Form\View\Helper\MultipageFormRow',
            'multipageFormRecap'        => 'UnicaenApp\Form\View\Helper\MultipageFormRecap',
            'formDate'                  => 'UnicaenApp\Form\View\Helper\FormDate',
            'formDateTime'              => Form\View\Helper\FormDateTime::class,
            'formDateInfSup'            => 'UnicaenApp\Form\View\Helper\FormDateInfSup',
+0 −31
Original line number Diff line number Diff line
@@ -2,17 +2,6 @@ Formulaires
===========


Formulaires
-----------

### MultipageForm

Classe mère des formulaire multi-pages (saisie en plusieurs étapes).

Cf. documentation du plugin de contrôleur
[MultipageFormPlugin](./Plugins.md#multipageformplugin).


Éléments de formulaire
----------------------

@@ -164,26 +153,6 @@ code HTML complet de cet élément (labels, champs et erreurs) est
\</note\>


### MultipageFormNavElement

Élément composite de navigation au sein d\'un formulaire multipage
(formulaire en plusieurs étapes)
[MultipageForm](/develop/unicaen2/moduleunicaenunicaenapp/form/MultipageForm).

Les boutons présents dépendent du contexte (de l\'étape courante) et du
paramétrage :

-   \"Précédent\"
-   \"Suivant\"
-   \"Terminer\"
-   \"Confirmer et enregistrer\"
-   \"Annuler\"

Cette aide de vue n\'a pas vraiment vocation à être utilisée directement
: elle est utilisée en interne par l\'aide de vue
[MultipageFormRow](/develop/unicaen2/moduleunicaenunicaenapp/viewhelpers/multipageformrow).


### SearchAndSelect

Élément de formulaire permettant de rechercher puis sélectionner quelque
+0 −318
Original line number Diff line number Diff line
@@ -152,321 +152,3 @@ public function popoverAction()
    return $viewModel;
}
```

MultipageFormPlugin
-------------

Plugin de contrôleur facilitant la mise en œuvre d'un processus de
saisie en plusieurs étapes (formulaire multi-pages) :

-   les infos saisies à l'étape courante doivent être valides pour
    pouvoir passer à l'étape suivante
-   stockage en session des infos saisies à chaque étape
-   application du pattern
    [Post-Redirect-Get](http://fr.wikipedia.org/wiki/Post-Redirect-Get)
    (délégation au [plugin fourni par ZF](http://framework.zend.com/manual/2.0/en/modules/zend.mvc.plugins.html#the-post-redirect-get-plugin))
-   possibilité de revenir en arrière sans perdre les infos saisies
    (même en cas de saisie partielle à l'étape courante)

### Formulaire

L'idée est de créer un formulaire global héritant de la classe
`\UnicaenApp\Form\MultipageForm` composé de plusieurs fieldsets, chaque
fieldset correspondant à une étape de la saisie.

Exemple d'un formulaire de contact avec une saisie en 3 étapes
(identité, coordonnées, divers) :

```php
namespace Application\Form;

use UnicaenApp\Form\MultipageForm;
use Laminas\Form\Element\Csrf;
use Laminas\Form\Element\Submit;

class ContactForm extends MultipageForm
{
    public function __construct($name = null, $options = array())
    {
        parent::__construct($name, $options);

        $this->addFieldsetFirst(new IdentFieldset('identite'))
             ->addFieldsetNext(new CoordFieldset('coordonnees'))
             ->addFieldsetLast(new DiversFieldset('divers'))
             ->add(new Csrf('csrf'))
             ->add(new Submit('ajouter', array('label'=>"Ajouter")));
    }
}
```

Chaque fieldset hérite de la classe `\Laminas\Form\Fieldset`.

Exemple du fieldset de saisie des coordonnées :

```php
namespace Application\Form;

use Laminas\Form\Element\Text;
use Laminas\Form\Element\Textarea;
use Laminas\Form\Fieldset;
use Laminas\InputFilter\InputFilterProviderInterface;

class CoordFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct($name = null, $options = array())
    {
        parent::__construct($name, $options);

        $this->setLabel("Coordonnées")
             ->add(new Textarea('adresse', array('label'=>"Adresse postale")))
             ->add(new Text('email', array('label'=>"Adresse mail")));
    }

    public function getInputFilterSpecification()
    {
        return array(
            'adresse' => array(
                'required' => false,
                'filters'  => array(
                    array('name' => '\Laminas\Filter\StringTrim'),
                ),
            ),
            'email' => array(
                'required' => true,
                'filters'  => array(
                    array('name' => '\Laminas\Filter\StringTrim'),
                ),
                'validators' => array(
                    array(
                        'name'=> 'NotEmpty',
                        'break_chain_on_failure' => true,
                        'options' => array(
                            'messages' => array('isEmpty' => "L'adresse mail est requise"),
                        )
                    ),
                    array(
                        'name' => '\Laminas\Validator\EmailAddress',
                        'options' => array(
                            'messages' => array('emailAddressInvalidFormat' => "L'adresse mail spécifiée est invalide"),
                        ),
                    ),
                ),
            ),
        );
    }
}
```

*Implémenter l'interface `InputFilterProviderInterface` (méthode
`getInputFilterSpecification()`) n'est pas obligatoire mais elle permet
de fournir les règles de filtrage et de validation des valeurs saisie
dans le fieldset.*

### Contrôleur

Pour fonctionner, le plugin a besoin de connaître :

- Le formulaire global :
  * Il doit donc être fourni à **chaque** invocation du plugin : `$this->multipageForm($this->getForm())`.
  
- La table de correspondance qui associe à chacun des fieldsets du formulaire une action du contrôleur :
  * Par défaut, le nom de l'action correspondant à un fieldset est le nom de ce fieldset. Par exemple, le fieldset 
    `new CoordFieldset('coordonnees')` correspondra à l'action nommée `coordonnees` (i.e. méthode `coordonneesAction()`
    du contrôleur).
  * Cette table de correspondance est obtenue par le plugin auprès du formulaire, vous pouvez donc spécifier une table 
    différente en appelant la méthode `setFieldsetActionMapping()` du formulaire.
    
- L'action du contrôleur correspondant au récapitulatif et à la demande de confirmation de la saisie complète (facultatif)
  * Par défaut, c'est `'confirmer'`.
  * Elle est obtenue par le plugin auprès du formulaire, vous pouvez donc spécifier une action différente grâce à la 
    méthode `setConfirmAction()` du formulaire.
    
- L'action du contrôleur correspondant à l'enregistrement de la saisie
  * Par défaut, c'est `'enregistrer'`.
  * Elle est obtenue par le plugin auprès du formulaire, vous pouvez donc spécifier une action différente grâce à la 
    méthode `setProcessAction()` du formulaire.
    
- L'action du contrôleur correspondant à l'abandon à tout moment de la saisie
  * Par défaut, c'est `'annuler'`.
  * Elle est obtenue par le plugin auprès du formulaire, vous pouvez donc spécifier une action différente grâce à la 
    méthode `setCancelAction()` du formulaire.

*NB: il est possible de spécifier un préfixe à
appliquer à **tous** les noms d'actions grâce à la méthode
`setActionPrefix()` du formulaire. Dans notre exemple, le préfixe
`'ajouter-`' est spécifié pour pointer vers les méthodes :*

-   `ajouterIdentiteAction()` plutôt que vers `identiteAction()`
-   `ajouterCoordonneesAction()` plutôt que vers `coordonneesAction()`
-   `ajouterDiversAction()` plutôt que vers `diversAction()`
-   `ajouterConfirmerAction()` plutôt que vers `confirmerAction()`
-   `ajouterEnregistrerAction()` plutôt que vers `enregistrerAction()`
-   `ajouterAnnulerAction()` plutôt que vers `annulerAction()`

Exemple de contrôleur pour la saisie d'une candidature :

-   l'action `ajouter` est le point d'entrée du processus
-   l'action `ajouter-identite` est la première étape
-   l'action `ajouter-coordonnees` est la deuxième étape
-   l'action `ajouter-divers` est la troisième et dernière étape
-   l'action `ajouter-confirmer` est chargée de récapituler les infos saisies et demander confirmation
-   l'action `ajouter-enregistrer` est chargée d'enregistrer les infos saisies dans une base de données par exemple
-   l'action `ajouter-annuler` correspond à la requête d'abandon de la saisie

```php
namespace Application\Controller;

use Application\Form\ContactForm;
use UnicaenApp\Form\MultipageForm;
use Laminas\Http\PhpEnvironment\Response;
use Laminas\Mvc\Controller\AbstractActionController;

class DemandeController extends AbstractActionController
{
    protected $form;

    public function ajouterAction()
    {
        return $this->multipageForm($this->getForm())->start(); // réinit du plugin et redirection vers la 1ère étape
    }

    public function ajouterIdentiteAction()
    {
        return $this->multipageForm($this->getForm())->process();
    }

    public function ajouterCoordonneesAction()
    {
        return $this->multipageForm($this->getForm())->process();
    }

    public function ajouterDiversAction()
    {
        return $this->multipageForm($this->getForm())->process();
    }

    public function ajouterAnnulerAction()
    {
        return $this->redirect()->toRoute('home');
    }

    public function ajouterConfirmerAction()
    {
        $response = $this->multipageForm($this->getForm())->process();
        if ($response instanceof Response) {
            return $response;
        }
        return array('form' => $this->getForm());
    }

    public function ajouterEnregistrerAction()
    {
        $data = $this->multipageForm($this->getForm())->getFormSessionData();
        // ...
        // enregistrement en base de données (par exemple)
        // ...
        return $this->redirect()->toRoute('home');
    }

    protected function getForm()
    {
        if (null === $this->form) {
            $this->form = new ContactForm('contact');
            $this->form->setActionPrefix('ajouter-');
        }
        return $this->form;
    }
}
```

### Vue

#### Étape de saisie

Un script de vue par étape de saisie doit être fourni, dans lequel
l'aide de vue `MultipageFormFieldset` doit être utilisée. Cette aide de
vue génère un titre (h2) indiquant "Étape i sur N", le code HTML du
fieldset de l'étape en cours ainsi que les boutons de navigation.

Exemple :

```phtml
<h1>Formulaire de contact</h1>
<?php echo $this->multipageFormFieldset(); ?>
```

*Inscrivez le même titre h1 pour toutes les étapes.*

#### Confirmation

Vous devez fournir le script de vue pour la page de
récapitulatif/confirmation et utiliser l'aide de vue
`MultipageFormRecap`. Cette aide de vue génère automatiquement la liste
de toutes les informations saisies et les boutons de navigation.

Exemple :

```phtml
<h1>Formulaire de contact</h1>
<h2>Récapitulatif et confirmation des informations saisies</h2>
<?php echo $this->multipageFormRecap(); ?>
```

*L'aide de vue `MultipageFormRecap` explore
automatiquement tous les éléments visibles de tous les fieldsets du
formulaire afin de collecter leur labels et leur valeurs et les
présenter sous la forme d'une liste de définition (dl). Si vous
souhaitez maîtriser plus finement la façon dont sont constitués les
labels ou les valeurs d'un fieldset, faites implémenter à la classe de
ce fieldset l'interface
`\UnicaenApp\Form\MultipageFormFieldsetInterface` pour fournir via la
méthode `getLabelsAndValues()` les labels et valeurs de tous les
éléments de ce fieldset.*

Exemple :

```php
namespace Application\Form;

use Unicaen\Exception;
use UnicaenApp\Form\MultipageFormFieldsetInterface;
use Laminas\Form\Element\Text;
use Laminas\Form\Fieldset;
use Laminas\InputFilter\InputFilterProviderInterface;

class DiversFieldset extends Fieldset implements InputFilterProviderInterface, MultipageFormFieldsetInterface
{
    public function __construct($name = null, $options = array())
    {
        parent::__construct($name, $options);

        $this->setLabel("Divers")
                ->add(new Text('age', array('label' => "Age")))
                ->add(new Text('profession', array('label' => "Profession", 'placeholder' => "Fonctionnaire")));
    }

    ...

    public function getLabelsAndValues($data = null)
    {
        if (null === $data && !($data = $this->getValue())) {
            throw new Exception("Aucune donnée saisie disponible.");
        }
        if (array_key_exists($this->getName(), $data)) {
            $data = $data[$this->getName()];
        }

        $values = array();

        $elem = $this->get('age');
        $values[$elem->getName()]['label'] = $elem->getLabel();
        $values[$elem->getName()]['value'] = $data[$elem->getName()] ? $data[$elem->getName()] . " ans" : "Non renseigné";

        $elem = $this->get('profession');
        $values[$elem->getName()]['label'] = $elem->getLabel();
        $values[$elem->getName()]['value'] = $data[$elem->getName()] ? $data[$elem->getName()] : "Chômage";

        return $values;
    }
}
```
+0 −71
Original line number Diff line number Diff line
@@ -1016,77 +1016,6 @@ ressemble à cela :
)
```

MultipageFormFieldset
=====================

Aide de vue à utiliser dans chaque vue associée aux étapes de saisie
d\'un formulaire multi-pages.

Cette aide de vue génère un titre (h2) indiquant \"Étape i sur N\", le
code HTML du fieldset de l\'étape en cours ainsi que les boutons de
navigation.

Exemple :

``` {.php}
<h1>Formulaire de contact</h1>
<?php echo $this->multipageFormFieldset(); ?>
```

\<note important\>Le fieldset courant est transmis à la vue par le
plugin de contrôleur
[MultipageFormPlugin](/develop/unicaen2/moduleunicaenunicaenapp/controllerplugins/multipageform)
via la variable de vue `fieldset`.\</note\>

MultipageFormNav
================

Aide de vue générant l\'élément de navigation (de type
`\UnicaenApp\Form\Element\MultipageFormNav`) au sein d\'un formulaire
multi-pages.

MultipageFormRecap
==================

Aide de vue à utiliser dans la vue associée à l\'étape de confirmation
de la saisie d\'un formulaire multi-pages.

Cette aide de vue génère automatiquement la liste de toutes les
informations saisies et les boutons de navigation.

Exemple :

``` {.php}
<h1>Récapitulatif et confirmation des informations saisies</h1>
<?php echo $this->multipageFormRecap(); ?>
```

\<note important\>Le formulaire complet est transmis à la vue par le
plugin de contrôleur
[MultipageFormPlugin](/develop/unicaen2/moduleunicaenunicaenapp/controllerplugins/multipageform)
via la variable de vue `form`.\</note\>

\<note important\> Cette aide de vue explore automatiquement tous les
éléments visibles de tous les fieldsets du formulaire afin de collecter
leur labels et leur valeurs et les présenter sous la forme d\'une liste
de définition (dl). Si vous souhaitez maîtriser plus finement la façon
dont sont constitués les labels ou les valeurs d\'un fieldset, faites
implémenter à la classe de ce fieldset l\'interface
`\UnicaenApp\Form\MultipageFormFieldsetInterface` pour fournir via la
méthode `getLabelsAndValues()` les labels et valeurs de tous les
éléments de ce fieldset. \</note\>

MultipageFormRow
================

Aide de vue générant chaque élément d\'un fieldset de formulaire
multi-page.

Délègue le travail à l\'aide de vue standard `FormRow` pour tous les
éléments, sauf pour ceux de type `MultipageFormNav` traitée par l\'aide
de vue
`[[develop:unicaen2:moduleunicaenunicaenapp:viewhelpers:multipageformnav|MultipageFormNav]]`.

Divers
------

Loading