Des graphiques dans vos projets Symfony 2

Des graphiques dans vos projets Symfony2

Introduction

Lorsque vous travaillez sur des projets à forte volumétrie de données, les résultats sous forme de tableaux deviennent très rapidement inexploitables. Vous devez alors vous tourner vers des représentations graphiques de ceux-ci. En effet, il est bien plus simple de dégager une tendance d’après une courbe de progression plutôt qu’un tableau de mille lignes de résultats. Vous avez sans doute généralement recours à des librairies JavaScript pour ce travail pour obtenir des graphiques interactifs. Si comme moi vous êtes Développeur Symfony2 et que l’utilisation des librairies en question est pour vous une épreuve douloureuse (ou du moins fastidieuse), n’ayez plus d’inquiétude, des solutions existent !

Des graphiques sans écrire de JavaScript

Bien évidemment, il ne s’agit pas ici de s’affranchir du JS puisque vous perdriez alors toutes ces petites animations qui font la joie de vos utilisateurs. Nous allons plutôt déléguer la génération de celui-ci à une surcouche PHP sous la forme d’un Bundle. Dans le cas exposé ici, on utilisera l’excellent ObHighchartsBundle développé par Marc Aube. Comme son nom l’indique, il s’agit d’une surcouche pour l’intégration de la librairie JavaScript HighCharts. Celle-ci est payante, certes, mais uniquement dans le cadre de projets commerciaux. Les avantages de ce Bundle sont multiples :

  • Il vous permet de ne plus écrire une ligne de JS pour vos graphiques en les construisant uniquement depuis le côté PHP de votre application;
  • Il est extrêmement simple à mettre en place. Pour couronner le tout, son core développeur est très réactif lorsque vous avez des questions à lui poser ou que vous faites face à un quelconque problème.

À l’abordage ?

A) Installation et configuration
Assez parlé : il est maintenant temps de coder ! On va commencer par installer et configurer le Bundle. Pour l’installer, on passe par Composer en ajoutant dans notre composer.json la ligne suivante :

"require": {
       ...
       "ob/highcharts-bundle": "1.*"
       ...
   }

On télécharge le Bundle via la commande :

php composer update "ob/highcharts-bundle"

Puis évidemment on met à jour son AppKernel.php en enregistrant le nouveau Bundle :

<?php
   ...
   public function registerBundles()
   {
       $bundles = array(
           ...
           new ObHighchartsBundleObHighchartsBundle(),
           ...
       );
   ...

Et voilà ! J’avais parlé de configurer le Bundle ? Et bien il n’y a en réalité aucune configuration à réaliser. En effet, une fois le Bundle installé, il est prêt à être utilisé.

B) Importer les librairies JavaScript requises
Vous pouvez utiliser le Bundle avec jQuery, Mootools ou sans aucune des deux librairies. Dans notre cas, on va travailler avec jQuery mais les différences entre chaque implémentation sont détaillées dans la documentation du Bundlea sur le Repository Git de Marc Aube.

Les imports JavaScripts sont les suivants :

<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" >
</script>

<scripttype="text/javascript"
src="{{ asset('bundles/obhighcharts/js/highcharts/highcharts.js') }}">
</script>

<script type="text/javascript"
src="{{ asset('bundles/obhighcharts/js/highcharts/modules/exporting.js') }}">
</script>

Pour ceux d’entre vous qui n’ont jamais utilisé HighCharts, sachez que la librairie exporting.js permet d’ajouter un raccourci pour imprimer votre graphique ou pour l’exporter en JPEG, PNG, PDF ou encore SVG. Elle n’est donc pas nécessaire si ces fonctionnalités ne vous intéressent pas.

C) Des graphiques comme s’il en pleuvait
Nous allons ici réaliser trois graphiques :

  • Un line chart représentant les ventes en quantité au cours de la semaine du 21/06 au 27/06;
  • Un bar chart représentant les bénéfices générés au cours de la semaine du 21/06 au 27/06;
  • Un graphique dans lequel on aura les deux précédents ainsi que la répartition des bénéfices totaux par type de produit vendu

1. Line Chart
Côté Controller, nous allons manipuler un Object Highcart sur lequel on définira nos options. Quelque soit le graphique que vous voudrez générer, vous travaillerez avec cet Object et les options que vous lui appliquerez définiront le rendu final de votre graphique :

<?php
    use ObHighchartsBundleHighchartsHighchart;

    // ...
    public function sellsHistoryAction()
    {
        // Chart
        $sellsHistory = array(
            array(
                 "name" => "Total des ventes",
                 "data" => array(683, 756, 543, 1208, 617, 990, 1001)
            ),
                        array(
                 "name" => "Ventes en France",
                 "data" => array(467, 321, 56, 698, 134, 344, 452)
            ),

        );

        $dates = array(
            "21/06", "22/06", "23/06", "24/06", "25/06", "26/06", "27/06"
        );

        $ob = new Highchart();
        // ID de l'élement de DOM que vous utilisez comme conteneur
        $ob->chart->renderTo('linechart');
        $ob->title->text('Vente du 21/06/2013 au 27/06/2013');

        $ob->yAxis->title(array('text' => "Ventes (milliers d'unité)"));

        $ob->xAxis->title(array('text'  => "Date du jours"));
        $ob->xAxis->categories($dates);

        $ob->series($sellsHistory);

        return $this->render('PaloBundle:Chart:template.html.twig', array(
            'linechart' => $ob
        ));
    }

