Bonjour tous le monde,
Je suis actuellement en train de créer un site de photo avec une partie admin et j'ai actuellement un petit soucis :
Je suis en train de faire une page de liste de catégories qui redirigera l'utilisateur vers une catégorie choisie.
Je veut donc faire apparaitre mes catégories fraichement créé avec mon Dashboard, ainsi que mes éléments stocké en bdd avec une boucle for.
Le soucis c'est que pour faire mon href et mon {{path('')}} je ne sais pas comment l'écrire, car je veux que dans mon path, il y ai le nom de ma catégorie (category.name), car c'est le Name de ma route.
J'ai grossièrement tenté de faire ça <a href="{{ path({{'category.name'}}) }}" > mais cela n'a pas fonctionné, auriez vous une idée s'il vous plait ? (j'espère que j'ai été clair, ce n'est pas évident a expliquer comme soucis ahah)

{% for category in category %}

                        <div class="col-lg-3 col-md-4 col-xs-6 gal-item">
                            <div class="box">
                                    <a href="{{ path({{'category.name'}}) }}" >
                                    <img src="/uploads/files/{{category.picture}}">
                                </a>
                            </div>
                            <div>
                                <h3 class="text-center cat-title mt-2">{{category.name}}</h3>
                            </div>
                        </div>
                    {% endfor %}

28 réponses


Et comment il est fait ton fichier de routes?

yonea80
Auteur

je n'ai pas utilisé mon ficher de route, mes routes sont définies dans mon controller :

    /**
     * @Route("/galeries/mariage", name="mariage")
     */
    public function mariage(): Response
    {
        $mariage = $this->entityManager->getRepository(Gallery::class)->findMariage();
        return $this->render('gallery/mariage.html.twig',[
            'mariage'=>$mariage
        ]);
    }

Ah oui c'est vrai que les routes sont définies dans les commentaires sur synfo (je suis sur Laravel ^^')

alors pour commencer, tu ne peux pas mettre de {{ }} à l'intérieur d'un autre {{ }}, le {{ }} intérieu seras considéré comme une string et donc ce sera pas parsé

la doc synfony donne cet exemple:

// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class BlogController extends AbstractController
{
    // ...

    /**
     * @Route("/blog/{slug}", name="blog_show")
     */
    public function show(string $slug): Response
    {
        // $slug will equal the dynamic part of the URL
        // e.g. at /blog/yay-routing, then $slug='yay-routing'

        // ...
    }
}

Synfony va comprendre que {slug} dans ta route est une variable, et qu'il attends d'avoir un certian slug, dans ton cas ce sera {category}

et dans ta vue:

{{ path('blog_show', {slug: $myBlogPost}) }}

Ah et dans ta vue, tu ne peux pas mettre de {{}} à l'intérieur d'un autre, sinon ton code va considérer que le {{}} intérieur est une chaine de caractère, il ne va pas le lire et lire uniquement les {{}} extérieurs :p

yonea80
Auteur

Je comprends, du coup ce n'est pas possible de mettre dans mon {{ path('') }} un élément récupéré d'une base de données ?

Si, en mettant la donnée en deuxième argument:

{{ path('laRouteDeTonController', {category: $taDonneeRecupereeEnBase}) }}

Et dans ton controller

/**
     * @Route("/photos/{category}", name="laRouteDeTonController")
     */
    public function show(string $category): Response
    {
        // $slug will equal the dynamic part of the URL
        // e.g. at /blog/yay-routing, then $slug='yay-routing'

        // ...
    }

Ou si dans Synfo tu peux faire comme Laravel tu injecte directement le Model:

/**
     * @Route("/photos/{category}", name="laRouteDeTonController")
     */
    public function show(Category $category): Response
    {
        // $slug will equal the dynamic part of the URL
        // e.g. at /blog/yay-routing, then $slug='yay-routing'

        // ...
    }

^^

yonea80
Auteur

Salut, merci pour ta réponse ca ne fonctionne toujours pas mais ça a l'air d'avancer un peu ^^
j'ai modifié mon code du coup quand je clique pour acceder a ma catégorie, j'ai cet URL : http://localhost/galeries?category=Mariage
du coup la redirection ne se fait pas

Voici mon code :

