Tuto Forms & Doctrine - SimplonReunion/developpeur-web GitHub Wiki

Pour effectuer ce mini tuto vous devez avoir un projet Symfony 3 et un bundle perso. Pour ce cas nous avons créer un bundle qui s'appelle SimplonReunionPlaygroundBundle. Vous devez aussi avoir ajouté au fichier app/parameters.yml le bon identifiant et mot de passe pour vous connecter à votre base de données MySQL. N'oubliez pas de donner un nom à votre base, par défaut c'est symfony.

Objectif

Créer un formulaire pour enregistrer les données en base de données.

Création de l'entité

Avant de commencer à créer réellement le formulaire, nous allons créer l'entité Contact. Une entité est un simple objet PHP qui contient les données à enregistrer en base. Pour cela vous pouvez soit créer la classe à la main ou soit utiliser une commande mise à disposition par Symfony pour la créer pour vous.

Comme on est astucieux, on va faire cela. On ouvre la console et on va à la racine de notre répertoire Symfony :

bin/console doctrine:generate:entity  

Vous devez répondre aux différentes questions posées par la commande. Tout d'abord le nom de votre entité. On va l'appeler Contact. Ce nom doit être précédé par le nom du bundle. Comme ça on est sûr qu'on utilise bien la bonne classe dans le bon bundle :

The Entity shortcut name: SimplonReunionPlaygroundBundle:Contact

Ensuite elle nous demande où elle peut retrouver les informations qui vont faire correspondre les propriétés de l'entité Contact et les champs en base de données. Pour cela vous avez plusieurs choix. Nous prendrons le choix par défaut annotations (donc il suffit juste d'appuyer sur "entrée").

Determine the format to use for the mapping information.

Configuration format (yml, xml, php, or annotation) [annotation]: 

Elle nous demande maintenant de créer les propriétés de Contact. Il faudra rentrer un à un les différentes propriétés qui sont firstname, lastname, tel, email, subject, message. Ne le les faites pas toutes en même temps ! Une propriété par demande. Vous verrez que pour chaque propriété vous devrez choisir le type, la taille, si elle peut être null par défaut et si c'est elle est unique. À vous de choisir.

Pour choisir le type, vous n'avez pas à deviner. La commande vous affiche une liste des types disponibles.

Instead of starting with a blank entity, you can add some fields now.
Note that the primary key will be added automatically (named id).

Available types: array, simple_array, json_array, object, 
boolean, integer, smallint, bigint, string, text, datetime, datetimetz, 
date, time, decimal, float, binary, blob, guid.

New field name (press <return> to stop adding fields):

Une fois que vous avez rentré toutes les propriétés, lorsque la commande affiche de nouveau :

New field name (press <return> to stop adding fields):

Appuyez directement sur "entrée". La commande comprendra que vous ne voulez plus rajouter de propriétés et qu'elle peut créer l'entité.

À la fin, la commande vous affiche que tout s'est bien passé et qu'elle a tout créé dans le répertoire de Entity de votre bundle. Dans notre cas elle a créé le fichier SimplonReunion/PlaygroundBundle/Entity/Contact.php

En regardant ce fichier on voit :

//SimplonReunion/PlaygroundBundle/Entity/Contact.php

namespace SimplonReunion\PlaygroundBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Contact
 *
 * @ORM\Table(name="contact")
 * @ORM\Entity(repositoryClass="SimplonReunion\PlaygroundBundle\Repository\ContactRepository")
 */
