Chúng ta sẽ xem cách xây dựng một giao diện quản trị bao gồm nhiều bảng có quan hệ Many2Many.
Hãy xem lại ví dụ của chúng ta về giao diện nhiều/nhiều có sẵn tại đây
Chúng ta có một bảng khu vực, bao gồm nhiều yếu tố từ bảng phòng ban. Trên những phòng ban này, chúng ta có các cơ quan.
Để hoàn thiện, và để ý nghĩa cho chuỗi dữ liệu này, chúng ta thêm một bảng zx_credential, đại diện cho nhân viên bán hàng.
Dưới đây là chuỗi dữ liệu của chúng ta: Nhân viên bán hàng->Khu vực->Phòng ban->Cơ quan.
Đối với dự án của chúng ta, chúng ta đã mô hình hóa toàn bộ cơ sở dữ liệu thông qua MysqWorkbench, và xuất schema sang MySql.
Tất cả những gì còn lại là xuất các thực thể (với getters và setters), và tạo 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
Vì hình thức, chúng ta đổi tên bảng "zx_credential" thành "Truy cập Thương mại".
Trong tệp service.yaml, chỉ cần sửa đổi đối số "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

Cho đến nay, không có gì bất thường. Nhưng điều chúng ta muốn là có một giao diện với toàn bộ chuỗi dữ liệu được liên kết với nhau, để khi chúng ta chỉnh sửa một nhân viên bán hàng, chúng ta có tùy chọn đi thẳng vào cấu hình của khu vực của họ, sau đó là phòng ban của họ, rồi đến cơ quan của họ.
Để làm điều này, chúng ta sẽ xác định các liên kết bảng với nhau thông qua một cuộc gọi đến phương thức "addChild" trong dịch vụ CRUD của chúng ta. Và chúng ta sẽ xác định các bảng con của mỗi bảng.
Để quá trình này hoạt động, chúng ta phải xác định dịch vụ con, cũng như trường cha mẹ được sử dụng cho liên kết.
Trong liên kết ZxCredential->ZxZone của chúng ta, chúng ta có trường sau trong thực thể con của chúng ta (ZxZone):

Vì vậy, đây là cái được sử dụng cho liên kết, và mà chúng ta sẽ sử dụng trong cấu hình của chúng ta.
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
Cấu hình hoàn chỉnh trông như thế này:
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
Ở giai đoạn này, tất cả các bảng của chúng ta đều được liên kết, nhưng chúng ta sẽ cần phải cấu hình menu để điều hướng giữa chúng.
Để làm điều này, chúng ta sẽ thêm phương thức configureSideMenu trong admin bán hàng ZxCredentialAdmin của chúng ta.
Hãy cẩn thận, nó ở điểm nhập của giao diện mà bạn phải cấu hình menu. Vì vậy, toàn bộ chuỗi Nhân viên bán hàng->Khu vực->Phòng ban->Cơ quan sẽ được thực hiện trong Nhân viên bán hàng.
Bước đầu tiên, chúng ta thêm các uses trong tất cả giao diện admin của chúng ta, để việc đó được hoàn tất.
<?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;
Tiếp theo, chúng ta thêm liên kết đầu tiên của chúng ta đến quản lý khu vực, từ giao diện 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])
]);
}
Cơ bản, điều bạn cần nhớ, và điều không rõ trong tài liệu, là tuyến đường để cấu hình. Chúng ta cần tên của tuyến đường, sau đó là id.
Đối với id, đơn giản, đối với một cấp độ đầu tiên, nó luôn là:
$id = $admin->getRequest()->get('id');
Và đối với tuyến đường, luôn là cùng một điều, đó là tên của dịch vụ theo sau là loại xem. Vì vậy ở đây admin.zx_zone.list
Điều này mang lại cho chúng ta:
$child=$menu->addChild( 'Listes des zones',
[
'uri' => $admin->generateUrl('admin.zx_zone.list', ['id' => $id])
]);

Ở giai đoạn này, chúng ta có giao diện lồng nhau đầu tiên của mình. Đối với những cái tiếp theo, chúng ta sẽ lặp lại cấu hình.
Điều tiếp theo không được giải thích trong tài liệu vì nó là logic hiển nhiên. Nhưng bạn có thể tìm kiếm trong một thời gian dài nếu bạn không hiểu hoạt động của việc lồng các giao diện.
Điều quan trọng là phải hiểu rằng mọi thứ đều được thực hiện từ giao diện đầu tiên. Việc bao gồm các nút menu và xây dựng các tuyến đường.
Cái đầu tiên, sau đó là cái đầu tiên+cái thứ hai, sau đó là cái đầu tiên+cái thứ hai+cái thứ ba.
Giao diện luôn phải được xây dựng trong giao diện đầu tiên của chúng ta, cho mỗi bước. Đối với điều này, chúng ta cần phải lấy id ở mỗi giai đoạn, và xây dựng tuyến đường.
Và để lấy các khóa, mẹo là để kiểm tra các tuyến đường được tạo bởi symfony, qua lệnh 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
Và sự chú ý đặc biệt của chúng tôi sẽ tập trung vào tuyến đường admin_app_zxcredential_zxzone_departement_agences_list của chúng tôi, nơi chứa toàn bộ chuỗi dữ liệu của chúng tôi.
Nó bao gồm các bảng của chúng tôi với tên của các id mà chúng tôi sẽ truy xuất để xây dựng các tuyến đường của các giao diện của chúng tôi.
/admin/app/zxcredential/{id}/zxzone/{childId}/departement/{childChildId}/agences/list
zxcredential: id
zxzone: childId
departement: childChildId
Vì vậy, bằng cách kiểm tra sự hiện diện của các biến này trong các URL đến của chúng tôi, chúng tôi sẽ có thể xác định độ sâu của giao diện của chúng tôi và xây dựng các menu tương ứng.
Đối với các tuyến đường, nó đơn giản, cho mỗi giai đoạn, chúng tôi thêm tuyến đường hiện tại được phân cách bởi một dấu ống |:
zxcredential: admin.zx_zone.list
zxzone: admin.zx_zone|admin.departement.list
departement: admin.zx_zone|admin.departement|admin.agences.list
Điều này cho chúng ta:
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')
])
]);
}
}
Giao diện của chúng tôi giờ đã hoàn chỉnh:
