Skip to content

Instantly share code, notes, and snippets.

@tomsihap
Last active November 6, 2019 08:13
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomsihap/99b7ad51257721ee9c96bd2d95db504f to your computer and use it in GitHub Desktop.
Save tomsihap/99b7ad51257721ee9c96bd2d95db504f to your computer and use it in GitHub Desktop.
Cours Symfony 4

Symfony 4

Installation de Symfony

  • On va initialiser un projet de type website-skeleton avec la commande suivante en console : composer create-project symfony/website-skeleton symfony

  • Ensuite, pointez vers votre dossier de travail (cd symfony) et pensez à faire un git init afin de versionner votre travail.

Note: pensez à faire un commit à chaque étape ! Les commits vous serviront de fiche de révision.

  • Le projet créé, ouvrez-le dans VSCode et éditez le fichier .env afin de configurer l'accès à la base de données.

  • Comme la console l'indique lors de l'installation de Symfony, vous pouvez dès à présent accéder à votre application en lançant le serveur natif : php bin/console server:run et aller sur l'URL http://127.0.0.1:8000.

  • Prenez un moment pour découvrir les outils de débogage inclus !

Créer une première route

1. Créer une route dans le fichier config/routes.yaml

Il existe plusieurs façons de créer nos routes. Nous utiliserons le fichier routes.yaml :

home:
    path: /home
    controller: App\Controller\HomeController::home

On définit ici que la route /home pointera vers le contrôleur App\Controller\HomeController, et sa méthode home().

2. Créer le contrôleur

Nos contrôleurs seront dans le namespace App\Controller.

On sait grâce au fichier composer.json que le namespace App pointe vers le dossier src.

On va donc créer notre contrôleur dans le dossier src/controller, et bien indiquer le namespace correspondant.

Voici le contenu d'un contrôleur type :

  1. On définit le namespace comme prévu
  2. On définit la classe et la méthode indiquée dans la route
<?php

namespace App\Controller;

class HomeController {
	
	public function home() {
	
	}
}
  1. Notre méthode retournera un objet de type Response (précisément Symfony\Component\HttpFoundation\Response) .

Comme il est long d'utiliser Symfony\Component\HttpFoundation\Response, si notre IDE est bien configuré, lorsque l'on précise que la méthode doit il devrait importer l'alias use Symfony\Component\HttpFoundation\Response afin de pouvoir utiliser l'alias Response directement.

<?php

namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;

class HomeController {
	
	public function home() : Response {
	
	}
}

On peut indiquer le type de retour de la méthode est une Response avec method() : Response. C'est une feature de PHP 7, qui est un prérequis à Symfony 4.

Le type Response est issu du composant HttpFoundation, qui permet d'ajouter une couche d'abstraction aux Requests et Response HTTP et les gérer pour nous. Il nous permet de faire des choses comme :

// url : http://www.example.com/user?name=Julie
$request->query->get('name'); // retourne 'Julie'

// url : http://www.example.com/user/hello-world
$request->getPathInfo(); // retourne /user/hello-world