class Contact
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="firstname", type="string", length=255)
     */
    private $firstname;

    /**
     * @var string
     *
     * @ORM\Column(name="lastname", type="string", length=255)
     */
    private $lastname;

    /**
     * @var string
     *
     * @ORM\Column(name="tel", type="string", length=255)
     */
    private $tel;

    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=255, unique=true)
     */
    private $email;

    /**
     * @var string
     *
     * @ORM\Column(name="subject", type="string", length=255)
     */
    private $subject;

    /**
     * @var string
     *
     * @ORM\Column(name="message", type="text")
     */
    private $message;


    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set firstname
     *
     * @param string $firstname
     *
     * @return Contact
     */
    public function setFirstname($firstname)
    {
        $this->firstname = $firstname;

        return $this;
    }

    /**
     * Get firstname
     *
     * @return string
     */
    public function getFirstname()
    {
        return $this->firstname;
    }

    /**
     * Set lastname
     *
     * @param string $lastname
     *
     * @return Contact
     */
    public function setLastname($lastname)
    {
        $this->lastname = $lastname;

        return $this;
    }

    /**
     * Get lastname
     *
     * @return string
     */
    public function getLastname()
    {
        return $this->lastname;
    }

    /**
     * Set tel
     *
     * @param string $tel
     *
     * @return Contact
     */
    public function setTel($tel)
    {
        $this->tel = $tel;

        return $this;
    }

    /**
     * Get tel
     *
     * @return string
     */
    public function getTel()
    {
        return $this->tel;
    }

    /**
     * Set email
     *
     * @param string $email
     *
     * @return Contact
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * Get email
     *
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set subject
     *
     * @param string $subject
     *
     * @return Contact
     */
    public function setSubject($subject)
    {
        $this->subject = $subject;

        return $this;
    }

    /**
     * Get subject
     *
     * @return string
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * Set message
     *
     * @param string $message
     *
     * @return Contact
     */
    public function setMessage($message)
    {
        $this->message = $message;

        return $this;
    }

    /**
     * Get message
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }
}

On remarque des choses importantes. Tout d'abord on import le Mapping de Doctrine qu'on renomme en ORM

use Doctrine\ORM\Mapping as ORM;

Et ensuite on voit plein de lignes en commentaires qui commencent par @ORM\.

Qu'est ce que c'est ?

En fait, ces commentaires vont permettre à Doctrine de créer la table et les champs, en base de données, correspondantes à l'entité.

À ce stade, la table n'est pas encore créée en base. On va le faire à l'aide d'une autre commande qui va lire les annotations et les traduire en MySQL afin de créer la table correspondante à l'entité !

bin/console doctrine:schema:update --force

Vous remarquerez le paramètre --force qui force la mise à jour de la base. Avant toute opération sur la base, vous avez la possibilité de voir les requêtes qui vont être effectuées en faisant :

bin/console doctrine:schema:update --dump

Une fois la commande effectuée avec le paramètre --force Vous pouvez le vérifier en regardant avec phpmyadmin.

Insérer en base

On a notre entité de créée ainsi que la table correspondante. Il est maintenant temps de voir si tout cela fonctionne et d'insérer un contact en base en passant par un controller.

Je vais le mettre dans le controller par défaut de mon bundle DefaultController :

namespace SimplonReunion\PlaygroundBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use SimplonReunion\PlaygroundBundle\Entity\Contact;
use SimplonReunion\PlaygroundBundle\Form\ContactType;

class DefaultController extends Controller
{
    /**
     * @Route("/insert",name="playground_insert")
     */
    public function insertAction(){
      //Créer l'entité à enregistrer en base de données
      $contact = new Contact();

      //Rentrer les informations dans l'entité
      $contact->setFirstname('Ernest');
      $contact->setLastname('Debogue');
      $contact->setTel('0262000000');
      $contact->setEmail('[email protected]');
      $contact->setSubject('Test');
      $contact->setMessage('Test message');

      //Récupérer le gestionnaire d'entité. C'est lui qui va s'occuper des interactions avec la base
      $em = $this->getDoctrine()->getManager();
      //On ajoute l'objet $contact avec les informations comme objet à sauvegarder.
      $em->persist($contact);
      //On lance l'action à faire sur la base, ici la sauvegarde
      $em->flush();

      //Retourne une vue basique. Juste pour afficher quelque chose. Si vous n'avez pas créé ce fichier, faites-le :)
      return $this->render('SimplonReunionPlaygroundBundle:Default:insert.html.twig');
    }
}

Comment ça marche tout ça :

  • On créé notre entité
  • On y insère des valeurs grâce aux méthodes prévues à cet effet,
  • On récupère le gestionnaire d'entité
  • On ajoute l'entité au gestionnaire pour la sauvegarde
  • Et on lance explicitement la sauvegarde en base

Pour faire fonctionner, il suffit d'aller à l'url /insert puis de regarder avec phpmyadmin si dans la base de données, la table contact possède des enregistrement. Ça doit être le cas.