Côté vue rien de très compliqué. On importe les librairies JS comme vu précédemment, on initialise son graphique et on ajoute l’élément de DOM que l’on utilise comme conteneur :

<script type="text/javascript"&lgt;
    {{ chart(linechart) }}
</script>

<div id="linechart" style="min-width: 400px; height: 400px;"></div>

 

2. Bar Chart
Pour le bar chart on reprend la même base, la seule différence à vrai dire, est la définition du format qui se fait via l’option $ob->chart->type(‘column’);
On aura donc :

<?php
    use ObHighchartsBundleHighchartsHighchart;

    // ...
    public function profitsHistoryAction()
                $sellsHistory = array(
            array(
                 "name" => "Bénéfices total",
                 "data" => array(9.1, 10.3, 6.5, 12.2, 5.3, 9.1, 11.1)
            ),
            array(
                 "name" => "Bénéfices pour la France",
                 "data" => array(6.6, 8.2, 0.76, 4.6, 2.1, 4.1, 3.9)
            ),

        );

        $dates = array(
            "21/06", "22/06", "23/06", "24/06", "25/06", "26/06", "27/06"
        );

        $ob = new Highchart();
        // ID de l'élement de DOM que vous utilisez comme conteneur
        $ob->chart->renderTo('barchart');
        $ob->title->text('Bénéfices du 21/06/2013 au 27/06/2013');
        $ob->chart->type('column');

        $ob->yAxis->title(array('text' => "Bénéfices (millions d'euros)"));

        $ob->xAxis->title(array('text' => "Date du jours"));
        $ob->xAxis->categories($dates);

        $ob->series($sellsHistory);

        return $this->render('PaloBundle:Chart:template.html.twig', array(
            'barchart' => $ob
        ));
    }

 

barchart

3. Graphique unifié
Comme mentionné précédemment, on souhaite afficher dans ce graphique :

  • Les ventes totales effectuées;
  • Les ventes pour la France;
  • Les bénéfices totaux;
  • Les bénéfices pour la France;
  • La répartition des bénéfices totaux par modèle de produit vendu.

On a donc ici 5 séries de données qu’il nous faut envoyer à notre graphique. Pour chaque série, nous allons fournir la liste des données de la série, un nom de série et un type d’affichage, de manière à avoir deux axes avec des unités différentes. Nous allons déclarer ces axes sous la forme d’un tableau et dans notre liste de série, nous préciserons celles qui dépendent du deuxième axe. On aura alors :

<?php
    use ObHighchartsBundleHighchartsHighchart;

    // ...
    public function overviewChartAction()
    {
        $series = array(
            array(
                 "name" => "Bénéfices total",
                 "data" => array(9.1, 10.3, 6.5, 12.2, 5.2, 9.1, 11.1),
                 "type" => "column"
            ),
            array(
                 "name" => "Bénéfices pour la France",
                 "data" => array(6.6, 8.2, 0.76, 4.6, 2.1, 4.1, 3.9),
                 "type" => "column"
            ),
            array(
                 "name" => "Total des ventes",
                 "data" => array(683, 756, 543, 1208, 617, 990, 1001),
                 "type" => "spline",
                 "yAxis" => 1,
            ),
            array(
                 "name" => "Ventes en France",
                 "data" => array(467, 321, 56, 698, 134, 344, 452),
                 "type" => "spline",
                 "yAxis" => 1,
            ),
            array(
                "type" => "pie",
                "name" => "Pourcentage des ventes totales",
                "data" => array(
                        array('Guinness', 32.0),
                        array('Westvleteren', 26.8),
                        array('Alchemist Heady Topper', 13.0),
                        array('La Thou', 12.8),
                        array('Russian River Pliny the Elder', 8.5),
                        array('Founders KBS', 6.2),
                        array('Rochefort Trappistes 10', 0.7)
                    ),
                "center" => array(50, 50),
                "size" => 100,
                "showInLegend" => false,
                "dataLabels" => array(
                    "enabled" => false
                )
            )
        );

        $yData = array(
            array(
                'title' => array(
                    'text'  => "Bénéfices (millions d'euros)",
                    'style' => array('color' => '#AA4643')
                ),
                'opposite' => true,
            ),
            array(
                'title' => array(
                    'text'  => "Ventes (milliers d'unités)",
                    'style' => array('color' => '#4572A7')
                ),
            ),
        );

        $dates = array(
            "21/06", "22/06", "23/06", "24/06", "25/06", "26/06", "27/06"
        );

        $ob = new Highchart();
        // ID de l'élement de DOM que vous utilisez comme conteneur
        $ob->chart->renderTo('overviewchart');
        $ob->title->text('Ventes au cours de la semaine');

        $ob->yAxis->title(array('text'  => "Vente en milliers"));
        $ob->yAxis($yData);

        $ob->xAxis->title(array('text'  => "Date du jours"));
        $ob->xAxis->categories($dates);

        $ob->series($series);


        return $this->render('PaloBundle:Chart:template.html.twig',
            array(
                'overviewchart' => $ob
            ));
    }

