Créer une interface d’administration avec Symfony 6 et EasyAdmin 4
À propos de ce tutoriel
Je vous montre comment créer facilement une interface d’administration (back-office) complète avec EasyAdmin 4 dans Symfony 6.
Création du projet Symfony
symfony new website --full
Installation de EasyAdminBundle
composer require easycorp/easyadmin-bundle
Création du dashboard
symfony console make:admin:dashboard
Création de l’entité « Category »
Nous allons maintenant créer une entité nommée « Category » avec les propriétés suivantes :
- name (type: string, length: 255)
- active (type: boolean)
- updatedAt (type: datetime, nullable: true)
- createdAt (type: datetime)
symfony console m:e category
Création de l’entité « Product »
Il faut ensuite créer l’entité « Product » avec les propriétés suivantes :
- name (t
- description (type: text)
- price (type: decimal, precision: 10, scale: 2)
- image (type: string, length: 255, nullable: true)
- active (type: boolean)
- updatedAt (type: datetime, nullable: true)
- createdAt (type: datetime)
- category (ManyToOne, nullable: false)
symfony console m:e product
Modification du Dashboard
<?php
class DashboardController extends AbstractDashboardController
{
public function __construct(private AdminUrlGenerator $adminUrlGenerator) {}
#[Route('/admin', name: 'admin')]
public function index(): Response
{
$url = $this->adminUrlGenerator
->setController(ProductCrudController::class)
->generateUrl();
return $this->redirect($url);
}
public function configureDashboard(): Dashboard
{
return Dashboard::new()
->setTitle('Website');
}
public function configureMenuItems(): iterable
{
yield MenuItem::section('E-commerce');
yield MenuItem::section('Products');
yield MenuItem::subMenu('Actions', 'fas fa-bars')->setSubItems([
MenuItem::linkToCrud('Create Product', 'fas fa-plus', Product::class)->setAction(Crud::PAGE_NEW),
MenuItem::linkToCrud('Show Products', 'fas fa-eye', Product::class)
]);
yield MenuItem::section('Categories');
yield MenuItem::subMenu('Actions', 'fas fa-bars')->setSubItems([
MenuItem::linkToCrud('Create Category', 'fas fa-plus', Category::class)->setAction(Crud::PAGE_NEW),
MenuItem::linkToCrud('Show Categories', 'fas fa-eye', Category::class)
]);
}
}
Création du CRUD Controller pour les catégories
<?php
class CategoryCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Category::class;
}
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id')->hideOnForm(),
TextField::new('name'),
BooleanField::new('active'),
DateTimeField::new('updatedAt')->hideOnForm(),
DateTimeField::new('createdAt')->hideOnForm(),
];
}
public function persistEntity(EntityManagerInterface $em, $entityInstance): void
{
if (!$entityInstance instanceof Category) return;
$entityInstance->setCreatedAt(new \DateTimeImmutable);
parent::persistEntity($em, $entityInstance);
}
public function deleteEntity(EntityManagerInterface $em, $entityInstance): void
{
if (!$entityInstance instanceof Category) return;
foreach ($entityInstance->getProducts() as $product) {
$em->remove($product);
}
parent::deleteEntity($em, $entityInstance);
}
}
Création du CRUD Controller pour les produits
<?php
class ProductCrudController extends AbstractCrudController
{
public const ACTION_DUPLICATE = 'duplicate';
public const PRODUCTS_BASE_PATH = 'upload/images/products';
public const PRODUCTS_UPLOAD_DIR = 'public/upload/images/products';
public static function getEntityFqcn(): string
{
return Product::class;
}
public function configureActions(Actions $actions): Actions
{
$duplicate = Action::new(self::ACTION_DUPLICATE)
->linkToCrudAction('duplicateProduct')
->setCssClass('btn btn-info');
return $actions
->add(Crud::PAGE_EDIT, $duplicate)
->reorder(Crud::PAGE_EDIT, [self::ACTION_DUPLICATE, Action::SAVE_AND_RETURN]);
}
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id')->hideOnForm(),
TextField::new('name', 'Label'),
TextEditorField::new('description'),
MoneyField::new('price')->setCurrency('EUR'),
ImageField::new('image')
->setBasePath(self::PRODUCTS_BASE_PATH)
->setUploadDir(self::PRODUCTS_UPLOAD_DIR)
->setSortable(false),
BooleanField::new('active'),
AssociationField::new('category')->setQueryBuilder(function (QueryBuilder $queryBuilder) {
$queryBuilder->where('entity.active = true');
}),
DateTimeField::new('updatedAt')->hideOnForm(),
DateTimeField::new('createdAt')->hideOnForm(),
];
}
public function duplicateProduct(
AdminContext $context,
AdminUrlGenerator $adminUrlGenerator,
EntityManagerInterface $em
): Response {
/** @var Product $product */
$product = $context->getEntity()->getInstance();
$duplicatedProduct = clone $product;
parent::persistEntity($em, $duplicatedProduct);
$url = $adminUrlGenerator->setController(self::class)
->setAction(Action::DETAIL)
->setEntityId($duplicatedProduct->getId())
->generateUrl();
return $this->redirect($url);
}
}