C'est bien beau tout ça mais les informations sont rentrées en "dur", c'est écrit dans le code du controller. Il faudrait un moyen plus dynamique... D'où les formulaires.

Le formulaire

Symfony possède un système de formulaire qui, dans la majorité des cas, vous simplifie le boulot.

Il y a plusieurs méthodes pour travailler avec les formulaires et Symfony. Nous allons opter pour la méthode où on créé un type de formulaire dans un fichier à part. Cela permet de réutiliser un formulaire dans plusieurs actions.

Pour créer ce fichier vous avez aussi une ligne de commande ! Celle-ci prends en paramètre le nom de l'entité sur laquelle le formulaire devra se baser pour créer ses champs (input, select, etc.). Ce nom est composé du nom du bundle et du nom de l'entité :

bin/console doctrine:generate:form SimplonReunionPlaygroundBundle:Contact

Rappelez-vous, dans ce cas ci, le bundle s'appelle SimplonReunionPlaygroundBundle et l'entité Contact

Cette commande a créé un fichier qui se trouve à SimplonReunion/PlaygroundBundle/Form/ContactType.php. Comme toujours on est curieux et on regarde ce qu'il y a à l'intérieur de ce fichier :

//src/SimplonReunion/PlaygroundBundle/Form/ContactType.php
namespace SimplonReunion\PlaygroundBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ContactType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('firstname')
            ->add('lastname')
            ->add('tel')
            ->add('email')
            ->add('subject')
            ->add('message')
        ;
    }
    
    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'SimplonReunion\PlaygroundBundle\Entity\Contact'
        ));
    }
}

Ce qu'il faut remarquer ici c'est la fonction buildForm. C'est à partir de cette méthode, que le système de formulaire de Symfony se base pour afficher le formulaire dans la vue (avec Twig), récupére les informations postées et fait le lien avec l'entité. Si c'est pas clair ça le deviendra bientôt.

Insérer un contact en base par le formulaire

Le formulaire est presque près, il faut le créer explicitement. C'est le controller qui va s'occuper de ça. On modifie notre action insertAction :

namespace SimplonReunion\PlaygroundBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use SimplonReunion\PlaygroundBundle\Entity\Contact;
use SimplonReunion\PlaygroundBundle\Form\ContactType;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    /**
     * @Route("/insert",name="playground_insert")
     */
    public function insertAction(Request $request){
      //l'entité Contact
      $contact = new Contact();

      //créer le formulaire à partir ContactType et fait un lien avec l'entité Contact
      $contactForm = $this->createForm(ContactType::class,$contact);

      //Récupère les données soumises par le formulaire et l'insère dans l'entité Contact
      $contactForm->handleRequest($request);

      //Enregistrer le formulaire uniquement quand celui-ci a été soumis et qu'il est valide
      if ($contactForm->isSubmitted() && $contactForm->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($contact);
        $em->flush();
      }

      return $this->render('SimplonReunionPlaygroundBundle:Default:insert.html.twig', array(
        //transforme le formulaire pour être générable par Twig dans la vue
        'contactForm'=>$contactForm->createView()
      ));
    }
}

À présent on s'intéresse à la vue. En utilisant Twig nous allons afficher le formulaire. Pour ne pas partir de rien on va étendre la vue base.html.twig (elle se trouve dans app/Resources/views) fourni par Symfony.On place le code du formulaire dans le block body. Et enfin on génère les balises HTML du formulaire.

{% extends 'base.html.twig' %}

{% block body %}

{# Afficher les balises du formulaire #}
{{ form_start(contactForm) }}
{{ form_widget(contactForm) }}
<input type="submit" value="Insert">
{{ form_end(contactForm) }}

{% endblock %}

Voilà maintenant on a système qui permet de sauvegarder les informations d'un formulaire de contact.

Aller plus loin

Ce petit tuto n'est qu'une base et il ne faudra pas vous s'arrêter à là !

Sachez que vous pouvez choisir quel type de wigdet HTML utilisé et pour quel champ exactement en modifiant des éléments dans le buildForm de ContactType.

Mettre en place une validation sur les champs du formulaire.

Personnaliser l'affichage des champs du formulaire dans une vue et même inclure du bootstrap

⚠️ **GitHub.com Fallback** ⚠️