Framework pour les exports
J'ai un peu réfléchi à un framework pour les exports du logiciel. Mon objectif: essayez d'abstraire les exports en découpant chaque étape, de manière à pouvoir réutiliser le code au maximum.
Comme le sujet est assez technique, j'utilise le français.
Fonctionnalités demandées
Résultat attendu
Une des fonctionnalités les plus demandées est de pouvoir obtenir ce genre de tableau :
Filtre par :
|
||||
---|---|---|---|---|
Nationalité | Genre | Nombre d'activités effectuées | ||
Janvier 2015 | Février 2015 | Mars 2015 | ||
Belge | Homme | 15 | 12 | 9 |
Belge | Femme | 25 | 22 | 19 |
Français | Homme | 15 | 12 | 9 |
Français | Femme | 25 | 22 | 19 |
Parcours utilisateur
L'utilisateur clique sur le menu "export" en haut à droite.
Il arrive sur une page qui liste tous les types d'exports possibles, avec un petit mot d'explication sur chaque export. Pour l'export précédent, il choisit un export nommé "Nombre d'activités".
Il arrive sur une page qui contient trois parties :
- les filtres ;
- les agrégateurs ;
- le format d'export.
Dans la première partie, il voit une liste les filtres. Un filtre est un texte + éventuellement un formulaire. Certains concernent les activités, d'autres les personnes. Pour choisir les filtres, il remplit le formulaire qui y est associé, et coche une case pour l'activer (Alternative: faire un glisser-déposer vers une autre zone de la page). Exemples de filtre :
- filtrer par date d'activité, avec un (choisi ici)
- filtrer par catégories et sujets d'activité (on peut cliquer sur les catégories et les sujets)
- filtrer par nationalité (on peut cliquer sur les nationalités) (choisi ici) ;
- filtrer par sexe ;
- ...
Dans la deuxième partie, l'utilisateur choisit des agrégateurs.
Exemple d'agrégateur :
- grouper par Genre ;
- grouper par Nationalité ;
- grouper par date (avec un formulaire où on peut choisir "par mois", "par semaine", "par jour", "par trimestre", "par année")
- grouper par catégorie d'activités
- grouper par types d'activités
- ...
Cette partie de page est divisée en deux:
- partie de gauche, la liste des agrégateurs ;
- partie de droite, vers le haut les agrégateurs que l'utilisateur a sélectionné par drag and drop (ça serait top) et doivent figurer dans les en-têtes de colonne (ici, Nationalité et Genre)
- partie de droite, vers le bas, les agrégateurs que l'utilisateur a sélectionné par drag and drop (ça serait top) et doivent figurer dans les en-têtes de ligne (ici, par mois)
Il fait un glisser déposer et les mets dans la partie de droite. Il peut également les ordonner. Il peut également remplir un formulaire avec les éventuelles précisions.
Dans la troisième partie, l'utilisateur peut choisir entre les différents formats d'export :
- CSV
- xslx
- graphiques (à venir)
Le code derrière
Je propose de découper le code en quatre type d'objets :
- les exports ;
- les filtres ;
- les agrégateurs ;
- les formatteurs (pas trouvé de nom français plus joli, on s'en contentera)
Toutes ces classes sont déclarées comme des services, avec un tag qui permet à l'injecteur de dépendance de les identifier.
Les formatteurs
Ils reçoivent les données des exports et les mettent en forme. Il y en aura peu, à priori un pour le CSV, un pour le XLSX et un pour les graphiques.
Les exports
Son boulot : rassembler tous les filtres, les agrégateurs et récupérer les données depuis la DB, puis les mettre sous une forme définie (un tableau associatif ?) qui précise quels sont les en-têtes de tableau, les en-têtes de ligne, etc. pour qu'elles puissent être mise en forme par le formatteur.
Le formatteur peut éventuellement être configuré par un formulaire (à voir si c'est nécessaire).
Techniquement, il s'agirait tout simple pour l'exporteur de préparer une requête "de base" avec le QueryBuilder de doctrine. La requête serait passée aux filtres et aux agrégateurs qui y ajouteraient leurs paramètres. Puis elle serait exécutée et les résultats transmis au formatteur.
Les exports indiquent quel type de filtre et d'agrégateur ils supportent. Par exemple, dans l'exemple ci-dessus, ils supportent uniquement les filtres et les agrégateurs de type person
et activity
.
Les filtres
Les filtres fournissent :
- éventuellement un formulaire, qui est affichée dans l'UI, pour configurer le filtre. Par exemple, pour le choix des dates, il peut s'agir d'un formulaire qui présente la date de début et de fin de la période. Pour le filtre par nationalité, un select avec l'option multiple qui présente toutes les nationalités, etc.
- ajoutent leurs paramètres à la requête de l'export. Il s'agirait, pour eux, d'ajouter des paramètres à la clause
WHERE
. Ici, j'ai ajouté deux filtres qui ajouteraient les clauses suivantes :WHERE person.nationality in (BELGIUM, FRANCE)
etWHERE activity.date BETWEEN 2015-01-01 AND 2015-03-31
.
Chaque filtre indiquerait pour quel objet il peut être appelé. Donc un filtre qui peut être appelé pour l'objet activity
sera ajouté au formulaire des exports qui concernent les activités, les filtres pour les personnes (filtre par nationalité, etc.) sera disponible pour les exports qui concernent les activités et les personnes, etc.
Les agrégateurs
Le boulot des agrégateur est de regrouper les données en fonction de certains paramètres.
- ils peuvent également fournir un formulaire, affiché dans l'UI, pour régler ces paramètres. Par exemple, un agrégateur par date pourrait demander aux utilisateurs de choisir entre une agrégation par mois, par semaine, par jour de la semaine, etc.
- ils ajouteraient des clauses
GROUP BY
aux requêtes SQL. Dans l'exemple ci-dessus, il pourrait s'agir d'un premier agrégateurGROUP BY person.nationality
puis un autre :GROUP BY person.gender
(+ le fait d'ajouterperson.nationality
etperson.gender
dans la clauseSELECT
).
Note : on aura peut-être un problème quand un utilisateur choisi un filtre qui concerne également les agrégations: dans ce cas là il faut utiliser HAVING plutôt que WHERE... (http://www.w3schools.com/sql/sql_having.asp).
Un contrôleur pour les exports
Pour orchestrer tout ce petit monde, un contrôleur, ou un autre service, serait informé de tous les agrégateurs, filtres et export possibles par l'injecteur de dépendance.
Il afficherait la liste des exports possibles, et, quand on sélectionnerait un export, il créerait un formulaire qui contiendrait tous les sous-formulaires des agrégateurs, filtres et exports.
Quand un utilisateur demanderait un export, il recevrait les données des formulaires, sélectionnairait les exports que l'utilisateur a demandé, en extrairait les données qu'il enverrait à l'export sélectionné (il créerait la requête DQL), puis aux filtres et agrégateur (ils modifieraient la requêtes DQL en ajoutant les paramètres WHERE et GROUP BY).
La ré-utilisation
Evidemment, à chaque fois, le code des agrégateurs et filtres pourrait être ré-utilisés dans les autres bundles. Autrement dit, si on a un filtre par nationalité défini dans la bundle "person", ce filtre est réutilisable par le bundle "activity" pour les exports propres au bundle "activity".
Qu'en pensez-vous ?
Si ça vous semble ok, je peux avancer :
- définir les interfaces et les formats d'échange ;
- faire un premier POC