1 / 20

🚀Bienvenue dans la Formation Symfony !

👋 Salut les développeurs !

Aujourd'hui, on va apprendre à créer les fondations de votre projet Symfony : les entités !

🏗️ Une petite analogie pour commencer...

Imaginez que vous construisez une maison :

  • Les entités sont comme les plans de votre maison
  • La base de données est le terrain où vous construisez
  • Doctrine est l'entrepreneur qui construit selon vos plans

🎯Objectifs de la session

📚 Comprendre

Les entités et leur rôle dans Symfony

🛠️ Créer

Votre première entité User

🔗 Relier

Les entités entre elles

Théorie
Pratique
Exercices
Projet

🤔Pourquoi avons-nous besoin d'entités ?

Sans entités (l'ancienne méthode) 😰

PHP
// Création d'un utilisateur à l'ancienne
$sql = "INSERT INTO user (email, password, first_name) VALUES (?, ?, ?)";
$stmt = $connection->prepare($sql);
$stmt->execute([$email, $password, $firstName]);

// Récupération
$sql = "SELECT * FROM user WHERE id = ?";
// ... beaucoup de code répétitif ...

Avec les entités Symfony 🎉

PHP
// Création d'un utilisateur avec Symfony
$user = new User();
$user->setEmail('jean@example.com');
$user->setFirstName('Jean');

$entityManager->persist($user);
$entityManager->flush(); // C'est tout !

❌ Sans entités

  • Code SQL partout
  • Risques d'injection SQL
  • Difficile à maintenir
  • Pas de validation automatique

✅ Avec entités

  • Code orienté objet
  • Sécurisé par défaut
  • Facile à maintenir
  • Validation intégrée

📦Une entité, c'est quoi concrètement ?

🎯 Pensez à une fiche de contact

Une entité, c'est comme une fiche de contact dans votre téléphone :

  • Elle a des champs (nom, prénom, téléphone...)
  • Chaque contact est unique (a un identifiant)
  • Vous pouvez les organiser (groupes, favoris...)

📱 Dans votre téléphone

Contact "Jean Dupont"
📧 Email: jean@example.com
📱 Téléphone: 06 12 34 56 78
🎂 Date naissance: 01/01/1990
⭐ Favori: Oui

💻 En code Symfony

PHP
class User {
    private $email;
    private $phone;
    private $birthDate;
    private $isFavorite;
}
💡 Astuce : Une entité = Un concept de votre application (User, Article, Product, Order...)

🎮 Mini-exercice : Identifiez les entités

Dans un site e-commerce, quelles seraient les entités principales ?

Voir la réponse
  • User (utilisateur)
  • Product (produit)
  • Category (catégorie)
  • Order (commande)
  • Cart (panier)

👥Contexte : Votre Projet de Groupe

📋 Rappel du projet

Vous travaillez en équipe sur un projet Symfony. Aujourd'hui, nous allons poser les bases solides pour que tout le monde puisse travailler efficacement !

🎯Les entités essentielles de tout projet

👤 User

La base de tout !

  • Authentification
  • Profils
  • Permissions

📝 Contenu

Selon votre projet :

  • Articles (blog)
  • Produits (e-commerce)
  • Posts (réseau social)

🔗 Relations

Les liens entre entités :

  • Auteur → Article
  • User → Commande
  • Catégorie → Produit

⚠️ Erreur courante des débutants

Vouloir créer toutes les entités d'un coup ! On va commencer par User car c'est la pierre angulaire de tout projet.

User
Contenu principal
Relations
Features avancées

🛠️Créons notre première entité : User

1Ouvrir le terminal

Dans VSCode : Terminal → Nouveau terminal
Ou utilisez le raccourci : Ctrl + ` (Windows/Linux) ou Cmd + ` (Mac)

2Lancer la commande magique

php bin/console make:user

3Répondre aux questions

The name of the security user class (e.g. User) [User]:

→ Appuyez sur Entrée (garder User)


Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:

→ Appuyez sur Entrée (oui)


Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:

→ Appuyez sur Entrée (utiliser email)


Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server) (yes/no) [yes]:

→ Appuyez sur Entrée (oui)

✅ Bravo ! Symfony a créé :

  • src/Entity/User.php - Votre entité User
  • src/Repository/UserRepository.php - Pour les requêtes
  • Configuration de sécurité mise à jour

🔍Explorons le code généré

📂 Ouvrez src/Entity/User.php

PHP
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true)]
    private ?string $email = null;

    #[ORM\Column]
    private array $roles = [];

    #[ORM\Column]
    private ?string $password = null;
    
    // ... getters et setters
}

