Symfony Level Up #8
La newsletter pour performer en Symfony 🚀
Symfony 7.3 arrive !
Salut collègue développeur-euse,
Après quelques mois de pause, la sortie imminente de Symfony 7.3 me décide enfin à écrire le niveau 8 de cette newsletter.
Nous voici avec plus de 385 inscrites et inscrits à ce jour ! Merci encore pour ton soutien 🙏
Si tu veux m'aider à faire décoller encore 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é(e) et que tu ne veux pas rater les prochaines éditions, clique sans plus tarder sur le bouton ci-dessous.
Quoi de neuf pour Symfony 7.3
Plus que quelques jours avant la sortie de cette nouvelle version de Symfony qui s’avère extrêmement riche ! Côté blog de Symfony, comme d’habitude, nous trouvons de très nombreuses informations, distillées depuis quelques semaines à raison d’une news par jour environ. Je ne vais pas lister ici toutes ces nouveautés, mais je vais m’attarder sur celles qui m’ont le plus marqué :
JsonPath
Ce nouveau composant permet d’utiliser directement dans Symfony, les requêtes de type JsonPath, correspondant à la RFC 9535 (Query Expressions for JSON). Plus besoin de passer par des librairies externes pour cela. J’ai eu à utiliser JsonPath il y a quelques mois sur un projet pro, et si j’avais eu ce composant en natif dans Symfony à l’époque, j’aurais gagné pas mal de temps… Mais c’est maintenant chose faite :-)
Pour l’installer :
composer require symfony/json-pathSi tu ne connais pas JsonPath, c’est un langage de requêtes puissant et flexible qui va te permettre de manipuler des données dans des JSON complexes. Sa syntaxe est similaire à celle de XPath pour XML. JsonPath permet de sélectionner des éléments spécifiques dans un JSON, de parcourir des structures imbriquées de manière récursive, et d'appliquer des filtres pour extraire uniquement les informations pertinentes.
Un petit exemple simple, soit je JSON :
{
"clients": [
{
"nom": "Jean Dupont",
"adresse": "123 Rue de la Paix 75000 Paris",
"commandes": [
{
"date": "2023-10-01",
"adresse": "456 Avenue des Champs 69000 Lyon"
},
{
"date": "2024-02-15",
"adresse": "789 Boulevard de la Liberté 13000 Marseille"
}
]
},
{
"nom": "Marie Martin",
"adresse": "321 Rue de la République 33000 Bordeaux",
"commandes": [
{
"date": "2025-05-05",
"adresse": "654 Avenue de la Gare 31000 Toulouse"
}
]
}
]
}dont on veut extraire toutes les adresses (à différents niveaux dans le JSON), il suffit de faire
// initialisation du composant
$crawler = new JsonCrawler($json);
// requête JsonPath pour récupérer les adresses
$locations = $crawler->find('$clients..adresse');
dd($locations);
/**array:5 [
0 => "123 Rue de la Paix 75000 Paris"
1 => "456 Avenue des Champs 69000 Lyon"
2 => "789 Boulevard de la Liberté 13000 Marseille"
3 => "321 Rue de la République 33000 Bordeaux"
4 => "654 Avenue de la Gare 31000 Toulouse"
]*/l’objet JsonCrawler prend le JSON en paramètre, tandis que la méthode find(string $jsonPath) attend une chaîne de caractère au format JSONPath. Ici la requête $clients..adresse signifie :
$: je pars de la racine (attention, rien à voir avec le $ des variables PHP)clients: je me place sur le champ “clients”..: je cherche de manière récursiveadresse: tous les champs “adresse”
La syntaxe JSONPath permet de faire bien d’autres choses avec des filtres puissants (par ex récupérer toutes les commandes de 2024 )
$locations = $crawler->find($.clients[*].commandes[?(@.date >= '2024-01-01' && @.date <= '2024-12-31')])Je te laisse chercher plus d’informations sur JsonPath pour découvrir toute la puissance de cette syntaxe.
Tu trouveras également plus de détails sur le composant Symfony dans la documentation.
Object Mapper
Ce nouveau composant permet de mapper un objet vers un autre objet. C’est une tâche qui arrive souvent lorsque l’on manipule des DTOs et qui est assez peu intéressante à coder. L’idée du composant ici est de simplifier au maximum ce process.
Prenons l’exemple d’une requête API qui nous renvoie ce JSON
{
"nom": "Dupont",
"prenom": Jean"
"adresse": "123 Rue de la Paix 75000 Paris",
"pays": "France",
"ddn": "22/02/2000"
}et le DTO
class ClientApiDto
{
public string $nom;
public string $prenom;
public string $adresse;
public string $pays;
public DateTimeInterface $birthDate;
}que tu souhaites enregistrer en base via ton entité métier
class Client
{
public string $lastname;
public string $firstname;
public string $address;
public string $country;
public bool $isAdult;
}Il faut ensuite indiquer le mapping entre les deux classes et les champs (s’ils n’ont pas le même nom, ou si tu as besoin d’effectuer des transformations)
#[Map(target: Client::class)]
class ClientApiDto
{
#[Map(target: 'name')]
public string $nom;
#[Map(target: 'firstname')]
public string $prenom;
#[Map(target: 'address')]
public string $adresse;
#[Map(target: 'country')]
public string $pays;
#[Map(target: 'birthDate', transform: 'checkIfAdult')]
public bool $isAdult;
}Puis une fois tout configuré, il suffit de faire
$client = $mapper->map($clientApiDto, Client::class);Comme tu le vois, l’attribut #[Map] permet de mapper les classes et les propriétés via l’argument target: depuis la classe source, vers la classe cible (mais tu peux aussi utiliser source: si tu préfères plutôt mapper depuis la classe cible).
Ensuite, il est possible d’ajouter à l’attribut Map des arguments if: (pour conditionner un mapping) ou transform: (pour appliquer une transformation lors du transfert des valeurs). Dans l’exemple, on applique la fonction checkIfAdult() sur les dates de naissance et un booléen est renvoyé si le client a plus de 18 ans.
Si tu souhaites faire une transformation plus complexe (par ex vérifier le pays également pour calculer si la majorité est à 18 ou 21 ans), tu peux déporter cette logique dans une classe dédiée qui devra implémenter l’interface TransformCallableInterface
Au final, il ne reste plus qu’à appeler la méthode map() du composant et c’est fini ! Ce composant devrait donc avoir un impact conséquent sur de très nombreux projets utilisant des DTOs. J’ai hâte pour ma part de pouvoir l’utiliser dans le cadre pro.
Pour plus d’exemples, je te renvoie à la documentation officielle.
J’ai également réalisé une courte vidéà pour illustrer ce composant :
Security
Du côté du composant Security, tu connais sans doute la fonction isGranted('ROLE_XXX') qui permet savoir si le user actuellement authentifié a le droit d’accès car il possède bien le ROLE_XXX.
Symfony 7.3 apporte une nouvelle méthode isGrantedForUser($user, $role) qui permet de tester le rôle pour un autre user (que l’utilisateur en cours). Cela peut être très pratique pour afficher d’autres utilisateurs en fonction de leur droits respectifs.
La fonction is_granted_for_user(user, role) apparaît également dans Twig au côté de is_granted().
Pour plus d’information, voici le billet de blog correspondant.
Console
Côté console, plusieurs nouveautés sont à dénombrer également. La syntaxe pour la création de commande se voit simplifiée par l’ajout de nouveaux attributs.
D’autre part, il sera possible d’afficher dans la console, des données sous forme d’arbre (avec même plusieurs styles d’affichage), très pratique par exemple pour afficher une arborescence de fichiers.
Un autre helper permettra d’afficher les données sous la forme d’un tableau, en utilisant la syntaxe Markdown.
Nouveautés de Symfony UX
Pas mal de nouveautés également côté Symfony UX ces derniers temps. Le principal ajout reste le nouveau composant toolkit (débuté par Halleck45 et finalisé par Kocal), mais également des améliorations sur le composant Map.
Ux-toolkit : un réel game changer
Le nouveau composant Ux-toolkit (qui sera très bientôt disponible) est une petite révolution dans le monde de Symfony UX. Peut-on d’ailleurs vraiment parler de composant ici ? Non, Ux-toolkit est davantage un outil permettant de télécharger des squelettes de nombreux composants (bouton, tableau, card…), donc du HTML et du CSS pré-établi, mais facilement personnalisable. Il a pour ambition de t’aider à construire rapidement et efficacement ton design system de composants. Il s’inspire fortement de shadcn dans le style et le fonctionnement.
Pour le moment, un seul “kit” de composants est proposé, qui utilise le style de shadcn et Tailwind par défaut coté style, mais Ux-toolkit permettra de télécharger d’autres kits avec d’autres styles graphiques, proposés par la communauté.
Si tu as l’habitude de l’approche par composant, tu as peut-être déjà créé tes propres librairies sur tes projets. Mais c’est long et répétitif. Avec Ux-toolkit, tu vas pouvoir gagner un temps fou. Prenons un exemple pour que tu comprennes un peu mieux le fonctionnement. Tu souhaites créer un composant bouton, avec plusieurs variantes (par ex la couleur, l’outline, la taille).
Il suffit de lancer la commande :
symfony console ux:toolkit:install-component ButtonEt un fichier Button.html.twig se téléchargera dans ton dossier components, prêt à l’emploi, accessible, pré-stylisé avec Tailwind, et comprenant déjà un certain nombre de variantes (en s’appuyant sur le mécanisme des cva).
{%- props variant = 'default', outline = false, size = 'default', as = 'button' -%}
{%- set style = html_cva(
base: 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
) -%}
<{{ as }}
class="{{ style.apply({variant, outline, size}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</{{ as }}>Une fois le composant téléchargé, il te suffit de modifier le fichier twig (qui est une base de travail) pour l’adapter à tes besoins et personnaliser son style à ton projet.
Pour utiliser ton bouton, tu n’as plus qu’à écrire dans ton Twig
<twig:Button>Mon button</twig:Button> Le site web de Symfony UX arborera pour l’occasion une toute nouvelle page, qui listera tous les composants disponibles (et la liste s’allongera avec le temps). De plus, une page dédiée à chaque squelette présentera les spécificités de chacun et permettra même une prévisualisation directe, super pratique et efficace !
UxMap :
Dans les autres nouveautés Symfony UX, je me permets de mettre en avant quelques nouvelles contributions de ma part. Suite à ma dernière newsletter où je faisait un retour d’expérience sur ma toute première contribution Symfony, je pense que j’ai pris goût :) et j’ai proposé quelque nouvelles améliorations (un grand merci à Simon et Kocal pour les reviews).
possibilité d’ajouter/supprimer des éléments de la carte (marqueurs, polygones, polylines). Cet ajout va permettre notamment de modifier dynamiquement des infos sur une Map dans un contexte de Live component.
possibilité d’ajouter des icônes à la place des marqueurs de base (ces icônes pouvant être des images, des svg inline ou des icônes basées sur ux-icons !)
exemple de code pour afficher l’icône mdi:store-marker d’Ux-icon :
->addMarker(new Marker(
position: new Point(46.7534031, 4.8295061),
title: 'Ma boutique',
icon: Icon::ux('mdi:store-marker')->width(24)->height(24)
))Pour une présentation plus détaillée de cette fonctionnalité, je vous propose également cette courte vidéo
Partenariat
Pour cette édition, je te propose un lien d’affiliation pour l’excellent site Code-Garage du non moins excellent Nicolas Brondin-Bernard.
Si tu ne connais pas encore Code-Garage, c’est un site proposant de nombreux cours de qualité autour du développement.
Avec ce lien et le code SYMFONYLEVELUP, tu bénéficieras de 10% de réduction sur les cours payants et l’abonnement Pro !
Conclusion
J’espère que cette nouvelle newsletter, bien qu’elle ait un peu tardé, t’aura plu. Si c’est le cas, n’hésite pas à la repartager et à en parler autour de toi. J’ai pour ma part très hâte de tester toutes ces nouveautés de Symfony !
Et si ton entreprise cherche à booster son équipe avec un freelance expérimenté, n’hésite pas à me contacter sur mon Linkedin ou sur levelup@sylvainblondeau.dev.
À très bientôt pour le prochain niveau ;-)






Bonjour, Sylvain. Merci pour votre super travail.
Je pense que si #[Map(target: Client::class)] est utilisé, alors la classe est ciblé par défaut. Pas besoin de préciser la classe...
$client = $mapper->map($clientApiDto);