Vue de la liste des catégories :

 {% for category in category %}
                    <!-- Famille -->
                    <div class="col-lg-3 col-md-4 col-xs-6 gal-item">
                        <div class="box">
                            <a href="{{ path('gallery',{category: category.name}) }}" >
                                <img src="/uploads/files/{{category.picture}}">
                            </a>
                        </div>
                        <div>
                            <h3 class="text-center cat-title mt-2">{{ category.name }}</h3>
                        </div>
                    </div>
            {% endfor %}

Controller qui affiche ma liste de catégorie, puis l'acces a une des catégories (mariage) :


    /**
     * @Route("/galeries", name="gallery")
     */
    public function index(): Response
    {
        $category = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/index.html.twig',[
            'category'=>$category
        ]);
    }

    /**
     * @Route("/galeries/{category}", name="mariage")
     */
    public function mariage(string $category): Response
    {
        $mariage = $this->entityManager->getRepository(Gallery::class)->findMariage();

        return $this->render('gallery/mariage.html.twig',[
            'mariage'=>$mariage
        ]);
    }

Okay alors le problème viens de la:

 <a href="{{ path('gallery',{category: category.name}) }}" >

En gros tu lui dit d'aller dans la route gallery, cette route gallery ne prends aucun paramètres, du coup il te le rajoute dans l'url mais il l'ignore
Il y'a ensuite ta route marriage qui lui prends une variable category, mais tu ne l'as pas appelée

il faut utiliser la route mariage
Dans la route mariage tu utilises bien les variables:

/**
     * @Route("/galeries/{category}", name="mariage")
     */

Par contre... c'est bien tu lui donnes category, mais dans ta fonction mariage tu ne l'utilises pas :/

En fait je pense que tu t'es trompé d'endroit, tu as placé la category dans la méthode mariage alors quelle devrait être dans la méthode index, enfin en vrai elle devrait etre dans sa propre méthode, rajoutes ça entre index et mariage:

/**
     * @Route("/galeries/{category}", name="gallery.category")
     */
    public function category(Category $category): Response
    {
        return $this->render('gallery/index.html.twig',[
            'category' => $category
        ]);
    }

Et dans ta vue mets ce lien:

    <a href="{{ path('gallery.category',{category: category.name}) }}" >

EDIT: Ah par contre je vois que tu à fait une boucle avec category dans ta vue, tu veux pouvoir donner plusieurs catégories en même temps? :/

yonea80
Auteur

Oui, c'est ça, je veut afficher des liens redirigeables vers toutes les catégories disponibles

Car actuellement, mon site les affiches,mais avec du code qui se repete dans la vue et dans le controller.
Je voulais rendre le code dynamique afin de pouvoir créer les catégories avec mon dashboard et y afficher les photos sans avoir a modifier mon code par la suite, mais cela me semble assez compliqué à réaliser ^^

Je vois, mmmh alors sur Laravel c'est beaucoup plus simple, par contre Symfo, il y'a des choses à faire pour simplifier le tout

Déjà commences par tes routes, tu devrais les retirer des commentaires et les mettre dans le fichier routes.yaml, plus simple à organiser
https://symfony.com/doc/4.1/routing/external_resources.html

Ensuite ta méthode mariage, c'est une catégorie, don tu peux virer cette méthode, mariage c'est une valeur dynamique

Maintenant tu veux afficher la liste des catégories, et un lien qui redirige vers une seule catégorie

Donc tu peux faire un truc du genre dans ton fichier routes.yaml

# Affiche toute les photos.
gallery.index:
    path: gallerie
    controller: App\Controller\GalleryController::index

# Affiche une catégorie de photos.
gallery.category:
    path: gallerie/{category}
    controller: App\Controller\GalleryController::category

# Affiche une photo.
gallery.show
    path: photo/{picture}
    controller: App\Controller\GalleryController::show

Ensuite dans ton controller, tu peux faire

    // Dans l'index il n'y a pas de categories, tu affiches toutes les phots, et tu as "categories" pour faire ta liste.
    public function index(): Response
    {
        $categories = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/index.html.twig',[
            'categories' => $categories,
        ]);
    }

    // Ici tu as la partie category, en gros toi tu as mis en dur marriage, mais pour une valeur dynamique il faut mettre category
    // car ça peut être mariage ou famille ou autres, c'est des categories
    // Ensuite tu as la variable categories, ca c'est autre chose, c'est la liste des categorie pour pouvoir mettre
    // la liste avec les liens
    // Bref "Category" c'est la categorie actuelle après avoir cliqué sur le lien, et "categories" c'est pour faire ta liste de 
    // categories.
    public function category(Category $category): Response
    {
        $categories = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/category.html.twig',[
            'categories' => $categories,
            'category' => $category,
        ]);
    }

    // Bon ça c'est bonus c'est pour voir une seule photo que tu aurait selectionné. x)
    public function show(Picture $picture): Response
    {
        return $this->render('gallery/show.html.twig',[
            'picture' => $picture,
        ]);
    }

