Nous allons voir comment on construit une interface d’admin,
composée de plusieurs tables qui ont des relations
Many2Many.
Reprenons notre exemple d’interface
many/many disponible ici
Nous avons une table zone, qui est composé de
plusieurs éléments de la table départements.
Sur ces départements, nous avons des agences.
Pour
couronner le tout, et pour donner du sens à cette
chaîne de données, nous ajoutons une table
zx_credential, qui représente des commerciaux.
Voici
notre chaîne de donnée :
Commerciaux->Zones->Départements->Agences.
Pour notre projet, nous avons modélisé
l’ensemble de la base via MysqWorkbench, et exporté
le schéma dans MySql.
Il ne nous reste plus
qu’à exporter les entity (avec getter et setters), et
créer les CRUD Sonata
php bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity
php bin/console make:entity --regenerate App
php bin/console make:sonata:admin App/Entity/ZxZone
php bin/console make:sonata:admin App/Entity/ZxCredential
php bin/console make:sonata:admin App/Entity/Agences
php bin/console make:sonata:admin App/Entity/Departement
php bin/console cache:clear
Pour la forme, nous renommons notre table
« zx_credential » en
« Accès commerciaux ».
Dans le
fichier service.yaml il suffit de modifier l’argument
« Label ».
admin.zx_credential:
class: App\Admin\ZxCredentialAdmin
arguments: [~, App\Entity\ZxCredential, App\Controller\ZxCredentialAdminController]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: "Accès commerciaux" }
public: true
Jusqu’ici, rien de plus classique. Mais ce que l’on
souhaite c’est avoir une interface avec toute la
chaîne de donnée liée entre elle, de sorte que
lorsque l’on édite un commercial, on ait la
possibilité d’aller directement dans la configuration
de sa zone, puis ses départements, puis ses
agences.
Pour cela, nous allons spécifier les liaisons
des tables entre elles via un appel à la méthode
« addChild » dans le service de notre CRUD.
Et nous allons spécifié les enfants de chaques
tables.
Pour que le process puisse fonctionner nous devons
spécifier le service enfant, ainsi que le champs du parent
utilisé pour faire la liaison.
Dans notre liaison
ZxCredential->ZxZone, nous avons le champs suivant dans notre
entity child (ZxZone) :
C’est donc celui-ci qui est utilisé pour faire la liaison, et que nous allons utiliser dans notre configuration.
admin.zx_credential:
class: App\Admin\ZxCredentialAdmin
arguments: [~, App\Entity\ZxCredential, App\Controller\ZxCredentialAdminController]
calls:
- [addChild, ["@admin.zx_zone", 'zxCredential']]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: "Accès commerciaux" }
public: true
La configuration complète nous donne ceci :
admin.departement:
class: App\Admin\DepartementAdmin
arguments: [~, App\Entity\Departement, ~]
calls:
- [addChild, ["@admin.agences","departement"]]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: Departement }
public: true
admin.zx_zone:
class: App\Admin\ZxZoneAdmin
arguments: [~, App\Entity\ZxZone, App\Controller\ZxZoneAdminController]
calls:
- [addChild, ["@admin.departement", "zxZone"]]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: Zones }
public: true
admin.zx_credential:
class: App\Admin\ZxCredentialAdmin
arguments: [~, App\Entity\ZxCredential, App\Controller\ZxCredentialAdminController]
calls:
- [addChild, ["@admin.zx_zone", 'zxCredential']]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: "Accès commerciaux" }
public: true
admin.agences:
class: App\Admin\AgencesAdmin
arguments: [~, App\Entity\Agences, ~]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: Agences }
public: true
A ce stade, toutes nos tables sont liées, mais il faudra
configurer le menu pour naviguer entre elles.
Pour cela, nous
allons ajouter la méthode configureSideMenu dans notre
admin des commerciaux
« ZxCredentialAdmin ».
Attention,
c’est dans le point d’entré de
l’interface qu’il faut configurer le menu. Donc toute
la chaîne
Commerciaux->Zones->Départements->Agences va se
faire dans Commerciaux.
1iere étape, on ajoute les
use dans toutes nos interfaces d’admin, comme ça
c’est bouclé.
<?php
declare(strict_types=1);
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
/*gestion de nos interfaces imbriquées*/
use Knp\Menu\ItemInterface as MenuItemInterface;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\RouteCollection;
Ensuite on ajoute notre premier lien vers la gestion des zones, depuis l’interface Zx_Credential.
protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null): void
{
if (!$childAdmin && !\in_array($action, ['edit'], true)) {
return;
}
$admin = $this->isChild() ? $this->getParent() : $this;
$id = $admin->getRequest()->get('id');
$label=$this->hasSubject() && null !== $this->getSubject()->getLabel() ? $this->getSubject()->getLabel():null;
$menu->addChild(
'Configuration de l\'accès commercial : '.$label,
$admin->generateMenuUrl('edit', ['id' => $id])
);
$child=$menu->addChild( 'Listes des zones',
[
'uri' => $admin->generateUrl('admin.zx_zone.list', ['id' => $id])
]);
}
En gros, ce qu’il faut retenir, et qui n’est pas clair
dans la documentation, c’est la route à configurer.
Il nous faut le nom de la route, puis l’id.
Pour
l’id, c’est simple, pour un premier niveaux,
c’est toujours :
$id = $admin->getRequest()->get('id');
Et pour la route, c’est toujours la même chose,
c’est le nom du service suivit du type de la vue. Donc ici
admin.zx_zone.list
Ce qui nous donne :
$child=$menu->addChild( 'Listes des zones',
[
'uri' => $admin->generateUrl('admin.zx_zone.list', ['id' => $id])
]);
A ce stade, nous avons notre première interface
imbriquée. Pour les suivantes, nous allons
répéter la configuration.
Ce qui suit n’est
pas expliqué dans la documentation, car c’est
d’une logique implacable. Mais vous pourrez chercher
longtemps si vous n’avez pas compris le fonctionnement de
l’imbrication des interfaces.
Ce qu’il faut bien
comprendre c’est que tout se fait à partir de la
première interface. L’inclusion des boutons du menu
et la construction des routes.
La premier, puis la
première+la seconde, puis la premier+la seconde+ la
troisième.
L’interface doit se construire
toujours dans notre première interface, pour chaque
étapes. Pour cela nous avons besoin de
récupérer l’id à chaque étapes,
et construire la route.
Et pour récupérer les
clés, le truc c’est de scruter les routes qui sont
générées par symfony, via la commande
debug:router
php bin/console debug:router
---------------------------------------------------------- ---------- -------- ------ -------------------------------------------------------------------------------------------------------------
Name Method Scheme Host Path
---------------------------------------------------------- ---------- -------- ------ -------------------------------------------------------------------------------------------------------------
_preview_error ANY ANY ANY /_error/{code}.{_format}
_wdt ANY ANY ANY /_wdt/{token}
_profiler_home ANY ANY ANY /_profiler/
_profiler_search ANY ANY ANY /_profiler/search
_profiler_search_bar ANY ANY ANY /_profiler/search_bar
_profiler_phpinfo ANY ANY ANY /_profiler/phpinfo
_profiler_search_results ANY ANY ANY /_profiler/{token}/search/results
_profiler_open_file ANY ANY ANY /_profiler/open
_profiler ANY ANY ANY /_profiler/{token}
_profiler_router ANY ANY ANY /_profiler/{token}/router
_profiler_exception ANY ANY ANY /_profiler/{token}/exception
_profiler_exception_css ANY ANY ANY /_profiler/{token}/exception.css
admin_app_departement_list ANY ANY ANY /admin/app/departement/list
admin_app_departement_create ANY ANY ANY /admin/app/departement/create
admin_app_departement_batch ANY ANY ANY /admin/app/departement/batch
admin_app_departement_edit ANY ANY ANY /admin/app/departement/{id}/edit
admin_app_departement_delete ANY ANY ANY /admin/app/departement/{id}/delete
admin_app_departement_show ANY ANY ANY /admin/app/departement/{id}/show
admin_app_departement_export ANY ANY ANY /admin/app/departement/export
admin_app_departement_agences_list ANY ANY ANY /admin/app/departement/{id}/agences/list
admin_app_departement_agences_create ANY ANY ANY /admin/app/departement/{id}/agences/create
admin_app_departement_agences_batch ANY ANY ANY /admin/app/departement/{id}/agences/batch
admin_app_departement_agences_edit ANY ANY ANY /admin/app/departement/{id}/agences/{childId}/edit
admin_app_departement_agences_delete ANY ANY ANY /admin/app/departement/{id}/agences/{childId}/delete
admin_app_departement_agences_show ANY ANY ANY /admin/app/departement/{id}/agences/{childId}/show
admin_app_departement_agences_export ANY ANY ANY /admin/app/departement/{id}/agences/export
admin_app_zxcredential_list ANY ANY ANY /admin/app/zxcredential/list
admin_app_zxcredential_create ANY ANY ANY /admin/app/zxcredential/create
admin_app_zxcredential_batch ANY ANY ANY /admin/app/zxcredential/batch
admin_app_zxcredential_edit ANY ANY ANY /admin/app/zxcredential/{id}/edit
admin_app_zxcredential_delete ANY ANY ANY /admin/app/zxcredential/{id}/delete
admin_app_zxcredential_show ANY ANY ANY /admin/app/zxcredential/{id}/show
admin_app_zxcredential_export ANY ANY ANY /admin/app/zxcredential/export
admin_app_zxcredential_zxzone_list ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/list
admin_app_zxcredential_zxzone_create ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/create
admin_app_zxcredential_zxzone_batch ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/batch
admin_app_zxcredential_zxzone_edit ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/edit
admin_app_zxcredential_zxzone_delete ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/delete
admin_app_zxcredential_zxzone_show ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/show
admin_app_zxcredential_zxzone_export ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/export
admin_app_zxcredential_zxzone_departement_list ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/list
admin_app_zxcredential_zxzone_departement_create ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/create
admin_app_zxcredential_zxzone_departement_batch ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/batch
admin_app_zxcredential_zxzone_departement_edit ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/edit
admin_app_zxcredential_zxzone_departement_delete ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/delete
admin_app_zxcredential_zxzone_departement_show ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/show
admin_app_zxcredential_zxzone_departement_export ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/export
admin_app_zxcredential_zxzone_departement_agences_list ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/list
admin_app_zxcredential_zxzone_departement_agences_create ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/create
admin_app_zxcredential_zxzone_departement_agences_batch ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/batch
admin_app_zxcredential_zxzone_departement_agences_edit ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/{childChildChildId}/edit
admin_app_zxcredential_zxzone_departement_agences_delete ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/{childChildChildId}/delete
admin_app_zxcredential_zxzone_departement_agences_show ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/{childChildChildId}/show
admin_app_zxcredential_zxzone_departement_agences_export ANY ANY ANY /admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/export
admin_app_agences_list ANY ANY ANY /admin/app/agences/list
admin_app_agences_create ANY ANY ANY /admin/app/agences/create
admin_app_agences_batch ANY ANY ANY /admin/app/agences/batch
admin_app_agences_edit ANY ANY ANY /admin/app/agences/{id}/edit
admin_app_agences_delete ANY ANY ANY /admin/app/agences/{id}/delete
admin_app_agences_show ANY ANY ANY /admin/app/agences/{id}/show
Et notre attention particuliere va se porter sur notre route
admin_app_zxcredential_zxzone_departement_agences_list qui
comporte toute notre chaine de donnée.
Elle est
composée de nos tables avec le nom des id que nous allons
récupéré pour construire les routes de nos
interfaces.
/admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/list
zxcredential
: id
zxzone : childId
departement : childChildId
Donc
en testant la présence de ces variables dans nos urls
entrantes, nous allons pouvoir définir la profondeur de
notre interface et construire les menus en
conséquences.
Pour les routes c’est simple,
pour chaques étapes, on rajoute la route courrante
séparé par un pipe | :
zxcredential :
admin.zx_zone.list
zxzone :
admin.zx_zone|admin.departement.list
departement :
admin.zx_zone|admin.departement|admin.agences.list
Ce qui
nous donnes :
protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null): void
{
if (!$childAdmin && !\in_array($action, ['edit'], true)) {
return;
}
$admin = $this->isChild() ? $this->getParent() : $this;
$id = $admin->getRequest()->get('id');
$label=$this->hasSubject() && null !== $this->getSubject()->getLabel() ? $this->getSubject()->getLabel():null;
$menu->addChild(
'Configuration de l\'accès commercial : '.$label,
$admin->generateMenuUrl('edit', ['id' => $id])
);
$child=$menu->addChild( 'Listes des zones',
[
'uri' => $admin->generateUrl('admin.zx_zone.list', [
'id' => $id
])
]);
if(!empty($admin->getRequest()->get('childId'))){
$child=$menu->addChild( 'Listes des departements',
[
'uri' => $admin->generateUrl('admin.zx_zone|admin.departement.list', [
'id' => $id,
'childId' => $admin->getRequest()->get('childId')
])
]);
}
if(!empty($admin->getRequest()->get('childChildId'))){
$child=$menu->addChild( 'Listes des agences',
[
'uri' => $admin->generateUrl('admin.zx_zone|admin.departement|admin.agences.list', [
'id' => $id,
'childId' => $admin->getRequest()->get('childId'),
'childChildId' => $admin->getRequest()->get('childChildId')
])
]);
}
}
Notre interface est maintenant complète :