🎨 Décryptons ensemble :

  • #[ORM\Entity] = "Hey Doctrine, ceci est une entité !"
  • #[ORM\Id] = "C'est l'identifiant unique"
  • #[ORM\Column] = "Ça devient une colonne en base"
  • unique: true = "Pas de doublons autorisés"
💡 Le saviez-vous ? Les attributs PHP 8 (avec #[]) remplacent les annotations (@) des anciennes versions de Symfony.

✨Personnalisons notre entité User

➕Ajoutons des champs utiles

php bin/console make:entity User

Symfony détecte que User existe déjà et vous propose d'ajouter des champs !

Exemple d'ajout de champs :

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

→ Tapez: firstName


Field type (enter ? to see all types) [string]:

→ Appuyez sur Entrée


Field length [255]:

→ Appuyez sur Entrée


Can this field be null in the database (nullable) (yes/no) [no]:

→ Tapez: no

💪 Exercice : Ajoutez ces champs

  1. firstName (string, 255, not null)
  2. lastName (string, 255, not null)
  3. birthDate (date, nullable)
  4. isVerified (boolean, default false)

💡 Pour arrêter d'ajouter des champs, appuyez sur Entrée sans rien taper.

🗄️Créons la base de données

⚠️ Prérequis

Assurez-vous que votre serveur de base de données (MySQL, PostgreSQL...) est démarré !

1Créer la base si elle n'existe pas

php bin/console doctrine:database:create

2Créer une migration

php bin/console make:migration

🤔 Qu'est-ce qu'une migration ?

C'est un fichier qui contient les instructions SQL pour créer/modifier vos tables. C'est comme un "historique des modifications" de votre base de données.

3Exécuter la migration

php bin/console doctrine:migrations:migrate

🎉 Résultat

Votre table user est créée dans la base de données avec tous les champs !

💡 Astuce Git : N'oubliez pas de commiter vos migrations ! Elles font partie du code source.

🧪Testons notre entité User !

📝Créons un contrôleur de test

php bin/console make:controller TestController

Dans src/Controller/TestController.php :

PHP
#[Route('/test-user', name: 'app_test_user')]
public function testUser(EntityManagerInterface $entityManager): Response
{
    // Créer un nouvel utilisateur
    $user = new User();
    $user->setEmail('test@example.com');
    $user->setFirstName('Jean');
    $user->setLastName('Dupont');
    $user->setPassword('$2y$13$...'); // Password hashé
    $user->setRoles(['ROLE_USER']);
    
    // Sauvegarder en base
    $entityManager->persist($user);
    $entityManager->flush();
    
    return new Response('Utilisateur créé avec l\'ID : ' . $user->getId());
}

⚠️ Important : Ne jamais stocker les mots de passe en clair !

On verra dans la prochaine session comment hasher correctement les mots de passe.

✅ Testez dans votre navigateur

Allez sur : http://localhost:8000/test-user

Vous devriez voir : "Utilisateur créé avec l'ID : 1"

🔗Les relations entre entités

🌍 Dans la vraie vie

Les relations sont partout :

  • Un auteur écrit plusieurs livres
  • Un livre appartient à une catégorie
  • Plusieurs étudiants suivent plusieurs cours

📊Les 3 types de relations principales

One-To-Many

1 → N

1 User
↓
N Articles

Un utilisateur peut écrire plusieurs articles

One-To-One

1 → 1

1 User
↕
1 Profile

Un utilisateur a un seul profil

Many-To-Many

N → N

N Students
↕
N Courses

Plusieurs étudiants dans plusieurs cours

💡 Règle simple : Posez-vous la question "Un X peut avoir combien de Y ?" et vice-versa.

📝Pratique : Relation User ↔ Article

1Créons l'entité Article

php bin/console make:entity Article

💪 Ajoutez ces champs à Article :

  • title (string, 255, not null)
  • content (text, not null)
  • createdAt (datetime_immutable, not null)

2Ajoutons la relation

New property name:

→ Tapez: author


Field type (enter ? to see all types):

→ Tapez: relation


What class should this entity be related to?:

→ Tapez: User


Relation type? [ManyToOne, OneToMany, ManyToMany, OneToOne]:

→ Tapez: ManyToOne

(Plusieurs articles peuvent avoir le même auteur)

✅ Symfony a créé :

  • La propriété author dans Article
  • La propriété articles dans User (automatiquement !)
  • Les méthodes pour gérer la relation

🔍Le code des relations expliqué

📄Dans Article.php

PHP
#[ORM\ManyToOne(inversedBy: 'articles')]
#[ORM\JoinColumn(nullable: false)]
private ?User $author = null;

public function getAuthor(): ?User
{
    return $this->author;
}

public function setAuthor(?User $author): static
{
    $this->author = $author;
    return $this;
}

👤Dans User.php

PHP
#[ORM\OneToMany(mappedBy: 'author', targetEntity: Article::class)]
private Collection $articles;

public function __construct()
{
    $this->articles = new ArrayCollection();
}

public function getArticles(): Collection
{
    return $this->articles;
}

🎯 Comprendre mappedBy et inversedBy

  • mappedBy = "Je suis mappé par la propriété 'author' de l'autre côté"
  • inversedBy = "De l'autre côté, la propriété inverse est 'articles'"

💡 Mémotechnique : Le côté avec la clé étrangère (Article) utilise inversedBy

🚀Testons notre relation !

1Créer et exécuter la migration

php bin/console make:migration
php bin/console doctrine:migrations:migrate

2Code de test dans TestController

PHP
#[Route('/test-article', name: 'app_test_article')]
public function testArticle(
    EntityManagerInterface $entityManager,
    UserRepository $userRepository
): Response {
    // Récupérer un utilisateur existant
    $user = $userRepository->findOneBy(['email' => 'test@example.com']);
    
    // Créer un article
    $article = new Article();
    $article->setTitle('Mon premier article');
    $article->setContent('Contenu super intéressant !');
    $article->setCreatedAt(new \DateTimeImmutable());
    $article->setAuthor($user); // Lier à l'utilisateur
    
    // Sauvegarder
    $entityManager->persist($article);
    $entityManager->flush();
    
    return new Response(sprintf(
        'Article "%s" créé par %s',
        $article->getTitle(),
        $article->getAuthor()->getFullName()
    ));
}

✅ Testez !

Visitez http://localhost:8000/test-article

Résultat : "Article 'Mon premier article' créé par Jean Dupont"

💡 La relation fonctionne dans les deux sens ! Vous pouvez faire $user->getArticles() pour récupérer tous les articles d'un utilisateur.

🛡️Sécurisons avec la validation

❓Pourquoi valider ?

Sans validation, un utilisateur pourrait :

  • Entrer un email invalide : "pasdunemail"
  • Laisser des champs vides
  • Entrer des données trop longues

✅Ajoutons des contraintes à User

PHP
use Symfony\Component\Validator\Constraints as Assert;

class User
{
    #[ORM\Column(length: 180, unique: true)]
    #[Assert\NotBlank(message: 'L\'email est obligatoire')]
    #[Assert\Email(message: 'L\'email {{ value }} n\'est pas valide')]
    private ?string $email = null;

    #[ORM\Column(length: 255)]
    #[Assert\NotBlank(message: 'Le prénom est obligatoire')]
    #[Assert\Length(
        min: 2,
        max: 255,
        minMessage: 'Le prénom doit faire au moins {{ limit }} caractères',
        maxMessage: 'Le prénom ne peut pas dépasser {{ limit }} caractères'
    )]
    private ?string $firstName = null;
}

💪 Exercice : Ajoutez ces validations

  1. Sur lastName : NotBlank et Length (min: 2, max: 255)
  2. Sur birthDate : LessThan('today') - doit être dans le passé
  3. Sur password : Length (min: 6) quand on créera le formulaire
💡 Les messages personnalisés rendent l'expérience utilisateur bien meilleure !

⏰Automatisons avec les Lifecycle Callbacks

🤖 C'est quoi un lifecycle callback ?

C'est comme une alarme qui se déclenche automatiquement :

  • Avant de sauvegarder → mettre à jour la date
  • Après la création → envoyer un email de bienvenue
  • Avant de supprimer → nettoyer les fichiers associés

📅Exemple : Dates automatiques

PHP
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks] // Active les callbacks !
class Article
{
    #[ORM\Column]
    private ?\DateTimeImmutable $createdAt = null;
    
    #[ORM\Column(nullable: true)]
    private ?\DateTimeImmutable $updatedAt = null;
    
    #[ORM\PrePersist] // Avant la première sauvegarde
    public function setCreatedAtValue(): void
    {
        $this->createdAt = new \DateTimeImmutable();
    }
    
    #[ORM\PreUpdate] // Avant chaque mise à jour
    public function setUpdatedAtValue(): void
    {
        $this->updatedAt = new \DateTimeImmutable();
    }
}

✨ Magie !

Plus besoin de faire $article->setCreatedAt(new \DateTimeImmutable()) dans votre contrôleur. C'est automatique !

📥 Callbacks disponibles

  • @PrePersist
  • @PostPersist
  • @PreUpdate
  • @PostUpdate

🗑️ Et aussi

  • @PreRemove
  • @PostRemove
  • @PostLoad

🐛Les erreurs courantes (et leurs solutions !)

❌ Erreur 1 : "Table already exists"

Cause : Vous essayez de créer une table qui existe déjà

Solution :

# Vérifier l'état des migrations
php bin/console doctrine:migrations:status

# Si besoin, marquer la migration comme exécutée
php bin/console doctrine:migrations:version --add --all

❌ Erreur 2 : "Column cannot be null"

Cause : Vous avez oublié de définir une valeur obligatoire

Solution : Vérifiez que tous les champs NOT NULL ont une valeur

PHP
// ❌ Mauvais
$user = new User();
$entityManager->persist($user); // Email est null !

// ✅ Bon
$user = new User();
$user->setEmail('test@example.com');
$user->setFirstName('Jean');
// ... autres champs obligatoires
$entityManager->persist($user);

❌ Erreur 3 : "Integrity constraint violation"

Cause : Problème de relation (clé étrangère)

Solution : Vérifiez que l'entité liée existe

PHP
// ❌ Mauvais
$article->setAuthor($user); // $user n'est pas en base !

// ✅ Bon
$entityManager->persist($user);
$entityManager->flush(); // D'abord sauver l'user
$article->setAuthor($user);
💡 En cas d'erreur bizarre, videz le cache : php bin/console cache:clear

🛠️Boîte à outils : Commandes essentielles

📝Création d'entités

# Créer une nouvelle entité
php bin/console make:entity NomEntite

# Créer l'entité User (spécial)
php bin/console make:user

# Ajouter des champs à une entité existante
php bin/console make:entity NomEntite

🗄️Base de données

# Créer la base de données
php bin/console doctrine:database:create

# Créer une migration
php bin/console make:migration

# Exécuter les migrations
php bin/console doctrine:migrations:migrate

# Voir le statut des migrations
php bin/console doctrine:migrations:status

🔍Débogage

# Valider le schéma de base de données
php bin/console doctrine:schema:validate

# Voir toutes les entités
php bin/console doctrine:mapping:info

# Vider le cache
php bin/console cache:clear
💡 Créez un fichier commandes.txt dans votre projet avec ces commandes pour les avoir sous la main !

💪Exercice récapitulatif

🎯 Mission : Créer une entité Category

Votre mission est de créer une entité Category qui sera liée aux articles.

📋 Cahier des charges :

  1. Créer l'entité Category avec :
    • name (string, 100, not null, unique)
    • description (text, nullable)
    • createdAt (datetime_immutable)
  2. Ajouter une relation :
    • Une catégorie peut avoir plusieurs articles
    • Un article appartient à une catégorie
  3. Ajouter les validations appropriées
  4. Créer et exécuter la migration
  5. Tester avec un contrôleur
make:entity
Relations
Validations
Migration
Test
💡 Besoin d'aide ? Cliquez ici

Commandes à utiliser dans l'ordre :

  1. php bin/console make:entity Category
  2. Ajouter les champs demandés
  3. Pour la relation : ajouter un champ "category" dans Article (ManyToOne vers Category)
  4. php bin/console make:migration
  5. php bin/console doctrine:migrations:migrate

🚀Pour aller plus loin

📚Ce qu'on a appris aujourd'hui

  • ✅ Créer des entités Symfony
  • ✅ Comprendre les annotations/attributs Doctrine
  • ✅ Créer des relations entre entités
  • ✅ Valider les données
  • ✅ Utiliser les lifecycle callbacks
  • ✅ Gérer les migrations

🎯Prochaines étapes dans votre projet

1️⃣ Authentification

  • Formulaire de connexion
  • Registration
  • Mot de passe oublié

2️⃣ Formulaires

  • Symfony Forms
  • Validation côté serveur
  • Upload de fichiers

3️⃣ API REST

  • Serialization
  • API Platform
  • JWT tokens

🔗Ressources utiles

🎉Félicitations !

🏆 Vous êtes maintenant capables de :

  • Créer des entités professionnelles
  • Gérer les relations complexes
  • Valider vos données
  • Travailler en équipe sur un projet Symfony

💭 Souvenez-vous...

Les entités sont les fondations de votre application. Prenez le temps de bien les concevoir, votre futur vous en remerciera !

🤝Pour votre projet de groupe

Conseils de travail en équipe :

  • Définissez ensemble vos entités principales
  • Documentez vos relations dans un schéma
  • Utilisez les migrations pour synchroniser vos bases
  • Commitez régulièrement vos entités
  • Communiquez sur les changements de structure

Bon code et amusez-vous bien ! 🚀

Des questions ? C'est le moment ! 🙋‍♂️🙋‍♀️