Créer une page de contact avec symfony 2

Posté par Lecesne Yannick dans Symfony

Symfony

Créer une page de contact avec symfony 2

Dans cette deuxième partie je vais vous montrer comment mettre en place un formulaire de contact basique très rapidement avec Symfony.

Avant de commencer

Cette deuxième partie est la suite de ce Tuto. Vous pouvez récupérer le code source en faisant :

// on récupère le projet sur github
git clone https://github.com/vikinglab/tuto-blog.git

// on installe les vendors du projet
php composer.phar install

Ou bien vous pouvez récupérer ce que je vais vous montrer dans cette partie et l'adapter à votre projet.

Créer la page contact

Nous allons créer la page qui va acceuillir notre formulaire de contact.
Je vais utiliser la méthode 1 que nous avons vue dans la partie 1 du tuto. Pour cela, je vais créer une méthode contactAction dans mon DefaultController.php.

<?php

namespace T\MainBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('TMainBundle:Default:index.html.twig', array());
}


public function tarifAction()
{
return $this->render('TMainBundle:Default:tarif.html.twig', array());
}


public function contactAction()
{
return $this->render('TMainBundle:Contact:contact.html.twig', array());
}

}

Maintenant je vais créer mon routing qui appellera ma méthode contactAction.
Dans le fichier de routing de votre bundle src/T/MainBundle/Resources/config/routing.yml, ajoutez :

t_main_homepage:
path: /
defaults: { _controller: TMainBundle:Default:index }

t_main_tarif:
path: /tarif
defaults: { _controller: TMainBundle:Default:tarif }

t_main_contact:
path: /contact
defaults: { _controller: TMainBundle:Default:contact }

Il ne nous reste plus qu'à créer le template.
Pour le différencier de mes 2 pages statiques ( accueil et tarif ), je vais le mettre dans un dossier à part src/T/MainBundle/Resources/view/Contact/contact.html.twig 
Voici son contenu :

{% extends 'TMainBundle::layout.html.twig' %}

{% block title %}
{#je recupere le contenu du block parent#}
{{ parent() }} - Contact
{% endblock %}

{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="jumbotron">
<h1>Page contact</h1>
</div>
</div>
</div>
{% endblock %}

 Voilà donc pour cette nouvelle page, les choses sérieuses peuvent commencer !

Notre formulaire de contact avec Symfony 2

Nous allons maintenant créer notre formulaire de contact, en utilisant un form type que nous allons insérer dans le repertoire suivant src/T/MainBundle/Form/Type/ContactType.php

<?php

namespace T\MainBundle\Form\Type;

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

class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', 'email')
->add('nom')
->add('prenom')
->add('sujet')
->add('contenu', 'textarea')
;
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
// ici nous indiquons la class Contact que le form doit utiliser
'data_class' => 'T\MainBundle\Form\Data\Contact',
)
);
}

public function getName()
{
return 'contact';
}
}

Les formulaires de Symfony fonctionnent avec des objets qui sont généralement nos entités doctrine, entités qui sont enregistrés en base de données.
Vous pouvez aussi créer des formulaire avec le form builder directement depuis votre controller, sans entitée, mais dans ce cas vos formulaires auront qu'une simple validation HTML5.
Dans le cas d'un formulaire de contact nous n'avons pas besoin de sauvegarder les emails envoyés par nos internautes mais nous devons tout de même fournir un objet à notre formulaire pour une validation compléte :)
Nous allons donc créer une entité tous ce qu'il y a de plus basique dans le repertoire suivant src/T/MainBundle/Form/Data/Contact.php

<?php
namespace T\MainBundle\Form\Data;

use Symfony\Component\Validator\Constraints as Assert;