Dans notre vue, rien ne change :

<script type="text/javascript">
    {{ chart(overviewchart) }}
</script>

<div id="overviewchart" style="min-width: 400px; height: 400px;"></div>

 

 

En conclusion
Comme nous venons de le voir, l’implémentation d’un graphique quelqu’il soit est assez simple avec ObHighchartsBundle. Cela est principalement dû au fait que Marc Aude a calqué son implémentation sur celle de la librairie JS. Il nous suffit ainsi de regarder le JSON fourni comme liste d’options dans la documentation de HighCharts pour savoir comment structurer notre array d’options en PHP. Le Bundle se passe de ce fait d’une documentation détaillée de l’implémentation puisque la documentation de la librairie JS est disponible sur le site d’HighCharts. Le fait de pouvoir déclarer nos graphiques en PHP au lieu de le faire en JS nous permet en plus de les valider via nos tests unitaires PHPUnit, alors… pourquoi se priver ?!

Références

Share
Quentin VIGNIER
Quentin VIGNIER

13830

Comments

  1. hi,
    Very nice example, thanks a lot.
    I have a question about how to define a js function. The “Use a Javascript anonymous function” (in the documentation).
    the $func = new ZendJsonExpr(“…”); doesn’t work.
    I’m wondering if you have any suggestion.

    • Je rejoins le premier commentaire,

      $func = new ZendJsonExpr…

      renvoie une erreur Symfony (Attempted to load class “Expr” from namespace…). Quel est le namespace à charger ?

      Merci pour ce bon tutoriel !

  2. Bonjour,

    Serait-il possible de mettre un Nom pour chaque Colonne (bleue et noire) ?

    Concrètement, dans l’exemple ci-dessus, Au lieu d’avoir une Date (par exemple 21/06) et 2 Colonnes, on puisse avoir deux Dates (une pour chaque colonnes) ?

    Merci.

  3. Quentin Vignier Quentin Vignier Says: August 1, 2014 at 11:08 am

    À ma connaissance cela n’est pas possible et à vrai dire cela ne serait pas logique.
    En effet on a deux colonnes par date parce que l’on veut comparer deux séries de données pour un même intervalle de temps avec un même pas.
    Cela veut dire que chaque colonne d”une couleur représente la même série de données, du coup on a une légende pour la série bleue, une pour la série noire.
    Si tu veux avoir une date par colonne il faut changer l’approche que l’on a ici.
    Dans mon exemple, mes séries bleues et noires ont des valeurs pour les mêmes dates:
    – série bleue: valeurs pour 21: 22, 23, 24, 25, 26
    – série noire: valeurs pour 21: 22, 23, 24, 25, 26
    Si tu veux avoir des dates différentes pour la colonne bleue et la colonne noire il faudrait par exemple avoir:
    – série bleue: valeurs pour 21: 23, 25
    – série noire: valeurs pour 22, 24, 26
    En faisant cela on sort partiellement de la logique présentée ici qui est une logique de comparaison de données dans un intervalle et avec une périodicité similaire.

  4. bonjour,

    est-il possible de faire apparaître des pourcentages ou les valeurs sur le graphique ?

    J’ai fais un pie chart, mais les valeurs n’apparaissent qu’au survol de la souris…je désirerai quelles apparaissent de manière fixe sur le graphique, ou sur la légende associée.

    Merci

  5. Bonjour,
    j’aimerai savoir comment mettre en relation les graphiques colonne avec les données d’une requête.
    parce que j’ai générer une requête dans un repository mais cela ne semble pas correspondre et le graphique n’apparaît même pas.
    Merci

  6. Bonjour,
    Execllent tuto et merci pour cette découverte.
    Pour le premier et deuxieme commentaire, j’ai eu la même erreur, et creusant un peu j’ai vu qu’il y a la dépendance zend-json qui manquait,du coup j’ai ajouté ligne suivante dans fichier composer.json comme suit “zendframework/zend-json”: “2.3.0”.

  7. Bonjour,
    Je m’associe aux autres internautes pour la qualité du tuto.
    Depuis plusieurs jours, je recherche des informations sur comment alimenter dynamiquement mes graphiques à partir de données Mysql sur Framework Symfony2.
    Merci d’avance.

  8. Bonjour,
    Très bon tuto. Mais depuis plus d’une semaine je cherche comment faire afficher la courbe pour les éléments dont le compte est à zéro.
    Par exemple si j’ai :
    A=5
    B=10
    C=0
    D=12,
    Quand je fais ma requêtte l’élément C apparaît mais prend la valeur de l’élément D.
    Merci

  9. Mickael Lutin Says: March 10, 2017 at 2:57 pm

    bonjour, votre code fonctionne toujours? parce que j’arrive vraiment pas a l’utiliser

  10. Merci Beaucoup , ça marche très bien

  11. merci beaucoup

Leave a Reply

Your email address will not be published. Required fields are marked *