Symfony 5 : Concaténer l'id et le slug dans l'url

Par bahahmadou94, il y a 5 ans


Bonjour à vous,

J'suis entrain de développer une application en ce moment, pour des besoins SEO, j'me suis dis de faire en sorte que les url de mes pages contiennent l'id et le slug. Exemple : monapp.com/45-titre-de-ma-page. Dans ce cas je pourrais toujours trouver l'entrée avec son id (vu que ça change pas) même si le titre arrive à changer (slug).

J'utilise Gedmo de stof/doctrine-extensions-bundle pour la génération de mon slug, après la méthode suivante dans mon entité pour intégrer l'id dans le slug :

/** * @ORM\PostPersist */ public function updateSlug(EventLifecycleEventArgs $args) { $this->setSlug($this->getId(). '-' . $this->getSlug()); $args->getObjectManager()->flush(); }

Pour récupérer l'entrée j'utilise la méthode suivante dans mon controller :

/** * @Route("/{slug}", name="category_show", methods={"GET"}) */ public function show(CategoryRepository $categoryRepository, $slug): Response { $id = (int) explode('-', $slug, 2)[0]; return $this->render('category/show.html.twig', [ 'category' => $categoryRepository->find() ]); }

Le fonctionnement escompté marche, sauf que si par exemple monapp.com/45-titre-de-ma-page je mets monapp.com/45-titre-de-ma-page-autre-chose ça passe toujours.

Je trouve pas trop User friendly l'url du coup, ce serait mieux si l'url machait avec le titre

Donc je voudrais savoir s'il y'a une autre démarche plus robuste (je pose déjà des questions de performance de mon app) d'intégrer ce fonctionnement à mon app ?

Merci d'avance à vous 🙏🙏.

11 réponses

lhapaipai, il y a 5 ans

Salut bahahmadou94,
peut-être que tu peux vérifier si le permalien correspond et faire une redirection vers la bonne url si n'est pas le cas.
du genre

/** * @Route("/category/{id}-{slug}", name="category_show", requirements={"id"="\d+"}) */ public function show(CategoryRepository $categoryRepository, $slug, $id): Response { $category = $categoryRepository->find($id); if($category->getSlug() !== $slug) { $this->redirectToRoute('category_show', [ 'id' => $id, 'slug' => $slug ]); } return $this->render('category/show.html.twig', [ 'category' => $categoryRepository->find() ]); }
OcB974, il y a 5 ans

Bonjour bahahmadou94.
Si tu est déjà sous la v5 , symfony as un composant qui permet de gérer les slugs : https://symfony.com/doc/current/components/string.html#slugger.

Tu as un bonne exemple à cette adresse , qui ce génère au niveau de ton entité : https://symfony.com/doc/current/the-fast-track/fr/13-lifecycle.html#generating-slugs

Tu pourras par exemple enregistrer ton slug en faisant ce bout de code :

$this->slug = (string) $slugger->slug((string) $this->Id() . ' ' . $this->getTitle())->lower();

Ensuite dans t'as route en passant un param {slug} , il connaîtra automatiquement à la ligne qui correspond en BD.
Par contre faut faire gaffe que ça soit unique, mais comme tu concatène avec l'ID y'aura pas de soucis.
NB : Les ' ' sera remplacer automatiquement par des -

bahahmadou94, il y a 5 ans

Bonjour OcB974, merci pour le tuyau. J'y jeterai un coup d'oeil.

lhapaipai, il y a 5 ans

oups je suis allé un peu vite...

/** * @Route("/category/{id}-{slug}", name="category_show", requirements={"id"="\d+"}) */ public function show(CategoryRepository $categoryRepository, $slug, $id): Response { $category = $categoryRepository->find($id); if($category->getSlug() !== $slug) { return $this->redirectToRoute('category_show', [ 'id' => $id, 'slug' => $category->getSlug() ]); } return $this->render('category/show.html.twig', [ 'category' => $category ]); }

il n'y a pas plus de requêtes db du coup.

bahahmadou94, il y a 5 ans

Dans mon template voici comment je génère ducoup le lien :

<a href="{{ path('category_show', {'slug': category.slug, 'id': category.id}) }}">show</a>

Est-ce exact ??

Dans l'url je vois l'id répété : Exemple pour une url monapp.com/categorie/26-sport ça rédirige vers monapp.com/categorie/26-26-sport.

Une idée ?

lhapaipai, il y a 5 ans

tu as écrit

$this->slug = (string) $slugger->slug((string) $this->Id() . ' ' . $this->getTitle())->lower();

du coup ton lien contiens déjà ton id concaténé : id -> 26, slug -> 26-sport

$this->slug = (string) $slugger->slug($this->getTitle())->lower();

de cette manière : id -> 26, slug -> sport

bahahmadou94, il y a 5 ans

Oui j'suis allé un peu à la hâte.
C'était bien le cas, l'id se trouvait déjà dans mon slug.
Maintenant j'ai plus bésoin cette méthone là :

/** * @ORM\PostPersist */ public function updateSlug(EventLifecycleEventArgs $args) { $this->setSlug($this->getId(). '-' . $this->getSlug()); $args->getObjectManager()->flush(); }

Merci beaucoup 🙏🙏🙏

lhapaipai, il y a 5 ans

de rien cool que tu aies résolu ton problème !

bahahmadou94, il y a 5 ans

Bonjour lhapaipai, désolé de revenir 🤭. Ce matin j'étais entrain de faire mes tests et j'me suis rendu compte que la méthode donne une erreur au cas où il trouvait pas l'id de la categorie.

$category = $categoryRepository->find($id); if($category->getSlug() !== $slug) { return $this->redirectToRoute('category_show', [ 'id' => $id, 'slug' => $category->getSlug() ]); }

Sur la ligne de la condition car catégorie n'existe pas dans ce cas : Call to a member function getSlug() on null, une idée ??

Merci d'avance.

Lartak, il y a 5 ans

Bonsoir.
Tu pourrais par exemple commencer par vérifier que tu récupères bien un enregistrement avant de faire ta condition sur le slug.
Si ce n'est pas le cas, tu pourrais par exemple rediriger l'utilisateur avec un message vers une autre page.