Symfony 4 sluggable
La documentation concernant l'extensionsluggable
de Doctrine est éclatée à plusieurs endroits différents.Voici comment installer et utiliser l'extension
sluggable
de Doctrine.Installer l'extension Sluggable
Pour installer l'extension Sluggable
, il faut installer le bundle stof/doctrine-extensions-bundle
qui permet d'implémenter les extensions Doctrine.Exécuter cette commande pour installer le bundle
stof/doctrine-extensions-bundle
:composer require stof/doctrine-extensions-bundle
Entrez y et appuyez sur Entrée quand la console vous demandera si vous souhaitez exécuter les recipes
:
Symfony operations: 1 recipe (ff1cacb136314e5c284c704cc082a47e)
- WARNING stof/doctrine-extensions-bundle (>=1.2): From github.com/symfony/recipes-contrib:master
The recipe for this package comes from the "contrib" repository, which is open to community contributions.
Review the recipe at https://github.com/symfony/recipes-contrib/tree/master/stof/doctrine-extensions-bundle/1.2
Do you want to execute this recipe?
[y] Yes
[n] No
[a] Yes for all packages, only for the current installation session
[p] Yes permanently, never ask again for this project
(defaults to n): y
Voilà, le bundle stof/doctrine-extensions-bundle
est maintenant installé. L'extension Sluggable
est donc maintenant utilisable dans votre projet. Voyons comment l'implémenter.Activer l'extension Sluggable
Lorsque vous avez répondu y, Symfony a crée pour vous le fichier de configuration du bundle stof/doctrine-extensions-bundle
dans config/packages/stof_doctrine_extensions.yaml
.Vous devriez avoir ceci :
# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
stof_doctrine_extensions:
default_locale: en_US
Rajoutez-y les éléments suivants pour obtenir ceci :# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
stof_doctrine_extensions:
default_locale: en_US
orm:
default: # Ici, laissez "default" sauf si vous avez plusieurs connexions différentes à vos bases de données et dans ce cas, mettez le nom de votre connexion
sluggable: true
Voilà, l'extension Sluggable
est maintenant activée et utilisable dans votre projet. Voyons comment l'utiliser.Utiliser l'extension Sluggable
Il ne vous reste plus qu'à utiliser cette extension Sluggable
dans vos entités.Prenons l'exemple d'une entité appelée
Post
qui contient les postes d'un blog avec leurs titres, leurs descriptions et leurs date de publication :<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\PostRepository")
*/
class Post
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $titre;
/**
* @ORM\Column(type="string", length=8191, nullable=true)
*/
private $description;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $dateDePublication;
public function getId(): ?int
{
return $this->id;
}
public function getTitre(): ?string
{
return $this->titre;
}
public function setTitre(?string $titre): self
{
$this->titre = $titre;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getDateDePublication(): ?\DateTimeInterface
{
return $this->dateDePublication;
}
public function setDateDePublication(?\DateTimeInterface $dateDePublication): self
{
$this->dateDePublication = $dateDePublication;
return $this;
}
}
Pour ajouter à cette entité un champ
slug
qui contiendra le champ titre
sluggé, il faut rajouter un champ unique qui contiendra le slug (ici, je l'ai appeler tout bêtement slug
mais vous pouvez le nommer comme bon vous semble) :
// ...
class Post
{
// ...
/**
* @ORM\Column(type="string", length=255, unique=true)
*/
private $slug;
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
}
Ensuite, il faut rajouter l'utilisation du namespace Gedmo comme ceci :
use Gedmo\Mapping\Annotation as Gedmo;
Et rajouter l'annotation @Gedmo\Slug(fields={"titre"})
sur le champ qui contient le slug :
@Gedmo\Slug(fields={"titre"})
Nous nous retrouvons avec notre entité Post
comme ceci :<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Entity(repositoryClass="App\Repository\PostRepository")
*/
class Post
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $titre;
/**
* @ORM\Column(type="string", length=8191, nullable=true)
*/
private $description;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $dateDePublication;
/**
* @ORM\Column(type="string", length=255, unique=true)
*
* @Gedmo\Slug(fields={"nom"})
*/
private $slug;
public function getId(): ?int
{
return $this->id;
}
public function getTitre(): ?string
{
return $this->titre;
}
public function setTitre(?string $titre): self
{
$this->titre = $titre;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getDateDePublication(): ?\DateTimeInterface
{
return $this->dateDePublication;
}
public function setDateDePublication(?\DateTimeInterface $dateDePublication): self
{
$this->dateDePublication = $dateDePublication;
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
}
Et voilà, l'entité Post
contient un champ nommé slug
qui contient le titre
sluggé.PS : A cette étape, n'oubliez pas de mettre à jour le schéma de votre base de données en exécutant les commandes
php bin/console make:migration
et php bin/console doctrine:migrations:migrate
.
Utiliser les slug au lieu des id dans les liens
Maintenant, avouons qu'il serait plus user-friendly d'avoir des liens de la forme/blog/post/comment-cultiver-des-tomates
plutôt que de la forme /blog/post/18
.
Pour cela, dans nos contrôleurs, au lieu d'utiliser l'attribut {id}
de notre entité pour l'identifier, nous allons utiliser son nouvel attribut {slug}
.Nous avions ceci avant :
<?php
// ...
class BlogController extends AbstractController
{
// ...
/**
* @Route("/{id}", name="blog_post_show", methods={"GET"})
*/
public function show(Post $post): Response
{
return $this->render('blog/post/show.html.twig', [
'post' => $post,
]);
}
}
Nous avons maintenant ceci :<?php
// ...
class BlogController extends AbstractController
{
// ...
/**
* @Route("/{slug}", name="blog_post_show", methods={"GET"})
*/
public function show(Post $post): Response
{
return $this->render('blog/post/show.html.twig', [
'post' => $post,
]);
}
}
Avant, dans nos vues Twig, nous générions les liens comme ceci :
<a href="{{ path('blog_post_show', {'id': post.id}) }}">Voir le post</a>
Maintenant, nous les générons comme ceci :
<a href="{{ path('blog_post_show', {'slug': post.slug}) }}">Voir le post</a>
Même chose dans nos controlleurs, avant, nous générions des liens/redirections comme ceci :$this->redirectToRoute('blog_post_show', ['id' => $post->getId()])
Maintenant, nous les générons comme ceci :
$this->redirectToRoute('blog_post_show', ['slug' => $post->getSlug()])
Et voilà, vous savez maintenant utiliser l'extension
Sluggable
de Doctrine !Une erreur ? une question ? une critique ? une faute ? un conseil ? ou tout simplement un merci ?
Lâche ton commentaire
Attila Le dimanche 28 juillet 2019 à 23:01:28
Un grand merci pour ces explications claires qui m'ont fait gagner bien du temps
Un grand merci pour ces explications claires qui m'ont fait gagner bien du temps
shengovou Le jeudi 15 août 2019 à 16:09:49
le bundle stof génère une vingtaine de warning de dépréciation, sur symfony 4.3.3, est-ce qu'il est toujours maintenu ?
le bundle stof génère une vingtaine de warning de dépréciation, sur symfony 4.3.3, est-ce qu'il est toujours maintenu ?
David Le samedi 17 août 2019 à 12:11:38
@shengovou, quels sont les dépréciations qui sont émises ?
@shengovou, quels sont les dépréciations qui sont émises ?
Lemzo Le mardi 28 janvier 2020 à 11:26:09
Merci pour cet article, très riche.
Merci pour cet article, très riche.
semi Le samedi 19 mars 2022 à 17:47:16
j'ai cette erreur "An exception has been thrown during the rendering of a template ("Parameter "slug" for route "appartement_show" must match "[^/]++" ("" given) to generate a corresponding URL.")."
j'ai cette erreur "An exception has been thrown during the rendering of a template ("Parameter "slug" for route "appartement_show" must match "[^/]++" ("" given) to generate a corresponding URL.")."
David Le lundi 21 mars 2022 à 10:58:00
@semi, pour comprendre un peu mieux l'erreur, pouvez-vous dire quel lien avez-vous appelé pour obtenir cette erreur et pouvez-vous me donner l'annotatiion "@Route(...)" que vous avez utilisé ?
@semi, pour comprendre un peu mieux l'erreur, pouvez-vous dire quel lien avez-vous appelé pour obtenir cette erreur et pouvez-vous me donner l'annotatiion "@Route(...)" que vous avez utilisé ?
Olivier Le vendredi 25 mars 2022 à 18:50:31
Bonjour, je suis sous Symfony 5.3 et visiblement une fois la dépendance installée et configurée telle que précisé, le 'use Gedmo\Mapping\Annotation ne fonctionne pas.
Bonjour, je suis sous Symfony 5.3 et visiblement une fois la dépendance installée et configurée telle que précisé, le 'use Gedmo\Mapping\Annotation ne fonctionne pas.
Davi Le lundi 28 mars 2022 à 07:42:51
@Olivier, alors il se peut qu'il y ai des petites différences pour Symfony 5 car le tuto est fait pour Symfony 4. Il faudrait regarder directement sur la documentation du bundle pour voir s'il n'y a pas des adaptations à faire pour Symfony 5
@Olivier, alors il se peut qu'il y ai des petites différences pour Symfony 5 car le tuto est fait pour Symfony 4. Il faudrait regarder directement sur la documentation du bundle pour voir s'il n'y a pas des adaptations à faire pour Symfony 5