Level 4 : sortie de Symfony 7.1
Salut collègue développeuse / développeur,
Quatrième édition de cette newsletter, et on dépasse les 160 inscrites et inscrits (bientôt le million) ! Merci à nouveau pour ton soutien 🙏
Si tu veux m'aider à faire décoller ce nombre 🚀, n'hésite pas à partager avec tes collègues qui comme toi, souhaitent performer en Symfony.
Et si tu n’es pas encore abonné et que tu ne veux pas rater les prochaines éditions, clique sans plus tarder sur le bouton ci-dessous.
Quoi de neuf ?
Ça y est ! Symfony 7.1 est là 🎉
Quelques nouveautés mineures ont été dévoilées depuis ma précédente newsletter où je listais les principales améliorations. Voici les plus notables :
Un debug simplifié
Il est maintenant possibile d’utiliser dd()
(dump and die) sans lui passer de variables. Utile quand on souhaite seulement stopper le code à un endroit sans voir spécifiquement le contenu d’une variable. C’est l’emoji :bug:
🐛 qui s’affichera dans ce cas.
🪦 RIP le dd('toto')
De plus jolis <select>
Depuis peu, les navigateurs supportent l’utilisation d’une balise <hr>
au sein d’un <select>
, afin d’avoir des séparateurs un peu plus jolis. Jusqu’alors, Symfony pouvait générer des séparateurs, en utilisant simplement des tirets, entre les options et les preferred_choices
. Il est maintenant possible d’utiliser le séparateur devra être un <hr>
.
Niveau code, il faut ajouter dans les options de ton ChoiceType
, la clé separator
avec <hr>
, et la clé separator_html
à true
.
->add('languages', ChoiceType::class, [
'separator' => '<hr>',
'separator_html' => true,
'preferred_choices' => ['php'],
'choices' => $languages,
])
Amélioration du mapping de paramètres de route
Il est super pratique dans Symfony, d’indiquer au niveau d’une route, un identifiant comme ceci
#[Route('/{id}', name: 'show')]
public function show(Product $product): Response
Dans ce cas, Symfony et Doctrine sont assez intelligents pour mapper l’id
en paramètre de route, avec l’entité Product
, et aller chercher le bon enregistrement en database (pas besoin d’appeler le ProductRepository
et sa méthode find()
).
Par contre, lorsque plusieurs entités sont en jeu dans la même route, cela devient plus compliqué, car il n’est plus possible de savoir quel id
correspond à quelle entité, ainsi le code ci-dessous ❌ ne fonctionne pas :
#[Route('/{id}/{id}', name: 'show')]
public function show(Category $category, Product $product): Response
// ❌ ne fonctionne pas
Dans ce simple cas ou le paramètre qui sert à faire le mapping est lui même la clé primaire, une astuce consiste à utiliser le nom de l’entité en paramètre. Doctrine ira alors récupérer la propriété correspondant à la clé primaire de la bonne entité :
#[Route('/{category}/{product}', name: 'show')]
public function show(Category $category, Product $product): Response
Mais complexifions un peu les choses. Si nous voulons utiliser un slug
du nom du produit dans la route, cela ne fonctionne plus car le paramètre {product}
va essayer de mapper par défaut un id
et non un slug
Là encore Symfony offre une solution à l’aide de l’attribut #[MapEntity]
:
#[Route('/{id}/{slug}', name: 'show')]
public function show(
#[MapEntity(id: 'id')] Category $category,
#[MapEntity(slug: 'slug')] Product $product,
): Response
Pratique, mais tout de même un peu verbeux. C’est pourquoi en 7.1, Symfony propose une nouvelle syntaxe supplémentaire pour faciliter ce type de configuration
#[Route('/{id:category}/{slug:product}', name: 'show')]
Propre ! Cette modification est super pratique et améliore l’expérience de code avec les paramètres de route, les rendant plus clairs.
⚠️ Mais attention, puisque c’est simple à implémenter, et empêche les ambiguités, il a été décidé de déprécier la syntaxe la plus basique avec uniquement l’id
, qui sera donc certainement retirée pour la sortie de Symfony 8.0. C’est un changement majeure puisque c’était la façon de faire par défaut depuis Symfony 2.0 !
Voici le résumé de la sytaxe dépréciée ❌
(
et donc à éviter à partir de maintenant), et des syntaxe permises ✅
❌ #[Route('/{id}', name: 'show')] // deprecated since 7.1
public function show(Product $product): Response
✅ #[Route('/{product}', name: 'show')]
public function show(Product $product): Response
✅ #[Route('/{id:product}', name: 'show')]
public function show(Product $product): Response
✅ #[Route('/{id}', name: 'show')]
public function show(#[MapEntity(id: 'id')] Product $product): Response
Source : https://symfony.com/blog/new-in-symfony-7-1-mapped-route-parameters
Symfony Jobs
Symfony vient de sortir sont propre Jobboard https://symfony.com/jobs 🤩
C’est une super initiative, car tu es sûr d’y trouver uniquement des jobs qui requiert en priorité du Symfony.
On peut sans doute déplorer une interface un peu austère et un manque de filtres de recherche, mais c’est une toute première version et des améliorations sont prévues dans le futur, sans doute en fonction de la popularité de cette page.
On y trouve des offres internationales, quelques unes pour freelance mais la majorité en CDI. Toutes les offres ont obligatoirement une fourchette de salaire.
En plus c’est gratuit (pour consulter ou déposer une offre), un super outil offert à la communauté !
Pour rappel, si tu es Freelance que tu cherches des top missions (et que tu n’as pas trouvé ton bonheur sur Symfony Jobs 😛) tu peux également rejoindre mon programme d’apport d’affaires en cliquant sur le lien ci-dessous et en remplissant mon formulaire de contact.
Vidéo
Je te parlais d’UX-icon dans une précédente newsletter, je te le (re)présente dans une vidéo que j’ai sorti il y a quelques jours. C’est vraiment super rapide et pratique à utiliser, n’hésite pas à tester :-)
Retour d’expérience
Ce que j’aime avec le dev, c’est qu’on a beau avoir des années et des années d’expériences sur un langage ou un framework, il y a toujours un petit truc qu’on ne sait pas, alors qu’il est peut-être utilisé par la majorité.
Pour ma part, j’ai découvert la semaine dernière, l’option priority
sur les routes (décidémment je parle beaucoup de routes aujourd’hui 😅)
Par défaut, Symfony recherche un match entre la route de notre requête en cours, et toutes les routes définies. Dès qu’il trouve un match, il s’arrête sans aller voir plus loin.
Simple et efficace… mais parfois un peu pénible. Je m’explique. Il arrive souvent d’avoir des paramètres de type string dans une route, un slug par exemple
#[Route('/product/{slug:product}', name: 'show')]
public function show(Product $product): Response
(tu l’auras remarqué, j’en profite pour adopter la nouvelle syntaxe de route dont je parle un peu plus haut dans cette newsletter)
Si j’ai une autre route définie juste en dessous
#[Route('/product/new', name: 'new')]
public function add(): Response
elle ne sera jamais trouvée. En effet, /product/new
va matcher avec la première route, car Symfony va penser que “new” est le slug que je recherche (ce qui a de forte chance de provoquer une erreur par ailleurs s’il n’existe pas).
Pour palier ce problème, il faut faire en sorte de placer la définition de route la moins précise en dessous des autres. Une fois fait, /product/new
trouvera un match avant d’atteindre product/{slug:product}
Mais c’est pénible de devoir déplacer des méthodes entières dans un fichier. Et quid de définitions conflictuelles se trouvant dans des fichiers séparés… Dans ce cas Symfony commence par regarder les routes en classant les controleurs par ordre alphabétique… un casse tête.
Eh bien, crois moi ou non (mais bon c’est dans la doc donc tu peux me croire), il existe une option priority
pour l’attribut #[Route()]
afin de définir l’ordre d’interprétation de la route (et qui prend donc le dessus sur sa position dans les fichiers). Par défaut cette priorité est à zéro pour toutes les routes, et il est possible d’indiqué une valeur positive (pour rendre une route précise prioritaire sur les autres) ou négative (pour rendre une route moins prioritaire que les autres).
Ce qui nous donne par ex
#[Route('/product/{slug:product}', name: 'show', priority:-1)]
public function add(): Response {...}
#[Route('/product/new', name: 'new')] // default priority:0
public function add(): Response {...}
La route new
a beau être en seconde position, sa priorité de zéro est supérieure à celle de show
et matchera donc en premier !
Article utile
Je suis tombé sur cet article il y a quelques jours, qui parle de récursivité dans les tableaux PHP, en utlisant RecursiveIterator
.
Il fait étrangement écho à ma mission en cours, où j’avais exactement ce type de problématique métier. En faisant quelques recherches, j’ai bien découvert RecursiveIterator
,
envisagé de l’utiliser, mais abondonné car la doc officielle est vraiment pauvre et les exemples pas très nombreux. Ca restait obscur et j’ai opté pour une autre solution, fonctionnelle mais sans doute moins élégante.
Et seulement quelques jours après, je découvre cet article super bien expliqué, et depuis je dois dire que la refacto me démange 😏
Bref, je voulais te le partager : https://gbh.fruitbat.io/2024/06/04/php-doing-recursion-with-recursive-iteratoriterators
Conclusion
J’ai commencé cette newsletter par très inspiré, mais finalement je suis plutôt content, j’ai pu abordé pas mal de points différents et l’actualité fournie de la nouvelle version de Symfony aide bien :-)
Si cette newsletter t'a plu, n’hésite pas à repartager, en parler autour de toi. Et si tu as le moindre commentaire, écris moi à [email protected] ou contacte moi sur mon Linkedin.
À bientôt pour le prochain niveau ;-)