Skip to content

Instantly share code, notes, and snippets.

@tomsihap
Last active July 29, 2022 14:52
Show Gist options
  • Select an option

  • Save tomsihap/bbec9a89da170c7d4e02bf8046799878 to your computer and use it in GitHub Desktop.

Select an option

Save tomsihap/bbec9a89da170c7d4e02bf8046799878 to your computer and use it in GitHub Desktop.
TP Symfony

TP-01 : Création du projet et routes

Exercices

1. Créer le projet

Créer un projet Symfony nommé Symazon qui sera une boutique en ligne.

Aide : Créer un nouveau projet En étant dans le dossier des projets Symfony, créeez le projet avec : $ composer create-project symfony/website-skeleton Symazon Puis rentrez dans le dossier avec cd Symazon et ouvrez le dossier Symazon dans votre IDE.

2. Créer un controller

Créer un controller grâce à la commande php bin/console make:controller nommé PagesController qui contiendra toutes les pages communes qui ne concernent pas un Model (par exemple: /home, /about, /contact...)

3. Créer nos premières routes

Créer la route /contact dans PagesController et la vue associée : elle affichera un formulaire dont l'action pointera vers la route /traitementContact.

Aide : Afficher une vue

  1. Créez le fichier template du formulaire de contact dans /templates/pages/contact.html.twig, qui héritera de base.html.twig grâce à `{% extends 'base.html.twig' %}
  2. Si vous souhaitez utiliser Bootstrap, vous pouvez l'inclure dans base.html.twig

Aide : Appeler une route dans Twig Pour appeler une route dans Twig (par exemple pour l'action du formulaire) :

  1. Nommez la route d'arrivée dans le Controller grâce à `@Route("/contact", name="app_contact")
  2. vous utiliserez la fonction path() de twig dans la vue, par exemple :
<form method="post" action="{{ path('nom_de_la_route') }}">
</form>
  1. Pour envoyer des paramètres à une route :
<a href="{{ path('articles_show', {'id': article.id}) }}">Voir l'article</a>

4. Récupérer les données du formulaire

Dans la définition de la méthode correspondant à /traitementForm, vous ajouterez en paramètres Request $request. Attention au use ! Il s'agit bien de ...\HttpFoundation\Request dans l'autocomplétion.

