Compte-rendu du TP Symfony
I. Tutoriel : Maîtriser les Notions de Symfony
Symfony est un framework PHP basé sur le MVC (Modèle-Vue-Contrôleur). Ce guide retrace les étapes et les explications pour faire une application web avec Symfony.
1 L'Environnement de Travail
Aujourd'hui pour héberger des site web on utilise en majorité Linux. Plutôt qu'une machine virtuelle lourde on va utiliser WSL (Windows Subsystem for Linux) pour installer PHP, MySQL et Composer.
# Installation et mise à jour wsl --install -d Ubuntu-24.04 sudo apt update && sudo apt upgrade -y sudo apt install -y php8.2 php8.2-cli php8.2-common php8.2-mysql php8.2-zip php8.2-gd php8.2-mbstring php8.2-curl php8.2-xml php8.2-bcmath php8.2-intl sudo apt install -y mysql-server curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | sudo -E bash sudo apt install symfony-cli # Création d'un nouveau projet symfony new mon_projet --webapp
2 Architecture MVC & Premier Contrôleur
Dans Symfony, tout commence par une Route qui appelle une méthode de Contrôleur. Une Route permet de répondre à la question : "Si l'utilisateur demande cette adresse, quel code dois-je exécuter ?". La route fait donc le pont entre le navigateur et notre application. Elle ne contient aucune logique, elle se contente d'identifier l'intention de l'utilisateur. Les routes peuvent aussi être dynamiques. Par exemple, /article/{id} permet de capturer un numéro d'article variable et de le transmettre au système.
Une fois que la route a trouvé le bon chemin, elle appelle une méthode spécifique dans une classe appelée Contrôleur. Il reçoit la Requête (Request), interroge les services (comme la base de données), et doit impérativement retourner une Réponse.
Ce système de séparation offre plusieurs avantages : On peut changer l'URL d'une page sans modifier la logique du code. Chaque action possède sa propre méthode, ce qui évite d'avoir des fichiers de code interminables et qui permet de vibler et modifier plus facilement.
# Exemple d'une route qui appel un Controlleur
app_galerie:
path: /galerie/{page} // Quand l'utilisateur rentre cet url
controller: App\Controller\GalerieController::index // On appel le controller GalerieController
defaults:
page: 1
requirements:
page: '\d+'
3 La Vue : Twig & Assets
Dans Symfony, on ne mélange pas le code PHP brut avec le HTML. On utilise Twig un moteur de template qui rend le code plus lisible, plus sûr et surtout plus facile à maintenir. Le contrôleur joue le rôle de préparateur en regroupant les informations (issues d'une base de données par exemple) dans un tableau associatif, qu'il transmet ensuite à la méthode render(). Une fois dans le fichier Twig, chaque clé de ce tableau devient une variable utilisable. L'affichage se fait alors avec une syntaxe simple : on utilise les doubles accolades {{ }} pour injecter une valeur (comme une image) et les balises {% %} pour la logique d'affichage. Cette séparation garantit que le HTML reste lisible et sécurisé, car Twig nettoie automatiquement les données pour tout sécuriser.
# Exemple d'affichage d'une image
<img src="{{ asset('images/' ~ image.url) }}">
4 Le Modèle : Doctrine (BDD & ORM)
Doctrine est l'ORM (Object-Relational Mapper) par défaut de Symfony. Au lieu d'écrire des requêtes SQL à la main, on manipule des Entités, qui sont des classes PHP. Grâce au fichier .env qui centralise les identifiants de connexion, Doctrine établit le pont : quand on crée ou modifie un objet en PHP, l'ORM génère et exécute automatiquement les requêtes INSERT ou UPDATE nécessaires dans la base de donnée. Pour recuperer les données on ne fait pas de SELECT mais on utilise les get des entité (comme on est en POO).
# Création de table et migration
php bin/console make:entity
php bin/console make:migration
php bin/console doctrine:migrations:migrate
# Exemple d'une entité
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180)]
private ?string $email = null;
public function getUserIdentifier(): string
{
return (string) $this->email;
}
}
5 Formulaires & Upload
La gestion des formulaires dans Symfony agit en sécurisant l'entrée des données : plutôt que de manipuler du HTML, on crée des classes PHP qui valident et transforment automatiquement les saisies utilisateur en objets exploitables. Pour l'upload, le fichier est intercepté sous forme d'objet UploadedFile, permettant de vérifier son extension et son poids via des contraintes de validation avant tout traitement.
# Traitement du fichier dans le contrôleur
$newFilename = uniqid().'.'.$file->guessExtension();
$file->move($this->getParameter('images_directory'), $newFilename);
6 Sécurité & Authentification
Le Security Bundle de Symfony à deux fonctions : l'authentification, qui vérifie l'identité de l'utilisateur (via une entité User et un authentificateur), et l'autorisation, qui définit ses droits d'accès. En implémentant la UserInterface, notre entité devient compatible avec le système de pare-feu (firewall) du framework, lequel intercepte les requêtes pour s'assurer que les identifiants sont valides et que les mots de passe sont hachés de manière sécurisée. Une fois l'identité confirmée, le système s'appuie sur des rôles hiérarchisés (tels que ROLE_ADMIN) pour restreindre dynamiquement l'accès à certaines routes ou fonctionnalités, garantissant ainsi une protection de l'ensemble de l'application.
// Protection d'une route
#[Security("is_granted('ROLE_ADMIN')")]
public function add(...)
II. État des Lieux des Exercices Réalisés
Exercice 1 à 3 : Socle de l'Application
Mise en place de l'environnement WSL et création de la structure de la galerie. J'ai configuré les routes incluant des contraintes sur les paramètres (ex: page doit être un entier \d*).
Code important : Configuration des routes et controllers
# src/Controller/GalerieController.php
// --- 1. GALERIE & FILTRES ---
public function index(int $page, Request $request, EntityManagerInterface $em): Response
{
$repository = $em->getRepository(Image::class);
$formFilter = $this->createFormBuilder()
->add('auteur', TextType::class, ['required' => false])
->getForm();
$formFilter->handleRequest($request);
if ($formFilter->isSubmitted() && $formFilter->isValid()) {
$nomAuteur = $formFilter->get('auteur')->getData();
$images = $repository->findBy(['auteur' => $nomAuteur], ['id' => 'DESC']);
} else {
$limit = 6;
$offset = ($page - 1) * $limit;
$images = $repository->findBy([], ['id' => 'DESC'], $limit, $offset);
}
return $this->render('galerie/index.html.twig', [
'images' => $images,
'page' => $page,
'form' => $formFilter->createView()
]);
}
# la route
# --- ROUTES DE LA GALERIE ---
app_galerie_add:
path: /galerie/add
controller: App\Controller\GalerieController::add
app_galerie:
path: /galerie/{page}
controller: App\Controller\GalerieController::index
defaults:
page: 1
requirements:
page: '\d+'
# --- ROUTES DE SÉCURITÉ (LOGIN / LOGOUT) ---
app_login:
path: /login
controller: App\Controller\SecurityController::login
app_logout:
path: /logout
controller: App\Controller\SecurityController::logout
# --- ROUTE D'INSCRIPTION ---
app_register:
path: /register
controller: App\Controller\RegistrationController::register
Exercice 4 à 7 : BDD & Formulaires
Création de l'entité Image avec validations strictes sur le titre et la date. Mise en œuvre du filtrage par auteur via un formulaire de recherche. Mise en place de la possibilité d'ajouter d'upload des images.
Code important : Upload d'image avec un formulaire.
# Formulaire
class ImageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('titre', TextType::class)
->add('auteur', TextType::class)
->add('date', DateType::class, ['widget' => 'single_text'])
->add('fichier', FileType::class, [
'mapped' => false,
'required' => true,
'constraints' => [
new File([
'maxSize' => '5M',
'mimeTypes' => ['image/jpeg', 'image/png'],
])
],
])
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults(['data_class' => Image::class]);
}
}
Exercice 8 : Sécurisation
Mise en place du système d'authentification complet. Création de l'entité User, des formulaires de login et d'inscription, et protection de la route d'ajout d'image.
Code important : AppAuthenticator.php
# src/Security/AppAuthenticator.php
class AppAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
public function __construct(private UrlGeneratorInterface $urlGenerator)
{
}
public function authenticate(Request $request): Passport
{
$email = $request->getPayload()->getString('email');
$request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->getPayload()->getString('password')),
[
new CsrfTokenBadge('authenticate', $request->getPayload()->getString('_csrf_token')),
new RememberMeBadge(),
]
);
}
// src/Security/AppAuthenticator.php (ou GalerieAuthenticator.php)
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// Si l'utilisateur essayait d'accéder à une page protégée avant (ex: /galerie/add),
// on le renvoie vers cette page.
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// SINON, on le renvoie vers la galerie par défaut :
// Remplace la ligne "throw new \Exception..." par celle-ci :
return new RedirectResponse($this->urlGenerator->generate('app_galerie'));
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
III. Bilan Personnel
💡 Difficultés Rencontrées
Le plus dur a été de devoir à chaque fois réinstaller l'environnement WSL, puis Symfony, PHP, etc. Ça m'a fait perdre beaucoup de temps que je n'ai pas pu dépenser à explorer Symfony. Puis j'ai eu un souci lors de la dernière séance qui a fait que j'ai perdu tout mon projet (j'étais à l'exercice 8 sur la sécurité). J'ai donc dû refaire tout en une seule séance (je me suis beaucoup aidé de l'IA) et je suis revenu à l'exercice 8. L'une de mes déceptions, c'est donc de ne pas avoir pu finir car je n'ai pas fait les exercices 9 et 10 : passer en mode production, voir les callbacks de Doctrine et les relations entre entités.
✅ Satisfactions
J'ai plutôt bien aimé Symfony : c'est une nouvelle façon de voir les sites web et la manière de les concevoir. De plus, je n'ai pas éprouvé de difficulté en soi pour comprendre le fonctionnement et je pense avoir acquis de bonnes bases solides pour la SAE à venir. J'ai notamment bien compris le modèle MVC.
Conclusion
Le TP a été réalisé avec succès jusqu'à l'étape 8. J'ai acquis une maîtrise des piliers de Symfony : le routage, la gestion des templates, l'interaction avec la base de données via Doctrine et la sécurisation des accès. Ce projet constitue une base solide pour développer mes futurs sites web.
📦 Code Source du Projet
Vous pouvez télécharger l'intégralité du code source (src, templates, routes).
Télécharger le code (.zip)