class Contact
{
/**
* @Assert\NotBlank()
* @Assert\Email(
* message = "'{{ value }}' n'est pas un email valide.",
* checkMX = true
* )
*/
protected $email;

/**
* @Assert\NotBlank()
*/
protected $nom;

/**
* @Assert\NotBlank()
*/
protected $prenom;

/**
* @Assert\NotBlank()
*/
protected $sujet;

/**
* @Assert\NotBlank()
*/
protected $contenu;

public function getEmail(){
return $this->email;
}

public function setEmail($email){
$this->email = $email;
}

public function getNom(){
return $this->nom;
}

public function setNom($nom){
$this->nom = $nom;
}

public function getPrenom(){
return $this->prenom;
}

public function setPrenom($prenom){
$this->prenom = $prenom;
}

public function getSujet(){
return $this->sujet;
}

public function setSujet($sujet){
$this->sujet = $sujet;
}

public function getContenu(){
return $this->contenu;
}

public function setContenu($contenu){
$this->contenu = $contenu;
}

}

Vous noterez la présence d'assert qui ce sont les validateurs de Symfony. Pour les validateurs (et contrairement au routing) j'aime utiliser le format Annotation qui permet d'avoir en un coup d'oeil la règle de validation. Encore une fois c'est à vous de choisir :) 
Vous pouvez trouver sur le site de Symfony un exemple d'utilisation avec les différents formats pour chaque validateurs.


Ci-dessous un exemple avec le validateur notBlank qui vous l'aurez compris, va valider à la soumission de notre formulaire si le champ n'est pas vide.

Exemple des différents format

Dans cette partie nous avons mis en place notre Form Type et notre Class Contact. Je vous entend déjà dire
"c'est bien beau mais à quoi ça me sert tout ça ?"
Et bien maintenant nous n'avons plus qu'à le renvoyer dans notre vue et faire l'envoi de mail :)

On envoie les mails tous aux abris !

Dans cette partie je vais vous montrer comment envoyer un mail avec SwiftMailer qui est implémenté "par defaut" dans Symfony avec le bundle SwiftmailerBundle.
Si vous voulez jetter un coup d'œil à la doc de Symfony sur l'envoi de mail c'est par ici.
Avant toutes choses nous allons afficher notre formulaire fraichement créé. Direction notre DefaultController.

Nous allons renvoyer notre formulaire dans la vue contact.html.twig

<?php

namespace T\MainBundle\Controller;

use T\MainBundle\Form\Type\ContactType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('TMainBundle:Default:index.html.twig', array());
}


public function tarifAction()
{
return $this->render('TMainBundle:Default:tarif.html.twig', array());
}


public function contactAction(Request $r)
{

// on créer le formulaire à partir de notre formType
$formContact = $this->createForm(new ContactType());


return $this->render('TMainBundle:Contact:contact.html.twig', array(
// on renvoi dans la vue "la vue" du formulaire
'formContact' => $formContact->createView()
));
}

}

Ici je renvoie la vue du formulaire dans la variable formContact, au sein de mon template contact.html.twig
Dans notre vue Twig nous allons maintenant afficher ce formulaire.

Version courte pour les Dev fainéants :O

{% extends 'TMainBundle::layout.html.twig' %}