La version clean sans mes commentaire du controller :p

    public function index(): Response
    {
        $categories = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/index.html.twig',[
            'categories' => $categories,
        ]);
    }

    public function category(Category $category): Response
    {
        $categories = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/category.html.twig',[
            'categories' => $categories,
            'category' => $category,
        ]);
    }

    public function show(Picture $picture): Response
    {
        return $this->render('gallery/show.html.twig',[
            'picture' => $picture,
        ]);
    }

Maintenant dans ta vue twig

    {% for category in categories %}
        <div class="col-lg-3 col-md-4 col-xs-6 gal-item">
            <div class="box">
                <!-- Tu mets en premier argume le nom de ta route, dans ton yaml tu veux 'gallery.category', et en deuxieme argument tu lui donnes le model category -->
                <a href="{{ path('gallery.category', category) }}" >
                    <img src="/uploads/files/{{category.picture}}">
                </a>
            </div>
        <div>

        <h3 class="text-center cat-title mt-2">{{category.name}}</h3>
{% endfor %}

Maintenant si tu veux une version plus propre et que tu es pointilleux sur les standards, pour ta liste de catégories il faudrait utiliser un component Vue ou React :p

yonea80
Auteur

Salut, merci a toi j'ai pu pas mal avancer et surtout, comprendre comment afficher la page d'une catégorie :)
Maintenant, pour afficher les photos dynamiquement selon la catégorie de celles ci, je vais devoir créer une nouvelle requete SQL pour permettre l'affichage selon la catégorie (avec un where précisant que l'id de la catégorie de la photo doit être égale a l'id de categorie) si j'ai pas tord

De rien ;)

Alors en PHP oui tu aurait du créer une catégorie
Mais pas tu es dans un framework :p
Tu doit juste modifier ta base de données, dans photos tu rajoutes une colonne category_id
Et depuis le model Category tu fais une methode photos qui retourne un hasMany(Photo::class) (enfin ça c'est en Laravel, il retournera l'équivalent sur Symfo), et dans le model Photo tu fais une methode qui retournes un belongsTo(Category::class) (pareil je t'ai mis la version Laravel, je connais pas la relation version symfo ^^'

Et comme ça pour retourner les photos d'une catégorie, tu aura juste à rcupérer la catégorie, et dans ta vue tu mets {{ foreach (category.photos as photo) }}

Bonjour,

Oui, c'est tout à fait ça, je me permets juste d'apporter quelques précisions.
Dans symfony, tu ne vas pas directement modifier ta BDD mais modifier ton entité Photo (en faisant 'php bin/console make entity Photo') la CLI de symfony te proposera d'ajouter un champ à l'entité, il faudra choisir 'Relation' puis 'OneToMany' (si une photo n'appartient qu'à une catégorie), la CLI te proposera un nom de champ et te génèrera automatiquement le getter et le setter ainsi que le champ dans l'entité Category (avec getter/setter).
A partir de là, tu pourras générer une migration et l'appliquer pour mettre à jour ta BDD.

Ah bah comme ça tu as la version symfony, c'est pratique d'avoir quelqu'un qui connaisse le framework dans ce sujet x)

yonea80
Auteur

Mes entités sont déja reliées entres elles, j'ai déjà un champ catégorie dans mon entité galerie, cependant je viens de voir que la relation galerie->category est en ManyToOne, et category->galerie est en OneToMany, cela peut poser probleme?

et ben ça, c'est la logique de ton appli mais je ne vois pas de problème: plusieurs galeries peuvent appartenir à la même catégorie et une catégorie peut avoir plusieurs galeries.

yonea80
Auteur

Pour ma requête afin d'afficher les photos d'une seule catégorie j'ai fait ceci je ne sais pas si c'est correct :

    public function findOneByIdJoinedToCategory(): array
    {
        return $this->FindByCatQuery()
            ->addSelect('g')
            ->innerJoin('g.category', 'c', 'WITH', 'g.category = c.id')
            ->where('g.category = c.id')
            ->getQuery()
            ->getResult();
    }

Après pour l'affichage, je ne sais pas trop comment je pourrais m'y prendre, auriez vous une idée s'il vous plait ?
Voici le controller :

    /**
     * @Route("/galeries", name="gallery")
     */
    public function index(): Response
    {
        $categories = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/index.html.twig',[
            'categories'=>$categories
        ]);
    }

    /**
     * @Route("/galeries/{categories}", name="cat")
     * @ParamConverter("Category", options={"mapping": {"categories" : "id"}})
     */
    public function cat(Category $Category): Response
    {
        $gallery = $this->entityManager->getRepository(Gallery::class)->findOneByIdJoinedToCategory();
        $categories = $this->entityManager->getRepository(Category::class)->findAll();
        return $this->render('gallery/cat.html.twig',[
            'gallery'=>$gallery,
            'categories'=>$categories,
            'Category'=>$Category,
        ]);
    }
