Initialiser le projet
Installation des prérequis
Tout d’abord, il faut une version de PHP installée sur le poste, ou bien passer par Docker.
Il faut également avoir installé Composer sur le poste.
Ensuite, il faut Installer scoop pour pouvoir installer Symfony CLI. Il s’agit d’un utilitaire en ligne de commande faisant intermédiaire pour utiliser Composer.
# Ouvrir PowerShell puis saisir les commandes : # Installer Scoop Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression # Installer Symfony CLI scoop install symfony-cli # Confirmer l'installation symfony version # Vérifier les prérequis symfony check:requirements
Initialiser un projet Symfony
- Se rendre dans le répertoire qui contient les sites. Exemple :
cd C:\Users\Fabien\Sites\ - Créer un projet Symfony, commandes possibles :
# Composants pour un microservice, une application "console" ou bien une API symfony new <nom_répertoire_projet> # Ajout de tous les composants nécessaires à une application web (avec interface) symfony new --webapp <nom_répertoire_projet> # Préciser la version de Symfony à Utiliser symfony new --webapp --version=7.2 <nom_répertoire_projet>
En ayant créer un vhost Apache qui pointe vers le répertoire /public du projet, il devrait dès maintenant possible d’accéder au site depuis un navigateur. Une page par défaut de présentation de Symfony devrait s’afficher, avec la barre de débogage en bas de page. Si la debugbar ne s’affiche pas, il faut ajouter un fichier .htaccess dans le répertoire /public avec le contenu minimum suivant :
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>La console Symfony
Depuis le terminal, en étant dans le répertoire du projet on peut exécuter une commande permettant de lister toutes les commandes Symfony disponibles :
# A exécuter depuis le répertoire du projet pour lister toutes les commandes Symfony disponibles : symfony console list # Permet de lister les commandes d'une section en particulier, ici, la secion "cache" : symfony console list cache
Créer une route, premier contrôleur
On peut créer un contrôleur via l’IDE, ou bien grâce à la commande : symfony console make:controller. Cette commande créée également un template Twig dans un sous-répertoire au nom du contrôleur au sein du dossier templates.
On peut par exemple créer un contrôleur nommé DefaultController avec la route / nommée app_default.
Template par défaut, CSS (via Bootstrap) et Javascript
Le template base.html.twig constitue le socle commun à toutes les futures pages du site qui pourront donc l’étendre.
Le CSS par défaut se situe dans le fichier assets/styles/app.css. Il est chargé par le fichier assets/app.js.
Découverte de Symfony AssetMapper et des ImportMaps
Des fichiers CSS et Javascript peuvent être gérés grâce au composant AssetMapper de Symfony. Il repose sur les ImportMaps disponibles nativement en HTML. Ils permettent d’intégrer facilement des bibliothèques Javascript sans devoir passer par des outils lourds et complexes tels que Webpack. Une simple commande pour intégrer la bibliothèque suffit. AssetMapper est automatiquement présent si Symfony a été installé avec l’option --webapp.
Commandes Symfony disponibles :
# Lister les dépendances non à jour et la dernière version disponible php bin/console importmap:outdated # Mettre à jour toutes les dépendances php bin/console importmap:update # Idem, mais uniquement pour une dépendance en particulier (ici, Bootstrap) php bin/console importmap:outdated bootstrap lodash php bin/console importmap:update bootstrap lodash
Installation de Bootstrap et FontAwesome
Comme évoqué ci-dessus, une simple commande suffit.
symfony console importmap:require bootstrap php bin/console importmap:require @fortawesome/fontawesome-free/css/all.css
Il faut ensuite compléter le fichier assets/app.js en ajoutant :
import './vendor/bootstrap/dist/css/bootstrap.min.css';import './vendor/@fortawesome/fontawesome-free/css/all.css';
Utiliser une base de données grâce à Doctrine
Présentation de Doctrine
Doctrine est l’outil intégré à Symfony qui permet de se connecter à une BDD et d’échanger avec via PHP. Doctrine peut s’utiliser dans un projet PHP ne reposant par sur Symfony. Il permet par ailleurs d’utiliser aussi bien une BDD MySQL, que PostgreSQL, MariaDB, NoSQL…
Doctrine met notamment à disposition DBAL et ORM :
- DBAL : signifiant DataBase Abstraction Layer, il s’agit d’une couche d’accès aux données. Cela s’apparente à PDO dont DBAL facilite l’utilisation.
- ORM : signifiant Object Relational Mapper, permet de faire le lien entre la BDD et les classes PHP. Permet une sorte de correspondance entre les tables SQL et les classes PHP.
Paramétrer l’accès à la BDD
Le fichier .env à la racine du projet permet de configurer le DSN, Database Source Name, dans la variable d’environnement DATABASE_URL.
Exemple pour MySQL :
DATABASE_URL="mysql://root:H1d7djue0a6l@127.0.0.1:3306/ma-bdd?serverVersion=8.0.31&charset=utf8mb4"
Créer une entité et définir ses propriétés
- On utilise la commande :
symfony console make:entity Movie. - Chaque entité dispose d’une propriété
id, car c’est obligatoire avec Doctrine. - Chaque propriété est créée avec son type, sont accesseur et son mutateur (méthodes
getetset). Exception pour la propriétéidqui n’a pas de méthodeset.
- Les propriétés non nullables sont bien créées
NOT NULLdans la BDD mais sont nullable dans la classe PHP. Cela date de PHP 7.4, une propriété typée doit avoir une valeur par défaut. Doctrine met doncnullpar défaut, mais on pourra revenir sur notre propriété dans la classe PHP pour corriger le type et la valeur par défaut. Il faudra également modifier les typages dans les méthodesgetetset. - Les propriétés de type
ENUMsont bien créées ainsi dans la BDD, mais sont de typestringdans la classe PHP. Il faut revenir dessus pour modifier le type après avoir créé la classe de l’ENUM correspondant. Il faudra donc ensuite revenir sur notre propriété dans la classe PHP pour corriger le type et la valeur par défaut. Il faudra également modifier les typages dans les méthodesgetetset.
Créer et exécuter les migrations
Lorsque nos entités sont à jour, on peut demander à Doctrine de créer les migrations, puis de les exécuter.
# Générer les migrations symfony console make:migration # Exécuter les migrations symfony console doctrine:migrations:migrate
Les migrations sont dans le répertoire /migrations. Chaque migration contient une méthode up pour les changements à apporter et une méthode down permettant d’annuler ces changement. Le nom du fichier est constitué du timestamp afin que les migrations soient exécutées dans l’ordre.
Authentification et autorisation des utilisateurs
Symfony dispose de tout le nécessaire afin d’authentifier un utilisateur et, s’il tente d’accéder à une page sécurisé sans l’être, renvoyer une erreur HTTP 401 Unauthorized. Le nécessaire est également disponible pour vérifier les autorisations d’un utilisateur connecté et, s’il tente d’accéder à une page non permise, renvoyer une erreur HTTP 403 Forbidden.
Créer l’entité utilisateur
La classe de l’entité utilisateur doit implémenter l’interface Symfony\Component\Security\Core\User\UserInterface. Elle met notamment à disposition les méthodes :
getUserIdentifier: Retourne l’identifiant de l’utilisateur. Il peut s’agir de son email ou d’un login quelconque.getRoles: Retourne un tableau de chaines de caractères qui sont les droits de l’utilisateur.eraseCredentials: Permet de ne pas avoir à conserver en mémoire le mot de passe ’utilisateur avant hachage.
L’interface Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface peut également être implémentée. Elle met à disposition la méthode getPassword qui retourne le mot de passe haché de l’utilisateur. Elle n’est pas obligatoire, car l’utilisateur peut s’authentifier sans mot de passe, notamment dans des systèmes récents qui utilisent les passkey.
Pour créer une entité utilisant ces interfaces, le composant MakerBundle de Symfony dispose de la commande symfony console make:user qui créera une entité implémentant automatiquement l’interface UserInterface. Le chois sera donné d’implémenter ou non l’interface PasswordAuthenticatedUserInterface.
Créer un formulaire d’ajout / inscription d’utilisateurs
La commande symfony console make:registration-form permet de générer un formulaire d’enregistrement d’utilisateurs avec tout ce qu’il faut notamment pour le chiffrement du mot de passe dans le contrôleur associé (créé en même temps), l’envoi d’un email de vérification de l’adresse mail.
Après validation de la commande, le template register.html.twig peut être personnalisé. De même que le comportement des champs du formulaire dans l’objet RegistrationFormType. On pourra par exemple retirer la case à cocher « J’accepte les termes ». Le résultat est visible via la route /register.
Dans le contrôleur RegistrationController, on verra comment est chiffré le mot de passe saisi en clair dans le formulaire.
La méthode de chiffrement des mots de passe est définie dans le fichier config/packages/security.yaml. Par défaut, le mode auto indique que Symfony effectue lui-même le meilleur choix.
Authentifier un utilisateur et connaitre ses droits
Symfony utilise un UserProvider provider afin de chercher si un utilisateur correspond à l’identifiant soumit pour authentification et si oui, il retourne l’objet de l’utilisateur. Un second objet nommé Authenticator s’occupe ensuite de vérifier que le mot de passe de l’objet récupéré correspond avec celui soumit dans le formulaire d’authentification. Dans Symfony, pour vérifier si un utilisateur dispose ou non d’un droit, on utilise le système de Voters.
La notion de Firewall
Dans Symfony, ces deux étapes sont effectuées par un objet appelé Firewall. Il peut y avoir plusieurs firewalls, tous sont définis dans le fichier config/packages/security.yaml. Chaque firewall s’applique aux URL correspondant à l’expression régulière dans variable pattern ou à toutes les URL si cette variable est vide.
Dans le cas où plusieurs firewalls sont présents, c’est le 1er dont le pattern correspond qui s’applique.
Important à savoir, si un firewall dispose de la variable security avec la valeur false, alors aucune règle de sécurité ne s’applique.
Créer le formulaire d’authentification
Pour créer rapidement un formulaire d’authentification avec gestion du token CSRF et vérification du mot de passe, symfony met à disposition la commande symfony console make:security:form-login. Après exécution, il n’y a qu’à personnaliser la route dans le contrôleur SecurityController si on le souhaite, et personnaliser la mise en page du formulaire dans le template login.html.twig.
Lorsque l’authentification a réussi, on voit normalement l’identifiant de l’utilisateur apparaitre dans la debugbar de Symfony.
Aussi bien après authentification qu’après déconnexion, la page vers laquelle l’utilisateur doit être redirigé peut être personnalisée. Cela s’effectue dans le fichier config/packages/security.yaml. Exemple :
security:
# ...
firewalls:
# ...
main:
# ...
form_login:
# ...
default_target_path: after_login_route_name
logout:
# ...
target: app_default_indexLes autorisations / vérifier un droit utilisateur
Lorsqu’un utilisateur est connecté, on utilise les Voters afin de savoir s’il est ou non autorisé à effectuer une action, accéder à une page etc. La méthode isGranted permet de contrôler une autorisation spécifiée via une clé. Éventuellement, on peut préciser sur quel objet le contrôle doit être effectué (un utilisateur pourrait par exemple avoir le droit de modifier certaines fiches clients mais pas d’autres, selon des règles définies par l’application).
Vérification d’un droit depuis un contrôleur :
#
# Soit depuis un attribut sur la route.
# Soit en vérifiant le droit dans la méthode.
# Soit en renvoyant une AccessDeniedException toujours dans la méthode.
#
#[IsGranted('ROLE_KEY')]
public function funcName(): Response
{
if ($this->isGranted('ROLE_KEY')) {
}
$this->denyAccessUnlessGranted('ROLE_KEY');
}L’intérêt dans le contrôleur, c’est que l’attribut IsGranted peut être déclarer sur la classe elle-même, afin de s’appliquer à toutes les routes de la classe. De la même manière qu’on peut déclarer éventuellement un préfixe à toutes les routes d’un contrôleur.
Vérification d’un droit depuis un classe autre qu’un contrôleur :
class FirstClassName
{
public function __consctruct(
Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface $checker
)
{
}
public function funcName()
{
$this->checker->isGranted('ROLE_KEY');
}
}
# Autre solution
class FirstClassName
{
public function __consctruct(
Symfony\Bundle\SecurityBundle\Security $security
)
{
}
public function funcName()
{
$this->security->isGranted('ROLE_KEY');
}
}Dans un template :
{% if is_granted('ROLE_KEY') %}
{% endif %}Il est également possible de limiter très rapidement l’accès à un ensemble de routes via la section access_control du fichier config/packages/security.yaml. Les restrictions peuvent y être définies selon le préfixe de la route (ou une REGEX), une ou plusieurs adresses IP (ou masque), le host (ou une REGEX)… De nombreux exemples sont disponibles dans la documentation officielle : https://symfony.com/doc/current/security/access_control.html.
Créer de nouveaux rôles
Il existe des rôles et des « comportements » fournis en standard, comme savoir si un utilisateur est connecté :
ROLE_ADMINROLE_USERIS_AUTHENTICATEDPUBLIC_ACCESS
Des rôles supplémentaires peuvent être créés, ROLE_SUPER_ADMIN par exemple. Ils doivent simplement d’utiliser le préfixe ROLE_ et être en majuscules. Tous les rôles et leur hiérarchie peuvent être déclarés dans le fichier config/packages/security.yaml. Par exemple :
security:
# …
role_hierarchy:
ROLE_USER: ~
ROLE_GESTIONNAIRE: ROLE_USER
ROLE_AJOUT_FACTURE: ROLE_USER
ROLE_ENCAISSER_DECAISSER: ROLE_USER
ROLE_COMPTABLE: [ROLE_USER, ROLE_AJOUT_FACTURE, ROLE_ENCAISSER_DECAISSER]
ROLE_GESTION_UTILISATEUR: ROLE_USER
ROLE_ADMIN: [ROLE_COMPTABLE, ROLE_GESTION_UTILISATEUR]Les rôles d’un utilisateur sont dans la propriété roles de l’entité User et peuvent être définis et récupérés par les méthodes setRoles et getRoles. Leur comportement est à coder selon ce qu’on souhaite que l’application fasse.
Déploiement en production
En cas d’erreur 500, tester cette solution :
composer remove symfony/apache-packcomposer require symfony/apache-pack- Renvoyer en production le fichier
/public/.htaccessqui vient d’être régénéré.
En cas d’erreur 404 sur les CSS / JS :
- Sur l’environnement de développement, exécuter
php bin/console asset-map:compile --env=prod - Déployer en production le contenu de
/public/assets
Sources :
– OpenClassrooms : https://openclassrooms.com/fr/courses/8264046-construisez-un-site-web-a-laide-du-framework-symfony-7
– Symfony : https://symfony.com/doc/current/index.html