Protection automatique contre les attaques CSRF et XSS
Validation automatique des données côté serveur
Un formulaire utilisable dans plusieurs contextes
Modifications centralisées et faciles
<?php
// src/Entity/Article.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank(message: 'Le titre est obligatoire')]
#[Assert\Length(
min: 5,
max: 255,
minMessage: 'Le titre doit contenir au moins {{ limit }} caractères'
)]
private ?string $title = null;
#[ORM\Column(type: 'text')]
#[Assert\NotBlank(message: 'Le contenu est obligatoire')]
private ?string $content = null;
// Getters et setters...
}
#[Assert\NotBlank] : champ obligatoire#[Assert\Length] : contraintes de longueur#[Assert\Email] : validation email
<?php
// src/Form/ArticleType.php
namespace App\Form;
use App\Entity\Article;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title', TextType::class, [
'label' => 'Titre de l\'article',
'attr' => [
'placeholder' => 'Saisissez le titre',
'class' => 'form-control'
]
])
->add('content', TextareaType::class, [
'label' => 'Contenu',
'attr' => [
'rows' => 10,
'class' => 'form-control'
]
])
->add('submit', SubmitType::class, [
'label' => 'Publier',
'attr' => ['class' => 'btn btn-primary']
]);
}
}
// Texte simple TextType::class // Zone de texte TextareaType::class // Email avec validation EmailType::class // Mot de passe PasswordType::class // Nombre NumberType::class // Date DateType::class
// Liste déroulante ChoiceType::class // Relation avec entité EntityType::class // Upload de fichier FileType::class // Case à cocher CheckboxType::class // Boutons radio RadioType::class // Collection dynamique CollectionType::class
->add('category', ChoiceType::class, [
'choices' => [
'Technologie' => 'tech',
'Sport' => 'sport',
'Culture' => 'culture'
],
'placeholder' => 'Choisir une catégorie',
'required' => false
])
<?php
// src/Controller/ArticleController.php
namespace App\Controller;
use App\Entity\Article;
use App\Form\ArticleType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ArticleController extends AbstractController
{
#[Route('/article/new', name: 'article_new')]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
// 1. Créer une nouvelle instance
$article = new Article();
// 2. Créer le formulaire
$form = $this->createForm(ArticleType::class, $article);
// 3. Traiter la requête
$form->handleRequest($request);
// 4. Vérifier si valide
if ($form->isSubmitted() && $form->isValid()) {
$article->setCreatedAt(new \DateTime());
// 5. Sauvegarder
$entityManager->persist($article);
$entityManager->flush();
$this->addFlash('success', 'Article créé !');
return $this->redirectToRoute('article_show', ['id' => $article->getId()]);
}
// 6. Afficher le formulaire
return $this->render('article/new.html.twig', [
'form' => $form->createView(),
]);
}
}
persist() car l'objet existe déjà en base !
{# templates/article/new.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}Nouvel Article{% endblock %}
{% block body %}
<div class="container mt-5">
<h1>Créer un nouvel article</h1>
{# Messages flash #}
{% for type, messages in app.flashes %}
{% for message in messages %}
<div class="alert alert-{{ type == 'success' ? 'success' : 'danger' }}">
{{ message }}
</div>
{% endfor %}
{% endfor %}
{# Formulaire #}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
</div>
{% endblock %}
form_start(form) : balise d'ouvertureform_widget(form) : affichage automatiqueform_end(form) : balise de fermeture
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{{ form_start(form) }}
<div class="mb-3">
{{ form_label(form.title) }}
{{ form_widget(form.title) }}
{{ form_errors(form.title) }}
</div>
<div class="mb-3">
{{ form_label(form.content) }}
{{ form_widget(form.content) }}
{{ form_errors(form.content) }}
</div>
{{ form_widget(form.submit) }}
{{ form_end(form) }}
// ❌ Problème
private string $title;
// ✅ Solution : Rendre nullable
private ?string $title = null;
// ✅ Ou rendre facultatif
->add('title', TextType::class, [
'required' => false,
])
form_start() et form_end()
// ❌ Problème
->add('category', EntityType::class, [
'class' => Category::class,
])
// ✅ Solution
->add('category', EntityType::class, [
'class' => Category::class,
'choice_label' => 'name',
])
// src/Entity/Contact.php (pas de mapping ORM)
class Contact
{
#[Assert\NotBlank(message: 'Le nom est requis')]
#[Assert\Length(min: 2, max: 50)]
private ?string $name = null;
#[Assert\NotBlank(message: 'L\'email est requis')]
#[Assert\Email(message: 'L\'email n\'est pas valide')]
private ?string $email = null;
#[Assert\NotBlank(message: 'Le message est requis')]
#[Assert\Length(min: 10)]
private ?string $message = null;
// Getters et setters...
}
// src/Form/ContactType.php
class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['label' => 'Votre nom'])
->add('email', EmailType::class, ['label' => 'Votre email'])
->add('message', TextareaType::class, ['label' => 'Message'])
->add('submit', SubmitType::class, ['label' => 'Envoyer']);
}
}
Toujours valider côté serveur, utiliser les groupes de validation
Optimiser les requêtes EntityType avec query_builder
Utiliser les attributs HTML5, placeholders utiles
Sous-formulaires pour entités complexes
# Générer un formulaire php bin/console make:form # Créer une entité php bin/console make:entity # Générer un CRUD complet php bin/console make:crud