Toutes les actions de routes doivent retourner un type Response (en effet, lorsque l'on saisit une URL, on attend bien une réponse HTTP !). On utilisera donc ce composant pour chaque action.

3. Hello world

On va donc retourner un objet Response pour compléter l'action :

public function home() : Response {
	return new Response('Hello world!');	
}

En allant sur localhost:8000/home, on voit 'Hello world!' s'afficher.

4. Une autre façon de faire des routes: les annotations

Pour créer des routes, nous avons vu le fichier yaml. Nous pouvons également rédiger nos routes directement au dessus de la méthode correspondante ! Par exemple, créons la page d'accueil (la route est / et la méthode sera accueil()) .

Attention ! N'oubliez pas d'importer l'alias pour Route avec use.

[...]
use Symfony\Component\Routing\Annotation\Route;

[...]

	/**
	 * @Route("/", name="accueil")
	 */
	public function accueil() : Response {
		return new Response('Page d\'accueil');	
	}

En testant localhost:8000, on tombe bien sur la bonne route. L'avantage est d'avoir la route directement liée à l'action dans le code, le désavantage étant d'avoir des routes de partout et qu'il peut parfois être difficile de s'y retrouver.

4. Exercice

Créez une route pour "localhost:8000/profil" qui affichera Profil de l'utilisateur, via un contrôleur de Users et avec une route en YAML. Créez une route pour "localhost:8000/apropos" qui affichera A propos du site, via le contrôleur de Home et avec une route en Annotations.

Retourner du HTML

Maintenant que nous avons réussi à retourner une réponse à notre route, nous allons voir comment retourner du HTML. Symfony utilise un moteur de template, Twig, nous verrons un peu plus tard comment il s'utilise.

1. Importer Twig

Nous pourrions importer Twig dans notre contrôleur afin d'utiliser cet outil :

class HomeController {
	private $twig;
	public function __construct(Environment $twig) {
		$this->twig = $twig;
	}
	public function home() : Response {
		return Response($this->twig->render('home.html.twig'));
	}
}

Mais ça serait contraignant d'importer différents services à chaque contrôleur. Pour cela, nous allons dire à notre contrôleur d'hériter de AbstractController (fichier ici) qui contient des tas de helpers disponibles, et notamment Twig. C'est une classe abstraite, qui est en quelque sorte un modèle de classe (exemple: une classe abstraite Félin, inutilisable seule, et des classes qui Tigre et Chat qui en héritent).

Nous allons donc plutôt hériter de AbstractController:

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class HomeController extends AbstractController {

	public function home() : Response {
		return $this->render('home.html.twig');
	}
}

2. Utiliser Twig

Décryptons la ligne return $this->render('home.html.twig'); :

  • return : nous allons retourner une vue. En fait, toujours un objet de type Response, qui va retourner du HTML via un template Twig
  • $this->render() est issu du trait ControllerTrait, importé dans la classe abstraite AbstractController. Il permet d'effectuer le rendu de...
  • template.twig.html, qui est un fichier template qui contient du HTML et du code Twig.

Créons enfin le template dans le dossier suivant : /templates/home/home.html.twig, dans lequel nous saisissons juste Hello world.

Si tout se passe bien, en allant sur http://localhost:8000/home, nous voyons le contenu du fichier Twig.

Symfony vient avec un fichier template de base que nous pouvons utiliser plutôt que d'écrire une page HTML complète dans ce fichier ! En étudiant le fichier base.html.twig, on découvre qu'il y a des éléments "block". Ce sont les blocs que nous pouvons remplir dans un fichier Twig enfant, voici comment étendre home.html.twig:

{% extends "base.html.twig" %}

{% block title %}Home !{% endblock %}

{% block body %}
	<h1>Hello world</h1>
{% endblock %}

Si tout se passe bien, en retournant sur /home, nous retrouvons notre Hello world mais avec la barre de débug Symfony !

3. Exercice

Pour agrémenter notre vue, nous allons importer Bootstrap 4 et la navbar de base : dans base.html.twig, avant le block stylesheets et avant le block javascripts, vous importerez les assets de Bootstrap 4. Pour décorer tout cela, vous importerez la navbar par défaut de Bootstrap, que vous mettrez juste avant le block body, toujours dans le fichier base.html.twig. Créez une vue pour chacune des routes créées.

4. Ajout de liens

Dans base.html.twig, nous allons modifier le lien vers la page d'accueil. Pour cela, nous utiliserons la fonction path() qui prend en argument non pas une URL mais un nom de route. Par exemple, pour la page d'accueil, ce sera path('accueil'). Pour executer du PHP dans twig, nous allons utiliser des double-accolades :

<a class="navbar-brand" href="{{ path('home') }}">TestApp</a>

Symfony 4

Utilisation de l'ORM : Doctrine2

  • Doctrine est un ORM (Object-Relation Mapper), c'est à dire que l'on va utiliser des classes, des Entités, qui vont se charger de faire le travail en base de données plutôt que d'utiliser des outils comme PHPMyAdmin.
  • On parle de persister une entité plutôt que d'enregistrer une entité.
  • Vérifiez bien que votre .env a une base de données configurée ! La base de données n'a pas a être créée dans PHPMyAdmin (eh oui, Doctrine s'en occupe).
  • N'oubliez pas de lancer votre serveur de développement avec
    • php bin/console server:run

Base de données, tables, migrations

Exercice 1 : Base de données

Doctrine nous permet de créer la base de données pour nous: en effet, selon la base de données demandée dans .env, il existe une commande de console qui nous permet de créer la base de données si elle n'existe pas. Tapez php bin/console et retrouvez la commande qui correspond à la création d'une base de données.

Exercice 2 : tables

La console de Symfony nous permet de générer pour nous, avec make, des fichiers. Trouvez la commande permettant de créer une Entité ! Nous allons créer l'entité suivante :

Produits
---
title (varchar 255) NN
description (text) nullable
price ( DECIMAL(10,2) ) NN

Attention ! Comme pour les Models, le nom de l'entité doit être au Singulier avec une lettre majuscule (Film, User, Voiture...).

Vous pouvez voir que deux fichiers ont été créés :

  • src/Repository/ProduitRepository.php
  • src/Entity/Produit.php

Prenez le temps de regarder le fichier Entity qui a été créé.

Exercice 3 : migrations

Une fois l'entité créée, la console vous a normalement proposé une commande pour créer une migration. Lancez-la ! Une fois la migration créée, vous pouvez effectivement enregistrer les modifications en base de données grâce à la commande php bin/console migrate.

Exercice 4 : modifier une entité et migrations

Pour modifier une entité existante (et donc modifier la base de données), on peut relancer la commande permettant de faire une entité en passant en paramètres l'entité à modifier : php bin/console make:entity Produit

  • Ajoutez le champ suivant :
