Dynamic-Mess.com


"The world is a dynamic mess of jiggling things..."

Symfony 2 : créer et gérer un formulaire

Article posté le 31-03-2015 dans la catégorie PHP

Symfony 2 propose plusieurs outils pour concevoir et traiter ses formulaires. Nous allons aborder ici quelques notions afin que vous puissiez vous débrouiller dans ce domaine. A la fin de ce tutoriel, vous en saurez assez pour celà, et vous pourrez compléter avec la documentation officielle. Allons-y!

Symfony 2

1- Préambule

Imaginons que nous voulions créer un formulaire pour créer une catégorie. Cette catégorie est représentée par une entité, qui comprend trois attributs :

2- Routes

Dans la classe de votre contrôleur qui contiendra le code pour gérer le formulaire, vous avez deux possibilités: soit utiliser un seule méthode soit séparer la création du formulaire de son traitement. Pour des raisons de clarté, c'est cette dernière que je vais vous présenter.

Dans le fichier routing.yml de votre module, créez deux routes (adaptez-les si besoin ) :

gestion_form_ajout_categorie:
    path:     /gestion/ajoutercateg/
    defaults: { _controller: VideoBundle:Gestion:formAjoutCateg }
    requirements:
        _method: GET
    
gestion_traitement_ajout_categorie:
    path:     /gestion/ajoutercateg/
    defaults: { _controller: VideoBundle:Gestion:traitementAjoutCateg }
    requirements:
        _method : POST

 

Vous remarquerez que les deux routes utilisent la même URL, sauf que l'une attendra une requête de type GET (affichage du formulaire) et l'autre une requête de type POST (traitement du formulaire) et qu'elles pointent chacune vers leur propre méthode du contrôleur.

3- Messages d'erreurs

Pour la gestion des messages d'erreurs - lors du traitement du formulaire par PHP -, vous avez plusieurs possibilités. En voici deux grandes :

A- Les annotations

Il s'agit simplement d'annotations rajoutées dans la classe de votre entité, aux côtés des annotations existantes, notamment celles de Doctrine.

Par exemple, pour l'attribut nom, je rajoute au milieu l'Assertion pour la longueur minimale de deux caractères :

/**
     * @var string
     *
     * @ORMColumn(name="Nom", type="string", length=50, nullable=false)
     * @AssertLength(min=2)
     */
    private $nom;

Pour utiliser ce système, votre entité doit avoir ceci en début de fichier :

use SymfonyComponentValidatorConstraints as Assert;

Personnellement, je n'utilise pas ce système car je ne crée que des applications unilingues, en français. Or, par défaut, le système des annotations utilise directement un message en anglais, ce qui nécessite un travail d'internationalisation. Du coup, je préfère utiliser la deuxième méthode, dans un fichier yml, où je spécifie directement un message en français.

Par contre, si un jour je dois faire du multilingue, je passerai par la méthode des annotations dans l'entité, car elle présente tout de même un avantage : tout est regroupé dans un seul et même fichier, ce qui n'est pas le cas avec la méthode qui suit.

B- Un fichier de contraintes

Comme son nom l'indique, il contient les contraintes de validation. C'est cette méthode que nous allons utiliser.

Dans le dossier de votre Bundle, allez dans Resources/config et créez un fichier validation.yml. Remplissez-le avec ceci (en adaptant bien entendu le nom de votre bundle) :

Eric\MultimediaBundle\Entity\Categorie:
    properties:
        nom:
            - Length:
                min: 2
                max: 50
                minMessage: "Le nom doit faire au moins {{ limit }} caractères"
                maxMessage: "Le nom ne peut pas être plus long que {{ limit }} caractères"
        url:
            - Length:
                min: 2
                max: 50
                minMessage: "L'URL doit faire au moins {{ limit }} caractères"
                maxMessage: "L'URL ne peut pas être plus long que {{ limit }} caractères"
            - Regex: 
                pattern: "/^[a-z0-9-]+$/"
                message: "L'URL ne peut contenir que des chiffres, des lettres ou des tirets"

                

Certains sont biens utiles, comme NotNull ou NotBlank.

Enfin, dans le fichier de configuration de Symfony (app/config/config.yml), vous devez vérifier que la ligne validation correspond bien à ceci :

validation:      { enabled: true }

Explications : Cela permet de dire à Symfony d'utiliser votre fichier de contraintes comme base de validation. Si vous aviez voulu utiliser les annotations, vous auriez du mettre ceci :

validation : { enable_annotations: true }

Ou si vous aviez souhaité utiliser les deux :

validation:    { enabled: true, enable_annotations: true }

Bon nous y sommes, passons au formulaire en lui-même !

4- Création du formulaire

Toujours pour notre entité, attaquons-nous à la création du formulaire.

Tout d'abord dans le contrôleur, créez cette méthode qui gèrera le formulaire en lui-même :

private $leFormulaire = null;
    
    private function createFormulaire() {
        
        $laCategorie = new Eric\MultimediaBundle\Entity\Categorie();
        
        $this->leFormulaire = $this->createFormBuilder($laCategorie)
            ->add('nom', 'text', array('max_length' => 50, 'label' =>'Nom de la catégorie : ')) // la seconde partie de add() (le tableau en paramètre) est optionnelle
            ->add('url', 'text', array('max_length' => 50, 'label' =>'SLUG de l'URL : '))
            ->add('enregistrer', 'submit')
            ->getForm();
        
    }

Ici, nous créons une entité (vide) de Catégorie, et l'envoyons au constructeur de formulaire pour qu'il fasse le mapping quand c'est nécessaire (plus tard). On donne le nom du champ (qui correspond à l'attribut de l'entité), on peut spécifier un label personnalisé, et mettre déjà quelques blocages comme la taille limite du champ à saisir. Cela sera ainsi pris en charge par le navigateur (si il gère le HTML 5).

Là aussi la doc vous sera très utile. Par exemple, si vous souhaitez utiliser une liste à partir d'entités, voici le code (pour l'entité Categorie) :

->add('categorie', 'entity', array('class' => 'EricMultimediaBundleEntityCategorie', 'property' => 'nom', 'required'  => false, 'label' =>'Catégorie : '))

A présent créons la vue qui affichera ce formulaire. Je vous laisse faire, mais en twig, voici ce que vous devez mettre, au minimum :

{{ form(formulaireAjout) }}

Et maintenant une vue pour afficher l'éventuelle liste d'erreurs lors de la saisie du formulaire :

<ul>
        {% for error in errors %}
            <li>{{ error[0] }}</li>
            {% endfor %}
    </ul>

5- Affichage du formulaire

Attaquons la méthode qui s'occupera de lancer l'affichage du formulaire. Rien de complexe, elle retourne juste la vue mentionnée ci-desus :

//formulaire d'ajout d'une catégorie
    public function formAjoutCategAction() {
        
        $this->createFormulaire(); //On crée le formulaire
        
        return $this->render('VideoBundle:Gestion:formcateg.html.twig', array('formulaireAjout' => $this->leFormulaire->createView())); //On envoie à la vue... une vue générée par le constructeur de formulaire.
    }

6- Traitement du formulaire

Une fois que votre formulaire été rempli, un traitement doit être réalisé pour le valider et éventuellement enregister notre nouvelle catégorie dans la base de données.

Au préalable, créez une nouvelle méthode privée (je vous recommande plutôt de créer un service, car au moins il sera réutilisable plus tard partout dans votre application) pour valider le formulaire :

private function getListeErreurs(SymfonyComponentFormForm $form) {
        $errors = array();
        foreach ($form->getErrors() as $key => $error) {
            $template = $error->getMessageTemplate();
            $parameters = $error->getMessageParameters();
            foreach ($parameters as $var => $value) {
                $template = str_replace($var, $value, $template);
            }
            $errors[$key] = $template;
        }
        if ($form->count()) {
            foreach ($form as $child) {
                if (!$child->isValid()) {
                    $errors[$child->getName()] = $this->getListeErreurs($child);
                }
            }
        }
        return $errors;
    }

Le but de cette méthode, est de retourner une éventuelle liste d'erreurs pour le formulaire passé en paramètre.

Maintenant, traitons ce fameux formulaire :

D'abord, en haut de votre classe, mettez ceci :

use SymfonyComponentHttpFoundationRequest;

Et maintenant, notre méthode chargée de gérer le traitement :

//traitement formulaire ajout catégorie
    public function traitementAjoutCategAction(Request $request) { //Ne pas oublier le paramètre
        
        $this->createFormulaire();
        $this->leFormulaire->handleRequest($request); //Ces deux lignes permettent de recréer le formulaire et de le valider à partir de la requête
        
        if ($this->leFormulaire->isValid()) {
            //Traitement à faire si le formulaire est valide
            $LaNouvelleCategorie = $this->leFormulaire->getData(); //Récuperer la catégorie automatiquement à partir des données saisies
            
            $LaNouvelleCategorie->setUrl(strtolower($LaNouvelleCategorie->getUrl())); //Pour notre exemple, mettre l'url saisie en minuscule
            
            $em = $this->getDoctrine()->getManager(); //Ces trois lignes servent à la persistance (sauvegarde de notre nouvelle catégorie)
            $em->persist($LaNouvelleCategorie);
            $em->flush();
            return $this->redirect($this->generateUrl('liste_categorie')); //Redirection à la page qui liste les catégories
            
        } else {  
            //Traitement à faire si le formulaire est invalide
            $errors = $this->getListeErreurs($this->leFormulaire); //Récuperer la liste des erreurs
     
            return $this->render('VideoBundle:Gestion:validation_form_categ.html.twig', array('errors' => $errors)); //Retourner la vue avec la liste des erreurs
        }
        
    }

Voilà, à présent, vous savez l'essentiel!

Note : si vous souhaitez, pour une certaine raison, récuperer votre formulaire, sous forme de tableau, voici ce qu'il faut utiliser :

$leForm = $request->request->get($this->leFormulaire->getName()); //Retourne un tableau du formulaire de type $this->leFormulaire contenu dans la requête

ou encore :

$postData = $request->get('form')

 

7- Et l'édition ?

Voici, sans explications (pas vraiment nécessaires...) la partie pour l'édition. Au préalable, vous devez ajouter ceci :

Dans votre entité, un setter pour l'id :

  public function setId($id) {
        $this->id = $id;
    }

Dans votre fichier route, une seule route qui accepte des requêtes de type POST et GET :

gestion_form_editer_categorie:
    path:     /gestion/editercateg/
    defaults: { _controller: VideoBundle:Gestion:formEditerCateg }
    requirements:
        _method: GET|POST

A présent, la partie du contrôleur :

/** SECTION Edition catégorie */
    
    private function verifExistanceCategorie($id) {
        $em = $this->getDoctrine()->getManager();
        $laCateg = $em->getRepository("VideoBundle:Categorie")->find($id);
        
        if($laCateg) {
            return $laCateg;
        }else {
            return false;
        }
    }
    
    private function createFormulaireEditionCategorie($laCategorie="") {
        
        if($laCategorie == "") {
            $laCategorie = new EricMultimediaBundleEntityCategorie();
        }
        
        $this->leFormulaire = $this->createFormBuilder($laCategorie)
            ->add('id', 'hidden')
            ->add('nom', 'text', array('max_length' => 50, 'label' =>'Nom de la catégorie : '))
            ->add('Mettre à jour', 'submit')
            ->getForm();
        
    }
    
    public function formEditerCategAction(Request $request) {
        
        if ($_SERVER['REQUEST_METHOD'] == 'GET') { //Cas de l'affichage du formulaire
            if (isset($_GET['id'])) {
                if (filter_var($_GET['id'], FILTER_VALIDATE_INT)) {
                    //Allons chercher la catégorie
                    $laCateg = $this->verifExistanceCategorie($_GET['id']);
                    if (!$laCateg) {
                        throw $this->createNotFoundException("La catégorie n'existe pas!");
                    }
                    //Créons le formulaire
                    $this->createFormulaireEditionCategorie($laCateg);
                    return $this->render('VideoBundle:Gestion:formcateg.html.twig', array('formulaire' => $this->leFormulaire->createView(), 'titre' => 'Editer'));
                } else {
                    throw $this->createNotFoundException("Identifiant au mauvais format!");
                }
            } else {
                throw $this->createNotFoundException("Identifiant manquant!");
            }
        } else { //Cas du traitement du formulaire
            
            $postData = $request->get('form');
            $id = $postData['id'];
            $ancienneVersion = $this->verifExistanceCategorie($id);
            if (!$ancienneVersion) {
                throw $this->createNotFoundException("La catégorie n'existe pas!");
            }
            $this->createFormulaireEditionCategorie();
            $this->leFormulaire->submit($request);
            
            if ($this->leFormulaire->isValid()) {
                //Traitement à faire si le formulaire est valide
                $ancienneVersion->setNom($postData['nom']);
                $em = $this->getDoctrine()->getManager();
                $em->persist($ancienneVersion);
                $em->flush();
                return $this->redirect($this->generateUrl('liste_categorie'));
                
            } else {
                //Traitement à faire si le formulaire est invalide
                
                $validateur = $this->get('validateur_formulaire');
                $errors = $validateur->getListeErreurs($this->leFormulaire);
                return $this->render('VideoBundle:Gestion:validation_form_categ.html.twig', array('errors' => $errors));
            }
        }
    }

 

8- Bonus

Si vous ne souhaitez par faire tout cela pour la gestion d'une entité, sachez que la doc de Symfony vous explique comment créer un contrôleur comprenant toutes les méthodes CRUD pour une entité, avec la console.


Cet article vous a plu? Découvrez d'autres articles


Tweet
comments powered by Disqus