vue : 

    <h3 class="text-center mt-5 mb-5">{{ Category.name }}</h3>

<div class="gallery">
    {% for # in #  %}
        <a href="/uploads/files/{{ gallery.picture}}" class="fancybox" rel="ligthbox">
            <img src="/uploads/files/{{ gallery.picture }}" alt="" class="zoom img-fluid">
        </a>
    {% endfor %}

</div>

Ah tu as fait une requête à la main :/ Pourquoi tu n'as pas utilisé la relation?
Au lieux de findByCat, tu peux tout simplement faire $category->galleries et pouf ca te donne une collection de galleries, et y'a plus qu'a faire un foreach dessus pour avoir la gallery

Alors dans ton controller, tu remplaces galery par 'galleries' => $Category->galleries,

et dans ta vue ce sera

{% for gallery in galleries %}

Et pouf :)

Ensuite pour categories alors pour ce que tu veux en faire c'est bon, mais tu duplique le code à chaque controllers, pas fou, si tu peux ce serait bien que tu exportes ton menu dans un component et que tu fasse un include dans tes vues ^^

yonea80
Auteur

le remplacement de findByCat par $category->galleries et 'galleries' => $Category->galleries je ne pense pas que cela se fait sur symfony, je viens d'essayer et ca me met des erreurs dans mon ide ^^

Euh alors un coup tu déclares $category et un coup tu déclares $Category, du cout c'est deux variables différentes x)
Mets tout en minuscule :p

Ensuite dans les model tu as bien placé la relation? Parce que bon si tu possède une relation OneToMany mais qu'elle ne sert a rien (justement le OneToMany et autres que tu as, c'est pour faire la requette avec les liaisons, c'est leur seul et unique role :p)

Ah et apparement la syntaxe est... J'aime pas Symfony au fait :)

https://symfony.com/doc/current/doctrine/associations.html

En gros ce sera

$category->getGalleries()

Et en plus faut configurer la relation

# src/Resources/config/doctrine/Category.orm.yml
App\Entity\Category:
    type: entity
    # ...
    manyToOne:
        category:
            targetEntity: App\Entity\Gallery
            inversedBy: category
            joinColumn:
                nullable: false

Un truc du genre

... j'ai déjà dis que j'aimais pas symfo? :)

yonea80
Auteur

ahah oui je vois que tu aimes pas symfony :)
Sinon c'est bon c'est fonctionnel grâce a $category->getGalleries() merci j'en voyais plus le bout!
Du coup j'ai une derniere question pour l'optimisation, j'ai actuellement une catégorie vidéos, et je voulais savoir s'il valait mieux créer une entité appart et la separer de mon entité gallery, ou faut mieux la laisser ?
car actuellement, dans gallery, si je veut afficher mes vidéos je doit ajouter ces lignes dans ma page qui affiche le contenu des catégories :

 <div class="video-wrapper mb-2">
            <video controls class="video-js vjs-big-play-centered vjs-fluid vjs-16-9"  data-setup="{}">
                <source src="/uploads/files/{{ video.picture }}" type="video/mp4">
                <source src="/uploads/files/{{ video.picture }}" type="video/webm">
            </video>
            </div>

Du coup je ne sais pas si c'est bon pour l'opti de mélanger les photos et videos

Ah top ! :D

Niveau niveau optimisation y'a pas beaucoup d'impact, mais c'est mieux de faire une entité à part
Ensuite Gallery il faudrait remplacer le nom par Picture, Comme ça tu aura deux entités Picture et Video, qui appartiennent chacun à une catégorie

Maintenant si dans la même page tu as des photos et des vidéos mélangées, c'est autre chose, il faudra faire du polymorphique et rajouter une colonne type à gallery
Type pourras être picture ou video

et dans la vue...

    {% include 'path_de_ta_vue/{{ gallery.type }}.html.twig' %}

comme ça tu aura deux fichier de vue, picture.html.twig et video.html.twig une pour les photos si contiendra ton bout de code pour la photo, et une pour les vidéos qui contiendra les bout de code juste au dessus :p
Et dans la boucle de ta view ça va inclure le bon morceau de HTML ^^

yonea80
Auteur

ça marche je vais suivre ton conseil et renommer mon entité puis en créer une seconde pour les vidéos, ca sera plus simple et mieux organisé en effet ^^
Merci, je garde l'idée en tête au cas ou je devrais mélanger les photos et vidéos sur la même page, merci a toi et a gillesr pour m'avoir bien éclairé dans cette galère ahah :)

Bonjour,

D'abord, tu ne devrais pas avoir besoin de configurer tes relations dans un fichier yaml et dans tes entités, c'est soit l'un soit l'autre (perso, j'utilise des annotations qui m'évitent de passer sans arrêt du php au yaml).
je suis d'accord avec popotte sur le renommage (ce sera plus clair) et sur la façon de faire.
Tu récupère l'id de la catégorie dans l'url, le paramConverter de symfony te sort tout seul l'entité Category qui correspond et tu la passe dans le template.
Les pictures et videos sont récupérées via la relation OneToMany
Il ne reste plus qu'à boucler sur les images avec qqchose comme :


{% for picture in category.pictures %}
    <img src="{{picture.src}}" name="{{picture.name}}" alt="{{picture.name}}" />
{% endfor %}

idem pour les videos.
ça te permetra déja de structurer ta page correctement, et quand ça fonctionnera comme tu veux, tu pourras utiliser le profiler pour vérifier les requêtes et optimiser.

yonea80
Auteur

A votre avis, est ce que je devrais dissocier 'videos' des catégories?
Car actuellement comme elle est reliée, elle est affichée par ma boucle qui affiche toutes mes catégories, ce qui fait que la redirection est la même que pour mes photos, avec le template qui affiche uniquement des photos

Je ne suis pas sûr d'avoir bien comrpis ce que tu entends par dissocier 'videos' des catégories, mais je tente quand même.

Tout dépend du pourquoi tu souhaite le faire, pour des raisons d'évolution de ton modèle ou pour simplifier les vues ?
Si tu trouves que ton modèle n'est plus adapté, dans ce cas oui, c'est bien de le modifier. Si tu souhaites modifier ton modèle parceque ça simplifie tes vues, je ne suis pas certain que ce soit une bonne idée, je conseillerais plutôt de définir ton modèle en fonction du métier (la logique de ton appli) et de le faire évoluer en fonction si besoin, mais faire évoluer le modèle en fonction des vues, ça peut vite devenir compliqué à gérer.
Si dans ton template, tu ne souhaite pas afficher les vidéos, tu n'appelle que les pictures et c'est tout. Ensuite si tu vois que ça rame ou que tu veux optimiser les requêtes, tu pourras passer par le repository et ajouter des méthodes plus optimisées.

yonea80
Auteur

Je voulais dire supprimer la catégorie vidéos, et avoir uniquement une entité pour celle ci, puis garder galeries et catégories pour les photos. (Je n'ai pour le moment aucun intéret a filtrer les vidéos par catégories, et si par la suite je le souhaite, je pourrais créer une autre entity catégorie mais cette fois pour les vidéos)
Je voulais faire ça car actuellement mon controller affiche toutes les catégories (dont vidéos) avec une boucle for et me redirige sur la vue pour l'affichage des photos, je ne sais pas si a cause de cette boucle, s'il est possible de définir une autre route d'affichage, que je voudrais créer pour vidéos

Dans ce cas, oui si tu n'utilise pas, tu peux la supprimer sans problème ;)

yonea80
Auteur

Super, merci pour la confirmation, j'ai plus qu'a editer mes entités :)