{% block title %}
{#je recupere le contenu du block parent#}
{{ parent() }} - Contact
{% endblock %}

{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="jumbotron">
<h1>Page contact</h1>

<form action="{{ path('t_main_contact') }}" method="post">
{{ form_widget(formContact) }}
<button class="btn btn-success btn-big">Envoyer</button>
</form>
</div>
</div>
</div>
{% endblock %}

Maintenant la version un peu plus détaillée pour nos amis intégrateurs.

{% extends 'TMainBundle::layout.html.twig' %}

{% block title %}
{#je recupere le contenu du block parent#}
{{ parent() }} - Contact
{% endblock %}

{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="jumbotron">
<h1>Page contact</h1>

<form action="{{ path('t_main_contact') }}" method="post">
{{ form_label(formContact.email) }}
{{ form_widget(formContact.email) }}
{{ form_errors(formContact.email) }}
<br/>

{{ form_label(formContact.nom) }}
{{ form_widget(formContact.nom) }}
{{ form_errors(formContact.nom) }}
<br/>

{{ form_label(formContact.prenom) }}
{{ form_widget(formContact.prenom) }}
{{ form_errors(formContact.prenom) }}
<br/>

{{ form_label(formContact.sujet) }}
{{ form_widget(formContact.sujet) }}
{{ form_errors(formContact.sujet) }}
<br/>

{{ form_label(formContact.contenu) }}
{{ form_widget(formContact.contenu) }}
{{ form_errors(formContact.contenu) }}
<br/>

{#Ce dernier form widget vas nous permettre d'ajouter des que nous aurions pu oublier et SURTOUT le form token sans ça votre formulaire ne sera jamais valide !!#}
{{ form_widget(formContact) }}
{{ form_errors(formContact) }}
<button class="btn btn-success btn-big">Envoyer</button>
</form>
</div>
</div>
</div>
{% endblock %}

Voilà avec cette dernière version il est plus facile pour les intégrateurs de styliser ce formulaire.

À ce stade, voici ce que vous devriez avoir :

Vous remarquerez que je me suis amusé à styliser un peu, vraiment un tout petit peu ... donc n'hésitez pas a récupérer le projet sur le repository pour avoir la même version

Il ne nous reste plus qu'à envoyer l'email après soumission de notre formulaire de contact.

<?php

namespace T\MainBundle\Controller;

use T\MainBundle\Form\Data\Contact;
use T\MainBundle\Form\Type\ContactType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
public function indexAction()
{
return $this->render('TMainBundle:Default:index.html.twig', array());
}


public function tarifAction()
{
return $this->render('TMainBundle:Default:tarif.html.twig', array());
}


public function contactAction(Request $r)
{

// on créer le formulaire à partir de notre formType
$formContact = $this->createForm(new ContactType());

// on vérifi si c'est une méthode post
if($r->isMethod('post')){
// on récupére les informations du formulaire soumis et on set les différents champs de notre formulaile avec, donc la class Contact.
$formContact->handleRequest($r);

//on vérifi si le formulaire est valide, souvenez vous des différents validateurs que nous avons mis en place sur notre entitée contact
if($formContact->isValid()){

// Ici on récupére la class Contact qui a été préalablement Set avec les champs du formulaire
$contact = $formContact->getData();

$message = \Swift_Message::newInstance()
->setSubject($contact->getSujet())
->setFrom($contact->getEmail())
// notre adresse mail
->setTo('contact@viking-lab.fr')
->setContentType('text/html')
//ici nous allons utiliser un template pour pouvoir styliser notre mail si nous le souhaitons
->setBody(
$this->renderView('TMainBundle:Contact:email.html.twig', array(
'contact' => $contact
)
)
)
;

// nous appelons le service swiftmailer et on envoi :)
$this->get('mailer')->send($message);

// on retourne une message flash pour l'utilisateur pour le prévenir que son mail est bien parti
$this->get('session')->getFlashBag()->add('success', 'Merci pour votre email !');
}else{
//si le formulaire n'est pas valide en plus des erreurs du form
$this->get('session')->getFlashBag()->add('danger', 'Désolé un problème est survenu.');

}
}

return $this->render('TMainBundle:Contact:contact.html.twig', array(
// on renvoi dans la vue "la vue" du formulaire
'formContact' => $formContact->createView()
));
}

}

Nous allons créer notre contenu de mail (src/T/MainBundle/Resources/Contact/email.html.twig):

<p>Bonjour,</p>

<p>Voici un message de la part de {{ contact.nom }} {{ contact.prenom }} : </p>
<p>{{ contact.contenu }}</p>

Et voilà nous pouvons envoyer des emails :) 
Pour tester tout ça je vous propose d'installer MailCatcher qui est un server SMTP fonctionnant en local et qui va intercepter les emails envoyés. Exemple d'email reçu via mon application : 

Exemple mail catcher

Pour le configurer avec Symfony vous aller avoir besoin d'ajouter "le port d'envoi" dans votre parameters.yml et votre config.yml. Mais pas d'inquiétude je l'ai déjà fais :)

Cette deuxième partie touche à sa fin, j'espère que ça vous sera utile. La troisième et dernière partie s'intéressera à la création de pages "dynamiques" et d'un petit back office simple, pour nous permettre de voir comment fonctionne Doctrine.