Des graphiques dans Symfony avec Twig et SVG

Pour afficher des graphiques et diagrammes dans nos applications Symfony, il existe une large offre de solutions en Javascript qui se chargent du rendu coté client.

Mais je vous propose ici une alternative low-tech, coté serveur, qui fait appel à un standard depuis longtemps éprouvé : SVG.

Abricot : 38% Melon : 18% Pêche : 6.5% Figue : 14% Prune : 23.5%

Qu'est-ce que SVG ?

SVG, pour Scalable Vector Graphics, c'est un format d'image un peu particulier puisque son contenu est décrit non pas par une grille de pixels mais par du texte et plus précisément un arbre XML.

Une image SVG se compose d'une zone de travail (viewBox) dont les dimensions sont absolues (pas d'unité) dans lesquels sont placées des formes géométriques plus ou moins complexes : rectangle, cercles, lignes, courbes, etc.

Chaque forme est décrite par un noeud XML qui porte des informations de position, de taille, de couleur, de contour, d'épaisseur de trait, etc.

Un exemple ?

<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <rect x="10" y="25" width="60" height="60" fill="red" /> <circle cx="55" cy="40" r="32" stroke="blue" fill="none" stroke-width="10" /> </svg>

Le code ci dessus décris l'image suivante :

Ses composants géométriques font du SVG un format particulièrement adapté à la réalisation de graphiques.

Twig + SVG = <3

Coté serveur, nous travaillons ici avec Symfony. Et le format SVG nous intéresse donc, puisque Twig est conçu pour générer des balises au format XML !

Le moteur de template de Symfony se marie donc parfaitement avec SVG :

{# consommation.svg.twig #} <svg viewBox="0 0 200 20" xmlns="http://www.w3.org/2000/svg"> {% for value in data %} <rect x="{{ loop.index }}" y="0" width="5" height="{{ value }}" /> {% endfor %} </svg>

Nous allons bénéficier de toute la puissance de Twig pour générer nos diagrammes :

  • Nos données métier dans des variables;
  • Des boucles, des conditions et des macros;
  • La traduction ! On peut par exemple fournir un graphique dont la légende s'adapte à la langue demandée dans la requête.

Servir une image SVG avec Symfony

Une fois notre image SVG générée, il va falloir l'afficher.

Pour cela il nous suffit renvoyer une réponse de type image/svg+xml à travers une route dédiée :

<?php # GraphController.php use Symfony\Component\HttpFoundation\Response; /** * @Route("/consommation.svg", name="consommation") */ public function consommation() { return $this->render( 'graph/consommation.svg.twig', [ 'data' => $user->getConsommation() ], new Response('', 200, ['Content-Type' => 'image/svg+xml']) ); }

Pour l'utiliser ensuite dans une balise image via son url :

<img src="{{ path('consommation') }}" />

Ou dans tout autre contexte utilisant une image servie via HTTP :

background-image: url('/consommation.svg');

💡 On peut aussi déclarer le format svg dans Symfony pour ajouter automatiquement le header Content-Type adapté à toutes nos routes SVG:

# config/packages/framework.yaml framework: request: formats: svg: 'image/svg+xml' # GraphController.php /** * @Route("/pie.svg", name="pie", defaults={"_format":"svg"}) */

Intégrer une image SVG dans une page

HTML supporte également un noeud SVG dans le DOM de la page. ✌️

On peut donc intéger notre graphique directement dans un template Twig HTML :

{# dashboard.html.twig #} {% block body %} <div> <h3>Mon activité</h3> {% include 'consommation.svg.twig' with { data: user.consommation } %} </div> {% endblock %}

Dans ce cas là, chacun des éléments géométrique de notre graphique est un élément DOM à part entière de la page.

Cela va notamment nous permettre d'interagir avec le graphique au survol ou au clic (pour afficher une légende par exemple).

Voila ce que ça peut donner :

127€JANV.114€FÉVR.98€MARS112€AVR.135€MAI156€JUIN222€JUIL.198€AOÛT116€SEPT.96€OCT.92€NOV.108€DÉC.

Définir le style d'un graphique SVG

Comme pour n'importe quel élément DOM, les propriétés de style d'un élément SVG (remplissage, bordure, couleur, etc.) peuvent être définis dans une feuille de style CSS.

Cette feuille de style peut être :

  • Interne : dans une balise <style> à l'intérieur de la balise <svg> (obligatoire dans le cas d'une utilisation sous forme d'image).
  • Externe : dans la feuille de style globale de votre site (dans le cas du SVG injecté directement dans le DOM HTML).

Nous pouvons cibler les éléments de notre graphique à l'aide du nom des éléments (svg, rect, ...), des attributs class et id, ou de tout autre sélecteur CSS. Comme avec n'importe quel élément HTML finalement.

Nous utiliserons par contre les propriété natives des éléments SVG comme fill et stroke-width directement dans le CSS, en lieu et place des habituels background-color et autres border :

/* style.css */ svg.histogram .bar { fill: #B5838D; } svg.histogram .bar:hover { fill: #b16575; } svg.histogram .axe { stroke: #6D6875; stroke-width: 0.6; }

💡 Note : oui les animations et transitions CSS sont très bien supportées sur les propriétés SVG, soyons créatifs ! 😏

Bilan

Quels-sont les avantages de l'utilisation de SVG coté serveur pour générer nos graphiques ?

  • Poids modeste : un graphique au format SVG est généralement assez léger, surtout face à son équivalent en bitmap.
  • Mise en cache : un graphique généré par le serveur peut être mis en cache et servi à tous les utilisateurs (contrairement à un rendu coté client sur chaque navigateur).
  • Sur mesure : faire ses diagrammes soi-même permet de coller exactement à la charte de son produit plutôt que de subir l'identité visuelle d'une librairie tierce.
  • Haute définition : le SVG étant vectoriel, les graphiques SVG seront rendu en haute définition sur tous les supports, que ce soit sur des écrans haute densité ou lors de l'impression.
  • Low tech : le SVG est une bonne vielle techno™️, supporté par les plus vieux navigateurs et bien documentée. J'ai même fait tourner mes graphiques sur une liseuse 😊

Je ne suis d'ailleurs pas le seul à penser que c'est une bonne idée, certains des diagrammes que nous côtoyons quotidiennement sont générés en SVG :

Github graph activity Le graph d'activité de Github Symfony profiler Le profiler de Symfony

À vous de jouer !

Le combo SVG + Twig ne répondra pas à tous vos besoin de data visualisation, notamment pour les plus interactifs.

Mais il constitue une solution simple et solide pour des dashboards, diagrammes et autres rapports, dans vos projets Symfony. Je vous recommande de l'envisager pour votre prochain besoin de ce genre !

Enfin, si vous décidez de vous lancer, j'ai préparé un petit exemple concret et fonctionnel avec quelques diagrammes classiques, qui peut servir de référence.