note (int) nullable
created_at (datetime) NN
  • Créez la migration
  • N'effectuez pas la migration !

Exercice 5 : champs par défaut

  • Nous voulons que la note ait une note de 3 sur 5 par défaut. Vous pouvez ajouter à l'annotation correspondante dans l'entité ceci, après "type".
options={"default": 3}
  • Nous voulons aussi que created_at ait la date actuelle en valeur par défaut. Nous allons effectuer ce changement par contre dans le constructeur afin de lui assigner un objet DateTime (plutôt que mettre un "CURRENT_TIMESTAMP" par défaut, qui n'est pas compatible avec tous les SGBD) :
  • Créez un constructeur après la déclaration des attributs dans l'entité.
  • Dans le constructeur, assignez à $this->created_at la valeur new \DateTime().
  • Créez la migration
  • Vérifiez dans la migration créée que le champ par défaut est bien présent
  • Effectuez la migration !

Interagir avec les données

Exercice 6 : Créer une page produits

Nous travaillons maintenant avec des produits :

  • Créez une route "/produits" et une méthode index() dans un contrôleur de produits ProduitController (cette fois produit est au singulier aussi).
  • Créez la vue correspondante, retournée par le contrôleur, dans le dossier produit/ (singulier aussi).

Exercice 7 : Créer un produit

Nous pouvons utiliser notre objet Entity pour créer un produit grâce à ses setters ! Créez un produit de cette façon :

$produit->setTitle('Premier produit')
->setDescription('Un nouveau produit !')
->setNote(4);

Pour l'enregistrer en base de données, on va utiliser l'entity manager présent dans l'AbstractController :

$em = $this->getDoctrine()->getManager();
$em->persist($produit);
$em->flush();

Si vous allez sur la page /produits, vous avez une erreur. Corrigez-la ! Une fois corrigée, vérifiez dans PHPMyAdmin que le produit a bien été enregistré.

Exercice 8 : le Repository

  • On veut maintenant récupérer les produits de la base de données. Mettez en commentaire toutes les instructions qui vous ont permis de créer un produit (en effet, on ne veut pas créer un nouveau produit à chaque fois que l'on va sur /produits !)
  • Pour commencer, on va récupérer le Repository pour l'entité Produit. Pensez à dumper ce repository afin de voir à quoi il ressemble !
$repository = $this->getDoctrine()->getRepository(Produit::class);
dump($repository);
  • Une autre manière de faire plus légère est d'instancier le Repository directement dans la méthode :
public  function  index(ProduitRepository $repository) : Response
{
   dump($repository);
   return  $this->render('produit/index.html.twig');
}
  • Comme nous allons utiliser ce Repository partout dans ce contrôleur, nous pouvons plutôt aussi l'instancier directement dans le constructeur : cela nous permettra de ne l'appeler qu'une fois, au constructeur, et d'y accéder via $this->repository.
class  ProduitController extends AbstractController
{
	private  $repository;
	
	public  function  __construct(ProduitRepository  $repository)
	{
		$this->repository = $repository;
	}

	/**
	* @Route("/produits", name="produits")
	*/
	public  function  index() : Response
	{
		dump($this->repository);
		return  $this->render('produit/index.html.twig');
	}
}

Exercice 9 : Jouer avec le Repository

Maintenant que le Repository est instancié, essayez de trouver les méthodes du repository qui permettent de récupérer les données et testez-les !

  • Méthode pour retrouver tous les enregistrements
  • Méthode pour retrouver 1 enregistrement par son id
  • Méthode pour retrouver des enregistrements par une valeur

Exercice 10 : Créer une méthode dans le Repository

  • En vous inspirant de la méthode commentée dans le repository, créez une méthode pour retourner tous les produits dont le prix est supérieur à 10 euros.

Exercice 11 : Afficher dans la vue

  • Passez dans la vue une variable contenant tous les éléments d'une table grâce à findAll : $data = $repository->findAll(); return $this->render('products/index.html.twig', compact('data'));
  • Parcourez les données de la variable passée en paramètre dans une vue Twig avec for...in, et affichez les données dans les cards Bootstrap. Par exemple pour afficher le titre :
{% for product in products %}
	{{ product.title }}
{% endfor %}

Exercice 12 : Filters avec Twig

  • En utilisant les filters, affichez la date dans une vue, dans un format lisible ('d-m-Y' par exemple).

Exercice 13 : Créer un lien vers une page produit

  • Créez une nouvelle route vers une méthode ProductController::show, pour afficher un produit.
  • Retournez une vue Twig pour cette page (produit/show.html.twig par exemple)
  • Remplissez cette vue Twig pour utiliser le template de base
  • Dans l'index des produits, ajoutez un lien pour chaque produit, qui redirigera vers la page d'un produit en passant un id. Exemple : <a href="{{ path('produit.show', { id : product.id }) }}">{{ product.title }}</a> Dans show, appelez un paramètre $id et utilisez-le pour récupérer les données du produit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment