Retour au blog

EasyAdmin : un admin bundle simple et efficace pour Symfony

Qu’est-ce qu’un “admin bundle” et en ai-je besoin ?

Un admin bundle (ou module d’administration en bon français) est un ensemble d’outils permettant de créer des interfaces d’administration plus ou moins avancées, et ce, sans réinventer la roue.

Tout ceux qui ont déjà eu à développer des back-offices customs savent à quel point ils peuvent être pénibles à faire évoluer et à maintenir dans le temps. Alors pourquoi s’embêter quand un admin bundle peut facilement faire 70% du travail et nous permettre de se concentrer sur les features spécifiques des clients ?

Les bundles d’administration conçus pour Symfony

Côté Symfony, il existe 2 bundles d’administration réputés dont les documentations sont officiellement référencées : EasyAdmin et Sonata. Lequel choisir et dans quelles circonstances ?

Il faut noter que les deux bundles, bien qu’ayant la même mission, n’ont pas les mêmes cibles.

  • Sonata est un bundle très riche en fonctionnalités. Ces dernières ne sont néanmoins pas toutes documentées. De ce fait, l’outil reste assez complexe lorsque l’on débute.
  • EasyAdmin est, quant à lui, un peu plus limité en features. Il est cependant très simple d’utilisation et possède une documentation très complète.

Il existe un bundle complémentaire permettant d’augmenter le nombre de fonctionnalités d’EasyAdmin. Nous le verrons un peu plus tard dans cet article.

Pour récapituler : EasyAdmin cible principalement les applications simples et de petite ou moyenne envergure (un site de news, un blog, des petits sites marchands, etc.).

Il est préférable d’utiliser Sonata si votre application comporte d'innombrables règles de gestion et de sécurité (un domaine qu’EasyAdmin ne maîtrise pas encore).

Que propose EasyAdmin ?

  • Le bundle EasyAdmin supporte Symfony 2.x, 3.x et 4.x.
  • Il propose de réaliser des opérations SCRUD (Search/Create/Read/Update/Delete) très facilement sur nos entités Doctrine (ORM seulement, ODM non supporté).
  • La configuration des entités se fait via des fichiers yaml où il est possible déterminer à quoi ressembleront les 5 vues/actions disponibles : Search, List, Show, Edit et New. Le but de l’article n’étant pas de répéter ce qui est dit dans la documentation du bundle, je vous invite à y faire un tour pour voir à quoi ressemblent ces “vues”.
  • Le mode “listing” propose un champ de recherche full-text, une pagination et la possibilité de trier les colonnes par défaut.
  • Le bundle est traduit dans des dizaines de langues différentes, ce qui représente une grosse valeur ajoutée si vous avez des besoin d’internationalisation.
  • EasyAdmin est rapide et performant dans ses tâches, permettant une navigation fluide.

Quelques conseils pour enrichir EasyAdmin

Contrôle d’accès

Comme expliqué plus haut, en dehors des fonctionnalités SCRUD du bundle, ce dernier se retrouve assez limité quand on aborde le sujet de “vues sécurisées”. En effet, il n’est pas possible de configurer une entité pour qu’elle ne soit utilisée que par tel ou tel rôle.

Cependant, grâce au côté Open Source du projet, il possible de combler ces lacunes soi-même ou, encore mieux, au travers d’un bundle complémentaire : EasyAdminExtension.

Ce bundle vise à apporter quelques fonctionnalités supplémentaires, dont un contrôle d’accès basé sur les rôles. Grâce à cela, il est possible de dire : “l’administration des produits ne doit être accessible que des Product Manager”. Ce qui se concrétise de cette manière :

easy_admin:
   entities:
       Product:
           class: App\Entity\Product
           role_prefix: ROLE_PRODUCT_MANAGER

Ainsi, toutes les vues liées à l’entité Product seront bloquées si l’utilisateur n’a pas le rôle ROLE_PRODUCT_MANAGER. En revanche le lien est toujours affiché dans le menu. Ceci n’est pas forcément toujours utile, voire encombrant pour les autres rôles. Un petit bout de configuration pour régler ceci :

easy_admin:
       menu:
           - { entity: 'Product', role: ROLE_PRODUCT_MANAGER }

Voilà, le lien n’est visible que par les personnes autorisées ! Il faut toutefois noter que seule l’entrée dans le menu est cachée. Ceci n’empêche pas d’accéder à la page par URL. C’est pour cela qu’il faut bien préciser le rôle sur la configuration de l’entité et pas uniquement dans celle du menu.

Le scénario que je viens de présenter est relativement simple et conviendra dans la plupart des cas. Il est cependant possible de pousser le concept un peu plus loin grâce à la mise en place d’une hiérarchie de rôles.

Si je reprends mon exemple, le Product Manager ne peut que manager l’entité Product. Dorénavant, il sera également en charge des Stocks et du Shipping.

Voici comment configurer le fichier security.yaml en conséquence :

security:
   role_hierarchy:
       ROLE_SUPER_MANAGER: [ROLE_PRODUCT_MANAGER, 
ROLE_STOCK_MANAGER, ROLE_SHIPPING_MANAGER]

On pourra affecter ce nouveau rôle aux Product Manager qui hériteront automatiquement des responsabilités des stocks et des envois.

Il est également possible de dire que le rôle de Product Manager hérite des deux autres rôles et ainsi de se passer du rôle Super Manager. Cependant, pour de potentielles futures évolutions, il est préférable de garder les rôles liés à une entité le plus simple possible. En faisant ainsi, on peut garder une configuration des rôles simple du côté de nos fichiers EasyAdmin.

Auto-complétion bloquée ?

Il est possible que vous ayez besoin de proposer un champ de type “autocomplete” basé sur l’une de vos entité à vos utilisateurs. Pour cela, vous avez utilisé le type “EasyAdminAutocompleteType”. Celui-ci permet très facilement de proposer des résultats basés sur la classe que vous avez spécifié dans l’attribut “class” du field.

Un comportement gênant peut être engendré si vous avez paramétré un rôle sur la vue “list” de l’entité en question. Si l’on tente d’utiliser le champ avec un utilisateur au rôle inférieur, aucun résultat ne sera retourné.

bundle symfony easy admin

L’origine du problème vient du fait que l’on ne peut pas différencier les configurations des actions “list” et “autocomplete” quand on spécifie un rôle (plus de détails dans cette issue github).

Pour résoudre ce problème, il existe deux options :

  • Ajouter le rôle de l’utilisateur en question aux rôles hérités par le rôle configuré sur l’action.
  • Modifier le rôle configuré sur l’action “list” afin qu’elle soit “accessible” au rôle inférieur, mais cacher l’affichage dans le menu (cf. le point sur le contrôle d’accès).

Améliorer les listings

Les listings acceptent une configuration de filtres DQL. Ceci permet de limiter les données affichées par défaut. On peut, par exemple, vouloir un listing des produits dont les stocks sont quasiment écoulés :

list:
   dql_filter: 'entity.stock < 50'

Et voilà, seulement les produits dont le stock est inférieur à 50 seront affichés dans la liste. A noter que par défaut, le DQL paramétré dans l’action “list” s’applique également à l’action “search”. Il est néanmoins possible de définir un autre filtre DQL sur cette dernière afin d’avoir deux comportements différents, si besoin.

Il est également possible de rendre le listing d’une entité plus dynamique grâce aux filtres apportés par le bundle EasyAdminExtension. On peut paramétrer des filtres de plusieurs types : select simple/multiple, booléen, chaîne de caractères ou numérique et même des champs en autocomplete. Les relations many-to-many ne sont toutefois pas supportées…

easy admin symfony interface

Vous l’aurez compris, ce bundle est un complément important, voire indispensable de par les fonctionnalités supplémentaires qu’il apporte !

Surcharger les templates

Bien que tout à fait correcte, l’interface de base reste très basique (basée sur Bootstrap et suivant le template AdminLTE). Et c’est tout ce qu’on demande à un module d’administration : de la fonctionnalité, pas de l'esthétique.

Alors n’hésitez pas à customiser vos interfaces afin d'y apporter votre propre identité visuelle ou des fonctionnalités supplémentaires.

Vous pouvez tout simplement rajouter des assets custom via la configuration :

easy_admin:
   design:
       assets:
           css:
               - 'bundles/app/css/custom_admin.css'
           js:
               - 'bundles/app/js/custom_admin.js'

Ou bien surcharger n’importe quel template de la façon suivante :

easy_admin:
   design:
       templates:
           # Twig namespace template syntax
           paginator: '@App/Default/fragments/_paginator.html.twig'

Si la surcharge du template n’est nécessaire que pour certaines entités, il est possible de déplacer la configuration globale à l’intérieur de celles des entités concernées :

easy_admin:
   entities:
       Product:
           # ...
           templates:
               # Twig namespace template syntax
               paginator: '@App/Default/fragments/_paginator.html.twig'

Un exemple concret d’ajout de fonctionnalité par template custom : sur le screenshot des filtres (visible un peu plus haut dans le point “Améliorer les listings”), il y a un bouton “Réinitialiser les filtres”. Ce bouton n’est pas disponible par défaut mais se révèle très pratique. Voici comment le rajouter :

  • Remplacer le template par défaut de votre listing par un template custom qui étend celui d’EasyAdmin. Il suffit de rajouter cette ligne à votre template twig :
{% extends 'bundles/EasyAdminBundle/default/list.html.twig' %}
  • Il faut ensuite récupérer les informations nécessaires dans l’URL du listing, comme l’entité sur laquelle on est, l’action, les informations du menu, les colonnes triées et la page courante :
{% set path = path('easyadmin', {
       'entity': app.request.query.get('entity'),
       'action': app.request.query.get('action'),
       'menuIndex': app.request.query.get('menuIndex'),
       'submenuIndex': app.request.query.get('submenuIndex'),
       'sortField': app.request.query.get('sortField'),
       'sortDirection': app.request.query.get('sortDirection'),
       'page': app.request.query.get('page')
   }) %}
  • Maintenant qu’on a le chemin sans les informations liées aux filtres, il suffit de rajouter le bouton au composant de filtres. Pour cela, il faut surcharger le bloc twig des filtres :
{% block list_form_filters %}
...
{% endblock list_form_filters %}

Un simple lien <a> avec un href pointant sur le chemin précédemment calculé fera l’affaire :

<a class="button" href="{{ path }}">
  <small>Réinitialiser les filtres</small>
</a>

Et voilà ! Simple et efficace.

Lancez-vous !

Il est facile et très rapide de créer une interface d’administration avec EasyAdmin, alors n’ayez crainte de perdre une semaine de développement !

Il est également possible d’utiliser EasyAdmin pour prototyper un Back Office pour une démonstration client et le remplacer par un autre admin bundle ou du custom après coup. Cela vous évite d’investir du temps dans un projet au cahier des charges encore incomplet ou auquel on ne donnera peut-être pas suite.