Nous allons voir la partie "Model" du framework MVC Symfony.
Dans la plupart des projets Symfony, la connexion à la base de données est effectuée grâce à Doctrine.
Doctrine est un ORM (Object-Relational Mapping).
Cela veut dire que les tables SQL ne sont pas manipulées directement (grâce à des requêtes SQL classiques), mais que chaque table est représenté par une classe.
Dans la précédente partie du TP, nous avons créé un objet Article
qui est la représentation d'un Article de blog dans
notre application.
Doctrine va prendre une classe comme celle-là et va la faire correspondre (mapper) à une table SQL.
Par exemple, le champ title
de notre classe correspondra au champ title
dans notre table SQL article.
Cette notion est assez compliquée à comprendre, nous allons essayer de comprendre par l'exemple.
On installe Doctrine à l'aide de composer. orm
est un alias de symfony/orm-pack
qui installe toutes les dépendances
nécessaires à Doctrine pour fonctionner.
composer require orm
Symfony Flex modifie le fichier .env
pour y ajouter une variable DATABASE_URL
avec une valeur par défaut.
En général, les valeurs par défaut pour MySQL sont l'utilisateur root sans mots de passe.
La valeur de db_name
n'a pas d'importance, c'est Doctrine qui va se charger de créer la DB, vous pouvez donc mettre ce
que vous souhaitez.
Enfin pour la version, vérifier que vous ayez bien la 5.7, si ce n'est pas le cas, remplacez 5.7 par votre version actuelle
Si vous avez une base mariadb, elle est considérée par Doctrine comme mysql donc, faites comme si c'était un MySQL.
Vous devriez donc remplacer dans le fichier .env
:
- DATABASE_URL="postgresql://app:[email protected]:5432/app?serverVersion=15&charset=utf8"
+ DATABASE_URL="mysql://root:@127.0.0.1:3306/my_blog?serverVersion=5.7&charset=utf8mb4"
NB : Doctrine possède une partie nommée DBAL (Database abstraction layer), qui fait abstraction du SGBD utilisé. Il est donc possible d'utiliser PostgreSQL ou SQLite par exemple. Pour éviter les problèmes spécifiques à chaque plateforme, nous allons utiliser MySQL dans le cadre de ce TP.
Commandes pour créer la base de données :
php bin/console doctrine:database:drop --force --if-exists
supprime la base de données si elle existe.php bin/console doctrine:database:create
- création de la base de données.
src/Entity
, il faut le supprimer avant de continuer.
Créer une entité App\Entity\Article
avec l'aide de php bin/console make:entity
.
Pour les différents champs à ajouter et leur type, référez-vous à la partie sur Twig à l'exception de body où vous
mettrez text
au lieu de string
.
Les champs :
- title (string, le titre de l'article)
- subtitle (string, le sous-titre, servant aussi de résumé pour la liste)
- createdAt (DateTime, la date de création) NB : DateTime est une classe PHP représentant une date (avec heure et timezone)
- author (string, le nom de l'auteur)
- content (text, le contenu de l'article)
- imageUrl (string, l'adresse de l'image)
Observez la classe Article
créée par la commande.
Vous remarquerez l'attribut #[ORM\Entity]
en haut de classe, qui permet de spécifier à Doctrine que cette classe est
une entité. Il y a également des attributs #[ORM\Column]
au-dessus de chaque propriété qui servent à définir le type
et les contraintes de chaque colonne en base de données.
Cette commande a également généré une seconde classe ArticleRepository
mais nous y reviendrons plus tard.
Voici un exemple tiré de la documentation officielle qui illustre le travail effectué par Doctrine.
Une fois votre entité complète, il faut créer et exécuter une migration. Une migration est un fichier qui est créé par Doctrine et qui contient les requêtes SQL à exécuter pour créer, modifier ou supprimer une table.
Pour cela Doctrine compare le mapping (les attributs dans la classe Article
) et l'état actuel de la base de données.
Comme la table article
n'existe pas, Doctrine va la créer avec tous les champs de notre entité.
Exécuter la commande php bin/console doctrine:migration:diff
qui calcule la différence entre le mapping et la DB.
Observez le fichier généré dans src/Migrations
Pour l'exécuter, il faut taper la commande php bin/console doctrine:migration:migrate
Cette commande va exécuter toutes les migrations qui n'ont pas encore été appliquées à la base de données. Ainsi, si vous tapez cette commande à nouveau, Doctrine devrait vous dire qu'il n'y a rien à exécuter.
Il en est de même pour php bin/console doctrine:migration:diff
, cela va générer un fichier de migration vide, car il
n'y a plus de différences entre le mapping et la DB.
La commande php bin/console doctrine:migrations:status
permet de voir l'état des migrations.
Afin de faciliter le développement et avant d'avoir à notre disposition un formulaire pour créer des articles, nous allons créer des fixtures.
Ce sont des fichiers php dans lesquels nous allons créer de faux articles pour le développement.
Regardez la documentation des fixtures pour comprendre comment cela fonctionne. Appliquez cette logique à nos articles pour créer 10 articles avec l'aide de Faker pour générer des données aléatoires lisibles : Librairie Faker pour rappel via composer require.
Puis chargez ces fixtures avec la commande php bin/console doctrine:fixtures:load
Si tout s'est bien passé, vous devriez avoir 10 articles en DB.
Il faut récupérer les articles grâce à la classe ArticleRepository
que nous avions volontairement mis de côté plus tôt
dans le TP.
Un Repository, dans Doctrine, c'est une classe qui possède des méthodes permettant de récupérer des entités.
Par défaut, un répository possède plusieurs méthodes, dont la méthode findAll() que nous allons utiliser dans notre Controller.
Grâce au Dependency Injection Container (DIC).
En Symfony, chaque classe créée est considérée comme un service à l'exception des classes définies dans Entity
et Migrations
ainsi que la classe Kernel.php.
Un service, c'est un objet qui a une fonction précise et qui sont gérés par le DIC.
Ce DIC nous permet d'injecter les services dans nos controllers par exemple (mais aussi dans d'autres services !), c'est-à-dire rendre disponible le service dans le controller.
Généralement, l'injection de dépendances est réalisée dans le constructeur de notre controller et depuis Symfony 4, nous pouvons utiliser l'autowiring pour l'injection de dépendances.
L'autowiring consiste à utiliser le typage des paramètres du constructeur pour injecter nos dépendances automatiquement.
Plus d'informations sur les services et le DIC ici.
Ici, en ajoutant le code ci-dessous à notre HomeController, nous avons maintenant accès à $this->articleRepository.
private $articleRepository;
public function __construct(ArticleRepository $articleRepository)
{
$this->articleRepository = $articleRepository;
}
use
en haut de votre fichier pour importer ArticleRepository
.
Utiliser maintenant la méthode findAll()
du repository pour remplacer le tableau articles
que nous passions au
template Twig.
En actualisant la page, vous devriez voir vos articles créés grâce aux fixtures !
Cliquez sur l'icône "doctrine" dans la debug bar.
Vous arrivez sur une page du Profiler qui vous indique quelles sont les requêtes SQL qui ont été exécutées.
On voit ici que Doctrine génère une requête SQL qui SELECT tous nos champs de la table article.
Oui... Si demain, on a 100, 1000, 10000 articles, ils seront tous affichés sur la page d'accueil.
Vous pouvez même tester en augmentant le nombre d'itérations dans vos fixtures pour générer des centaines ou milliers d'articles.
C'est là qu'intervient : le repository !
Nous allons ici créer une méthode customisée findLast
dans le repository qui va nous permettre de récupérer seulement
les derniers articles.
public function findLast(int $count) {
return $this->createQueryBuilder('article')
->getQuery()
->getResult()
;
}
Nous créons ici un QueryBuilder
, c'est l'objet que nous allons manipuler afin de construire nos requêtes dans un
langage assez proche du SQL, le DQL (Doctrine Query Language).
Le code ci-dessus revient à exécuter la requête SQL SELECT * FROM article
, sauf que Doctrine va automatiquement créer
des objets Article
à partir du résultat de la requête SQL. C'est ce qu'a également fait la méthode findAll
que nous
avons utilisé précédemment. Ce processus est appelé hydratation.
À l'aide du code ci-dessus et de la documentation du QueryBuilder, récupérez les 4 derniers articles et affichez-les sur la page d'accueil, du plus récent au plus ancien.
À nouveau, regardez l'onglet Doctrine dans le profiler et observez la nouvelle requête générée.
Dans cette partie, nous avons vu comment ajouter une connexion à la base de données grâce à Doctrine, les entités et les repositories.
Nous avons aussi vu comment générer des tables SQL grâce aux entités et aux migrations et comment générer de fausses données pour le développement.
Nous avons également abordé l'injection de dépendances.
Avec tout ça, nous avons maintenant presque une page d'accueil fonctionnelle à l'exception de :
Les liens sur le titre des articles doivent nous rediriger vers la vue de l'article en question, c'est ce que nous allons voir dans le prochain tp.