Exemple :

    /**
     * @Route("/traitementContact", name="app_traitement_contact")
     */

    public function traitementContact(Request $request) {

    }
  1. Débuguez $request grâce au dump/die : dd($var).
  2. Retrouvez dans l'objet Request les données du formulaire.
  3. Trouvez comment récupérer ces données POST venues du formulaire dans votre controller Symfony (Aide : https://lmgtfy.com/?q=symfony+request+post+params )

TP-02 : Enregistrer les données du formulaire de contact

Documentation : Doctrine https://symfony.com/doc/current/doctrine.html

Exercices

1. Optimiser le code existant : méthodes HTTP

Nous allons optimiser le code produit dans le TP-01 : nos routes sont pour le moment toutes accessibles en POST ou GET alors que :

  • le formulaire ne doit accepter que GET
  • le traitement du formulaire ne doit accepter que POST

Modifiez vos routes en précisant les méthodes autorisées :

/**
 * @Route("/maRoute", name="app_ma_route", methods={"GET"})
 */

/**
 * @Route("/maRouteTraitement", name="app_ma_route_traitement", methods={"POST"})
 */

Vous pouvez également préciser plusieurs méthodes pour une route :

/**
 * @Route("/maRoute", name="app_ma_route", methods={"GET", "POST"})
 */

Il faudra à ce moment-là tester dans le controller si on vient de POST ou de GET pour adapter le comportement de la méthode, on le verra plus tard.

2. Créer une entité Contact

Vous créérez une nouvelle entité Contact grâce à la commande :

php bin/console make:entity Contact

Les champs seront les suivants :

Contact
-------
email           VARCHAR(150)    NN
message         TEXT            NN
created_at      DATETIME        NN

3. Assigner une valeur par défaut à l'attribut createdAt de l'entité

Par défaut, notre entité aura une valeur de createdAt égale à la date de création de l'object correspondant.

Vous allez donc créer un constructeur dans l'entité Contact qui va assigner une valeur par défaut à createdAt :

ATTENTION ! Le DateTime à utiliser avec l'autocomplétion est simplement use DateTime, pas un package Symfony\Component\....

class Contact {

    public function __construct() {
        $this->setCreatedAt( new DateTime() );
    }
}

4. Configurer la base de données

Nous allons :

  1. Créer la base de données
  2. Créer une migration, c'est à dire un fichier qui compare nos entités à l'état de la base de données et qui effectue les requêtes SQL correspondant aux différences (par exemple: l'entité a un champ en plus que la table en BDD, alors la requête de la migration sera ALTER TABLE ... )
  3. Exécuter la/les migrations

Création de la base de données

  1. Modifiez le fichier .env à la racine du projet avec vos identifiants de base de données. Dans db_name, vous pouvez saisir Symamzon. Ce n'est pas grave si la BDD n'existe pas encore : on va la créer avec la console !

  2. Saisissez dans la console du projet : php bin/console doctrine:database:create

S'il n'y a pas d'erreurs, passez à la suite.

Créer les migrations et les exécuter

  1. On vérifie qu'il n'y ait pas de migrations en attente : php bin/console migrate

  2. On crée les migrations : php bin/console make:migration

  3. On migre : php bin/console migrate

Attention : lorsque vous ferez des migrations, faites DANS TOUS LES CAS ces trois lignes successivement ! Cela vous évitera de nombreux soucis de BDD.

5. Enregistrer les données

Maintenant que notre base de données et notre entité sont prêtes, nous allons reprendre la route /traitementContact, créer un objet Contact et l'hydrater des données du formulaire :

public function traitementContact(Request $request) {

    $email = $request->request->get('email');
    $message = $request->request->get('message');

    /**
     * 1. Créer l'object Contact et l'hydrater
     */

    $contact = new Contact;
    $contact->setEmail($email);
    $contact->setMessage($message);
}

Ensuite, nous allons enregistrer cet objet en base de données. Pour cela, on va instancier l'EntityManager qui s'occupe de la manipulation des objets en base de données. Grâce à l'héritage de notre classe de AbstractController, nous avons accès à divers services et instances de classe, notamment Doctrine, notre ORM :

$entityManager = $this->getDoctrine()->getManager();

On dit ensuite à Doctrine que nous souhaitons enregistrer (persister) notre objet en base de données :

$entityManager->persist($contact);

Enfin, on exécute réellement la ou les requêtes en attente (ici, l'insert) :

$entityManager->flush();

Voici le code final de notre méthode :

public function traitementContact(Request $request) {

    $email = $request->request->get('email');
    $message = $request->request->get('message');

    /**
     * 1. Créer l'object Contact et l'hydrater
     */

    $contact = new Contact;
    $contact->setEmail($email);
    $contact->setMessage($message);

    $entityManager = $this->getDoctrine()->getManager();

    $entityManager->persist($contact);
    $entityManager->flush();
}

Testez votre code. Vous devriez avoir une erreur vous indiquant que la route ne retourne pas de Response: en effet, toutes les routes Symfony doivent retourner un objet Response. Néanmoins, le message devrait être enregistré en BDD !

6. Retourner une redirection et un message de confirmation après l'enregistrement

Nous voulons maintenant rediriger l'utilisateur vers le formulaire d'origine, avec une alerte indiquant que le message a bien été envoyé.

1. Redirection

Nous voulons rediriger l'utilisateur sur le formulaire une fois le message enregistré.

Pour cela :

  1. Nommez la route /contact, par exemple app_contact, de sorte à pouvoir l'appeler par la suite.
  2. Après le flush du traitement du formulaire, rajoutez une redirection vers la page de formulaire :
return $this->redirectToRoute('app_contact');

2. Ajout d'un message flash

Nous voulons avertir l'utilisateur que le message a bien été envoyé.

Pour cela, nous allons utiliser les Flash Messages (doc: https://symfony.com/doc/current/controller.html#flash-messages).

Il s'agit en fait d'un array de messages existant dans la session de l'utilisateur, qui se suppriment une fois appelés (en effet, une fois le message affiché, on n'en a plus besoin).

Dans le controller, après le flush, et avant la redirection :

// On met un flash de type "info", avec le message :
$this->addFlash(
    'info',
    'Le message a bien été envoyé !'
);

Ensuite, dans base.html.twig, on va afficher les flash messages si il y en a.

Mettez ce code avant {% block body%}{% endblock %} :

{% for message in app.flashes('info') %}
    <div class="alert alert-success" role="alert">
        {{ message }}
    </div>
{% endfor %}

Ce code va faire une boucle sur les messages flashs de l'application et les afficher dans une alert Bootstrap.

TP-03 : Créer un CRUD

Exercices

1. Créer un CRUD avec la commande

  1. Créer une entité Category :
Category
---
name    VARCHAR 150 NN
  1. Créer un CRUD : php bin/console make:crud Category

Les formulaires autogénérés peuvent prendre le style Boostrap en modifiant config/packages/twig.yaml et en ajoutant l'attribut suivant :

twig: form_themes: ['bootstrap_4_layout.html.twig'] Attention, ce sont bien 4 espaces et non pas une tabulation !

  1. Étudiez les fichiers créés et testez sur localhost:8000/category.

  2. Comment index() réussit à récupérer tous les éléments ? Comprennez bien le fonctionnement de cette méthode.

2. Ajout d'un message Flash

  1. Ajoutez un message Flash de type info avec le message La catégorie a bien été créée., à la création d'une catégorie, qui s'affichera dans base.html.twig.

3. Création d'une route pour trier les catégories

Le but est de créer une route /category/by/{name} de sorte à pouvoir retrouver des catégories filtrées par nom. Par exemple, /category/by/jardi nous retournerait "jardinières", "jardinage", "jardin" (si ces catégories existent bien sûr !)

  1. Créez une nouvelle route /category/by/{name}, avec un paramètre string $name dans la méthode.
  2. Pour le moment, la méthode doit retourner la vue category/index, avec tous les éléments, comme le fait la méthode index() du controller.

4. Création d'une méthode dans le Repository

  1. Ouvrez CategoryRepository.php et ajoutez la méthode suivante :
    /**
     * @return Category[] Returns an array of Category objects
     */
    public function findByName(string $name) {

        return $this->createQueryBuilder('c')
            ->andWhere('c.name LIKE :name')
            ->setParameter('name', '%'.$name.'%')
            ->orderBy('c.id', 'ASC')
            ->getQuery()
            ->getResult()
            ;
    }
  1. Dans la méthode du controller créée dans l'exercice 3, remplacez findAll() par findByName($name).

  2. Testez votre route en allant sur /category/by/UN_NOM_DE_VOTRE_CHOIX

TP-04 : Créer un CRUD pour Product et une relation dans le CRUD en ManyToOne

Exercices

1. Création de l'entity

Créez une Entity Product :

Product
name        VARCHAR 255 NN
description TEXT        NN
photo       VARCHAR 255 NN
price       FLOAT       NN
stock       INT         NN
created_at  DATETIME    NN
category    ManyToOne   Entity:Category

2. Entity : valeurs par défaut

Créez un constructeur dans l'entité Product (avec public function __construct() {}), et utilisez les setters pour appliquer une valeur par défaut sur createdAt (voir le TP 1 et 2).

Effectuez les migrations.

3. Créer un CRUD

Créez un CRUD pour l'entity Product.

4. Mettre à jour la classe ProductType

Les classes *Type sont des créateurs de formulaire, contenant plusieurs paramètres pour chaque champs. Ces formulaires sont ensuite appelés dans le controller, puis envoyés à la vue.

Pour le moment, la route /product/new génère un bug à propos de la relation avec Category.

Vous allez modifier la classe ProductType de sorte à ce que nous ayons un select en HTML qui nous permette de choisir une catégorie.

  1. Modifier la ligne ->add('category') en vous inspirant de la documentation : https://symfony.com/doc/current/reference/forms/types/entity.html#basic-usage

Attention Pensez bien à importer correctement les classes avec l'autocomplétion

  1. Testez votre formulaire de produit !

TP-05 : Mise en pratique

Vous allez créer un site complet en vous inspirant des TP précédents. Vous pourrez utiliser du code autogénéré avec make:crud par exemple, mais dans tous les cas il y aura un effort de mise en page à faire !

L'objet de ce site sera de publier vos souvenirs de vacances dans un mini réseau social.

Modèle de données

Post
---
username    VARCHAR 255 NN
message     TEXT        NULLABLE
photo       VARCHAR 255 NULLABLE
likes       INT         NULLABLE
created_at  DATETIME    NN

Pensez bien à mettre une valeur par défaut pour created_at !

Projet

Header

Page d'accueil

Page d'ajout d'un Post

  • Vous ferez un formulaire qui permette d'ajouter un nouveau Post. Pour le moment, vous laisserez le champ "photo" en un input text classique.
  • Après avoir posté un Post, vous redirigerez vers la page d'accueil.

Améliorations à voir en cours

  • Upload de fichiers
  • Authentification
  • Espace administrateur et modération des posts

TP : Projet Vacances

Le but de l'application sera d'être une plateforme où sont enregistrées plusieurs destinations (des villes, des moments, par ex : Paris, Londres, Taj Mahal, Times Square, Valée de Chaudefour...)

Il y aura une page par ville ou destination

Les utilisateurs logués pourront ajouter des commentaires et photos sur la destination

Ils pourront noter sur 5 la destination

Ils pourront cliquer sur "j'ai visité" : il y aura un compte de visites sur la page d'une destination et la liste des destinations visitées

On peut éventuellement mettre un maximum d'informations sur la page de l'utilisateur : récupérer les photos et commentaires postés par exemple

Page d'accueil : caroussel avec les 5 villes les plus visitées

Modèle de données

Destination : ville, monument, évenèment... à visiter

Destination
---
name
country
description
type

Type : type ou catégorie d'une destination (ville, monument, évènement...)

Type
---
name

Review: avis avec photo (nullable) sur une Destination

Review
---
content
notation
photo

User

User
---
email
photo
pseudo
password

Relations

  • User OneToMany Review
  • User ManyToMany Destination
  • Review ManyToOne Destination
  • Destination ManyToOne Type

Actions

CreerDestination

Un User créée une nouvelle Destination

VisiteDestination

Un User possède une nouvelle Destination

AjouterReview

Un User créée une nouvelle Review sur une Destination et possède une nouvelle Destination si il ne l'avait pas déjà

ListeDestinations

Liste des Destination visitées par le User

ListeReview

Liste des Review visitées par le User

Pages et rôles

Il y aura un système d'authentification et de rôles.

  • CRUD Destination (UD : ROLE_ADMIN)
  • CRUD Review (UD : ROLE_ADMIN + User propriétaire)
  • CRUD Type (CUD : ROLE_ADMIN)
  • CRUD User (UD : ROLE_ADMIN)

Idées

  • Caroussel en page d'accueil avec les 5 destinations les plus visitées
  • Étoiles de notation sur 5
  • Page profil public, un utilisateur logué peut voir les profils des autres utilisateurs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment