studoo-app/symfony-tp5-formulaires
Applicaiton permettant d'utliser les formulaires et leurs validations
Symfony TP 5 : Formulaires et Validation
🎯 Contexte professionnel
Vous êtes développeur web dans une startup EdTech nommée SkillBoost Academy. L'entreprise souhaite créer une plateforme innovante de formation en ligne permettant aux formateurs de proposer leurs cours et aux apprenants de s'inscrire facilement.
Le système doit gérer :
- Formations avec leurs détails, programmes et tarifs
- Utilisateurs (formateurs et apprenants)
- Inscriptions des apprenants aux formations
- Catégories de formation pour faciliter la recherche
Votre mission : développer les interfaces de saisie avec formulaires et validation en utilisant le système de formulaires Symfony avec des contraintes de validation personnalisées pour garantir la qualité des données éducatives.
📋 Objectifs pédagogiques
Compétences techniques visées :
- Maîtriser le système de formulaires Symfony (FormBuilder, FormType)
- Implémenter la validation avancée avec contraintes personnalisées (relativement simple, juste pour démontrer leur existence et utilisation)
Compétences transversales :
- Concevoir des formulaires intuitifs adaptés au contexte éducatif
- Structurer le code selon les bonnes pratiques Symfony
- Garantir la qualité et la cohérence des données métier
🛠️ Consignes détaillées
🚀 Phase 1 : Modélisation et Entités de Base (60 minutes)
Étape 1.1 : Préparation du projet
Créez un nouveau projet Symfony et installez les dépendances :
symfony new symfony-tp5-formulaires --webappConfiguration de la base de données dans .env :
DATABASE_URL="mysql://root:@127.0.0.1:3306/skillboost_academy?serverVersion=8.0"
Étape 1.2 : Création des entités principales
Créez les quatre entités de base avec la commande make:entity.
Entité Categorie :
symfony console make:entity CategorieAjoutez les propriétés suivantes :
nom: string, 100 caractèresslug: string, 100 caractères (version URL-friendly du nom)description: text, nullablecouleur: string, 7 caractères (format hex #RRGGBB)icone: string, 50 caractères
Entité User :
symfony console make:entity UserAjoutez les propriétés suivantes :
email: string, 180 caractères, uniqueroles: json (stocke un tableau de rôles)password: string, 255 caractèresprenom: string, 100 caractèresnom: string, 100 caractèrestelephone: string, 20 caractères, nullabledateNaissance: date, nullableentreprise: string, 255 caractères, nullableposte: string, 100 caractères, nullablebio: text, nullablephotoProfil: string, 255 caractères, nullabledateCreation: datetimeestActif: boolean
Entité Formation :
symfony console make:entity FormationAjoutez les propriétés suivantes :
titre: string, 250 caractèresslug: string, 250 caractèresdescription: textprogramme: textobjectifs: text, nullableprerequis: text, nullableduree: integer (durée en heures)niveau: string, 50 caractères (débutant, intermédiaire, avancé)prix: decimal (10,2)capaciteMax: integermodalite: string, 50 caractères (présentiel, distanciel, hybride)dateDebut: datedateFin: dateestPublie: booleandateCreation: datetime
Entité Inscription :
symfony console make:entity InscriptionAjoutez les propriétés suivantes :
dateInscription: datetimestatut: string, 50 caractères (en_attente, confirmee, annulee, terminee)modePaiement: string, 50 caractères, nullable (carte, virement, cheque)commentaire: text, nullable (besoins spécifiques de l'apprenant)noteSatisfaction: integer, nullable (de 1 à 5)commentaireSatisfaction: text, nullable
Étape 1.3 : Ajout des relations et migration de la structure de base de données
Modifiez l'entité Formation pour ajouter les relations :
symfony console make:entity FormationAjoutez les relations bidirectionnelles suivantes :
categorie: relation ManyToOne vers Categorieformateur: relation ManyToOne vers User (le formateur)
Modifiez l'entité Inscription pour ajouter les relations :
symfony console make:entity InscriptionAjoutez les relations bidirectionnelles suivantes :
formation: relation ManyToOne vers Formationapprenant: relation ManyToOne vers User (l'apprenant)
Générez la migration et mettez à jour la base de données :
symfony console make:migration
symfony console doctrine:migrations:migrateÉtape 1.4 : Fixtures de données
Créez des fixtures pour peupler votre base avec des données de test :
composer require --dev orm-fixtures fakerphp/fakerCréez au minimum :
- 5 catégories de formation (Développement web, Data Science, Design, Marketing, Gestion de projet)
- 10 utilisateurs (5 formateurs et 5 apprenants)
- 15 formations variées avec différentes modalités et niveaux
- 25 inscriptions avec différents statuts
Étape 1.5 : Contrôleur CategorieController avec CRUD complet
Créez le contrôleur pour gérer les catégories :
symfony console make:crud CategorieÉtape 1.6 : Mission autonome - Contrôleur UserController
Mission : Sur le modèle du CategorieController, créez un UserController complet avec :
- Route
/user- Index listant tous les utilisateurs - Route
/user/create- Création automatique d'un utilisateur avec Faker - Route
/user/{id}- Affichage des détails d'un utilisateur - Route
/user/{id}/update- Mise à jour aléatoire (email, téléphone, bio) - Route
/user/{id}/delete- Suppression d'un utilisateur
📝 Phase 2 : Formulaires de Base (90 minutes)
Étape 2.1 : Mission autonome - Formulaire User
Mission : Sur le modèle du formulaire Categorie, créez un formulaire complet pour les utilisateurs
Créez les éléments suivants :
- FormType :
UserTypeavec tous les champs (email, prenom, nom, telephone, dateNaissance, entreprise, poste, bio) - Contrôleur : Modifiez
UserControllerpour ajouter les actionsnew,edit,deleteutilisant le formulaire - Templates : Créez
user/new.html.twig,user/edit.html.twiget mettez à jouruser/index.html.twigetuser/show.html.twig
Points d'attention :
- Pour les champs : utilisez
EmailType,TextType,TelType,DateType,TextareaType - N'incluez pas le champ
passworddans ce formulaire (on le traitera séparément avec un formulaire d'inscription) - N'incluez pas les champs
roles,photoProfil,dateCreation,estActif(gérés automatiquement) - Ajoutez des labels et placeholders appropriés
✅ Phase 3 : Validation des Données (75 minutes)
Étape 3.1 : Contraintes de validation sur Categorie
Mission : Ajoutez des contraintes de validation dans l'entité Categorie
Modifiez src/Entity/Categorie.php en ajoutant les imports et contraintes :
nom: NotBlank, Length (min: 3, max: 100)slug: NotBlank, Regex (/^[a-z0-9]+(?:-[a-z0-9]+)*$/), UniqueEntitydescription: Length (max: 1000) - optionnelcouleur: NotBlank, Regex (/^#[0-9A-Fa-f]{6}$/)icone: NotBlank, Regex (/^fa-[\w-]+$/)
Testez votre formulaire en essayant de soumettre des données invalides :
- Slug avec majuscules ou espaces
- Couleur mal formatée
- Nom trop court
- Icône invalide
Étape 3.2 : Contraintes de validation sur User
Mission : Ajoutez les contraintes de validation sur l'entité User
email: NotBlank, Email, UniqueEntityprenom: NotBlank, Length (min: 2, max: 100)nom: NotBlank, Length (min: 2, max: 100)telephone: Regex (/^[0-9\-\+\s\(\)\.]+$/) - optionneldateNaissance: LessThan ('today'), GreaterThan ('-120 years') - optionnelentreprise: Length (max: 255) - optionnelposte: Length (max: 100) - optionnelbio: Length (max: 1000) - optionnel
Étape 3.3 : Contrainte personnalisée - Validation de dates de formation
Mission : Créez une contrainte personnalisée pour valider les dates de formation
Créez le fichier src/Validator/FormationDatesValides.php (l'attribut de contrainte) :
<?php
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
#[\Attribute]
class FormationDatesValides extends Constraint
{
public string $messageFinAvantDebut = 'La date de fin doit être postérieure à la date de début';
public string $messageDebutDansLePasse = 'La date de début ne peut pas être dans le passé';
public string $messageDureeInconsistante = 'La durée de la formation semble incohérente avec les dates fournies';
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}Créez le fichier src/Validator/FormationDatesValidesValidator.php (le validateur) :
<?php
namespace App\Validator;
use App\Entity\Formation;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class FormationDatesValidesValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof FormationDatesValides) {
throw new UnexpectedTypeException($constraint, FormationDatesValides::class);
}
if (!$value instanceof Formation) {
return;
}
$dateDebut = $value->getDateDebut();
$dateFin = $value->getDateFin();
$duree = $value->getDuree();
if (!$dateDebut || !$dateFin) {
return; // Les champs NotBlank se chargeront de ces validations
}
// Vérifier que la date de fin est après la date de début
if ($dateFin <= $dateDebut) {
$this->context->buildViolation($constraint->messageFinAvantDebut)
->atPath('dateFin')
->addViolation();
}
// Vérifier que la date de début n'est pas dans le passé
$aujourdhui = new \DateTime('today');
if ($dateDebut < $aujourdhui) {
$this->context->buildViolation($constraint->messageDebutDansLePasse)
->atPath('dateDebut')
->addViolation();
}
// Vérifier la cohérence de la durée (avec une tolérance)
if ($duree) {
// Calcul du nombre de jours entre les dates
$interval = $dateDebut->diff($dateFin);
$joursFormation = $interval->days;
// On suppose une formation de 7h par jour en moyenne
$heuresEstimees = $joursFormation * 7;
// Tolérance de 50% (la durée réelle peut varier selon l'intensité)
$toleranceBasse = $heuresEstimees * 0.5;
$toleranceHaute = $heuresEstimees * 1.5;
if ($duree < $toleranceBasse || $duree > $toleranceHaute) {
$this->context->buildViolation($constraint->messageDureeInconsistante)
->atPath('duree')
->addViolation();
}
}
}
}Étape 3.4 : Application de la contrainte personnalisée
Mission : Ajoutez les contraintes sur l'entité Formation
titre: NotBlank, Length (min: 10, max: 250)slug: NotBlank, Regex (/^[a-z0-9]+(?:-[a-z0-9]+)*$/)description: NotBlank, Length (min: 50)programme: NotBlank, Length (min: 50)objectifs: (pas de contraintes) - optionnelprerequis: (pas de contraintes) - optionnelduree: NotBlank, Positive, Range (min: 1, max: 1000) - en heuresniveau: NotBlank, Choice (debutant, intermediaire, avance)prix: NotBlank, PositiveOrZero, Range (max: 10000)capaciteMax: NotBlank, Positive, Range (min: 1, max: 100)modalite: NotBlank, Choice (presentiel, distanciel, hybride)dateDebut: NotBlankdateFin: NotBlankcategorie: NotNull (relation)formateur: NotNull (relation)- Contrainte de classe : FormationDatesValides (personnalisée)
Étape 3.5 : Mission autonome - Formulaire Formation
Mission : Créez un formulaire complet pour créer et modifier une formation
Créez les éléments suivants :
- FormType :
FormationTypeavec tous les champs - Contrôleur :
FormationControlleravec actions CRUD complètes (index, new, show, edit, delete) - Templates : Tous les templates nécessaires
Points d'attention particuliers :
- Utilisez
EntityTypepour les relations (categorie, formateur) - Utilisez
DateTypepour les dates avec widget approprié - Utilisez
MoneyTypepour le prix - Utilisez
ChoiceTypepour niveau et modalite - Utilisez
IntegerTypepour duree et capaciteMax - Utilisez
CheckboxTypepour estPublie - Testez que la contrainte personnalisée
FormationDatesValidesfonctionne bien
Cas de test pour la contrainte personnalisée :
- Essayez de créer une formation avec dateFin avant dateDebut
- Essayez de créer une formation avec dateDebut dans le passé
- Essayez de créer une formation où la durée est incohérente avec les dates
🎨 Phase 4 : Amélioration de l'Affichage des Formulaires (45 minutes)
Étape 4.1 : Personnalisation de l'affichage des erreurs
Mission : Créez un template personnalisé pour l'affichage des erreurs
Créez le fichier templates/form/custom_errors.html.twig :
{% block form_errors %}
{% if errors|length > 0 %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong><i class="fas fa-exclamation-triangle"></i> Erreur(s) de validation :</strong>
<ul class="mb-0 mt-2">
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% endblock %}Pour utiliser ce template personnalisé, dans vos templates de formulaires :
{% form_theme form 'form/custom_errors.html.twig' %}📖 Ressources utiles
Documentation officielle
Commandes essentielles
# Création de FormType
symfony console make:form
# Création de contrainte de validation personnalisée
symfony console make:validator
# Création d'entité
symfony console make:entity
# Création de contrôleur
symfony console make:controller
# Migration
symfony console make:migration
symfony console doctrine:migrations:migrate
# Chargement des fixtures
symfony console doctrine:fixtures:loadTypes de champs fréquemment utilisés
TextType: Champ texte simpleEmailType: Champ email avec validation HTML5TelType: Champ téléphoneIntegerType: Nombre entierMoneyType: Montant monétaireDateType: Sélection de dateTextareaType: Zone de texte multi-lignesChoiceType: Liste déroulante ou boutons radioEntityType: Sélection d'une entité DoctrineCheckboxType: Case à cocherColorType: Sélecteur de couleur
Contraintes de validation courantes
@Assert\NotBlank: Champ non vide@Assert\Length: Longueur min/max@Assert\Email: Format email@Assert\Regex: Expression régulière@Assert\Range: Valeur dans un intervalle@Assert\Positive/@Assert\PositiveOrZero: Nombre positif@Assert\Choice: Valeur parmi une liste@Assert\LessThan/@Assert\GreaterThan: Comparaison@UniqueEntity: Unicité en base de données
