vendor/easycorp/easyadmin-bundle/src/Factory/MenuFactory.php line 48

Open in your IDE?
  1. <?php
  2. namespace EasyCorp\Bundle\EasyAdminBundle\Factory;
  3. use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA;
  4. use EasyCorp\Bundle\EasyAdminBundle\Config\UserMenu;
  5. use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
  6. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Factory\MenuFactoryInterface;
  7. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Menu\MenuItemInterface;
  8. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Menu\MenuItemMatcherInterface;
  9. use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
  10. use EasyCorp\Bundle\EasyAdminBundle\Dto\MainMenuDto;
  11. use EasyCorp\Bundle\EasyAdminBundle\Dto\MenuItemDto;
  12. use EasyCorp\Bundle\EasyAdminBundle\Dto\UserMenuDto;
  13. use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
  14. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGeneratorInterface;
  15. use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
  16. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  17. use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
  18. use function Symfony\Component\Translation\t;
  19. use Symfony\Contracts\Translation\TranslatableInterface;
  20. /**
  21.  * @author Javier Eguiluz <javier.eguiluz@gmail.com>
  22.  */
  23. final class MenuFactory implements MenuFactoryInterface
  24. {
  25.     private AdminContextProvider $adminContextProvider;
  26.     private AuthorizationCheckerInterface $authChecker;
  27.     private LogoutUrlGenerator $logoutUrlGenerator;
  28.     private AdminUrlGeneratorInterface $adminUrlGenerator;
  29.     private MenuItemMatcherInterface $menuItemMatcher;
  30.     public function __construct(AdminContextProvider $adminContextProviderAuthorizationCheckerInterface $authCheckerLogoutUrlGenerator $logoutUrlGeneratorAdminUrlGeneratorInterface $adminUrlGeneratorMenuItemMatcherInterface $menuItemMatcher)
  31.     {
  32.         $this->adminContextProvider $adminContextProvider;
  33.         $this->authChecker $authChecker;
  34.         $this->logoutUrlGenerator $logoutUrlGenerator;
  35.         $this->adminUrlGenerator $adminUrlGenerator;
  36.         $this->menuItemMatcher $menuItemMatcher;
  37.     }
  38.     /**
  39.      * @param MenuItemInterface[] $menuItems
  40.      */
  41.     public function createMainMenu(array $menuItems): MainMenuDto
  42.     {
  43.         return new MainMenuDto($this->buildMenuItems($menuItems));
  44.     }
  45.     public function createUserMenu(UserMenu $userMenu): UserMenuDto
  46.     {
  47.         $userMenuDto $userMenu->getAsDto();
  48.         $builtUserMenuItems $this->buildMenuItems($userMenuDto->getItems());
  49.         $userMenuDto->setItems($builtUserMenuItems);
  50.         return $userMenuDto;
  51.     }
  52.     /**
  53.      * @param MenuItemInterface[] $menuItems
  54.      *
  55.      * @return MenuItemDto[]
  56.      */
  57.     private function buildMenuItems(array $menuItems): array
  58.     {
  59.         /** @var AdminContext $adminContext */
  60.         $adminContext $this->adminContextProvider->getContext();
  61.         $translationDomain $adminContext->getI18n()->getTranslationDomain() ?? '';
  62.         $builtItems = [];
  63.         foreach ($menuItems as $i => $menuItem) {
  64.             $menuItemDto $menuItem->getAsDto();
  65.             if (false === $this->authChecker->isGranted(Permission::EA_VIEW_MENU_ITEM$menuItemDto)) {
  66.                 continue;
  67.             }
  68.             $subItems = [];
  69.             foreach ($menuItemDto->getSubItems() as $j => $menuSubItemDto) {
  70.                 if (false === $this->authChecker->isGranted(Permission::EA_VIEW_MENU_ITEM$menuSubItemDto)) {
  71.                     continue;
  72.                 }
  73.                 $subItems[] = $this->buildMenuItem($menuSubItemDto, [], $translationDomain);
  74.             }
  75.             $builtItems[] = $this->buildMenuItem($menuItemDto$subItems$translationDomain);
  76.         }
  77.         $builtItems $this->menuItemMatcher->markSelectedMenuItem($builtItems$adminContext->getRequest());
  78.         return $builtItems;
  79.     }
  80.     private function buildMenuItem(MenuItemDto $menuItemDto, array $subItemsstring $translationDomain): MenuItemDto
  81.     {
  82.         if (!$menuItemDto->getLabel() instanceof TranslatableInterface) {
  83.             $label $menuItemDto->getLabel();
  84.             $menuItemDto->setLabel(
  85.                 '' === $label $label t($label$menuItemDto->getTranslationParameters(), $translationDomain)
  86.             );
  87.         }
  88.         $url $this->generateMenuItemUrl($menuItemDto);
  89.         $menuItemDto->setLinkUrl($url);
  90.         $menuItemDto->setSubItems($subItems);
  91.         // if menu item points to an absolute URL and no 'rel' attribute is defined,
  92.         // assign the 'rel="noopener"' attribute for performance and security reasons.
  93.         // see https://web.dev/external-anchors-use-rel-noopener/
  94.         if ('' === $menuItemDto->getLinkRel() && MenuItemDto::TYPE_URL === $menuItemDto->getType()) {
  95.             $menuItemDto->setLinkRel('noopener');
  96.         }
  97.         return $menuItemDto;
  98.     }
  99.     private function generateMenuItemUrl(MenuItemDto $menuItemDto): string
  100.     {
  101.         $menuItemType $menuItemDto->getType();
  102.         if (MenuItemDto::TYPE_CRUD === $menuItemType) {
  103.             $routeParameters $menuItemDto->getRouteParameters();
  104.             $this->adminUrlGenerator
  105.                 // remove all existing query params to avoid keeping search queries, filters and pagination
  106.                 ->unsetAll()
  107.                 // set any other parameters defined by the menu item
  108.                 ->setAll($routeParameters);
  109.             $entityFqcn $routeParameters[EA::ENTITY_FQCN] ?? null;
  110.             $crudControllerFqcn $routeParameters[EA::CRUD_CONTROLLER_FQCN] ?? null;
  111.             if (null === $entityFqcn && null === $crudControllerFqcn) {
  112.                 throw new \RuntimeException(sprintf('The CRUD menu item with label "%s" must define either the entity FQCN (using the third constructor argument) or the CRUD Controller FQCN (using the "setController()" method).'$menuItemDto->getLabel()));
  113.             }
  114.             // 1. if CRUD controller is defined, use it...
  115.             if (null !== $crudControllerFqcn) {
  116.                 $this->adminUrlGenerator->setController($crudControllerFqcn);
  117.             // 2. ...otherwise, find the CRUD controller from the entityFqcn
  118.             } else {
  119.                 $crudControllers $this->adminContextProvider->getContext()?->getCrudControllers();
  120.                 if (null === $controllerFqcn $crudControllers->findCrudFqcnByEntityFqcn($entityFqcn)) {
  121.                     throw new \RuntimeException(sprintf('Unable to find the controller related to the "%s" Entity; did you forget to extend "%s"?'$entityFqcnAbstractCrudController::class));
  122.                 }
  123.                 $this->adminUrlGenerator->setController($controllerFqcn);
  124.                 $this->adminUrlGenerator->unset(EA::ENTITY_FQCN);
  125.             }
  126.             return $this->adminUrlGenerator->generateUrl();
  127.         }
  128.         if (MenuItemDto::TYPE_DASHBOARD === $menuItemType) {
  129.             return $this->adminUrlGenerator->unsetAll()->generateUrl();
  130.         }
  131.         if (MenuItemDto::TYPE_ROUTE === $menuItemType) {
  132.             return $this->adminUrlGenerator
  133.                 ->unsetAll()
  134.                 ->setRoute($menuItemDto->getRouteName(), $menuItemDto->getRouteParameters())
  135.                 ->generateUrl();
  136.         }
  137.         if (MenuItemDto::TYPE_SECTION === $menuItemType) {
  138.             return '#';
  139.         }
  140.         if (MenuItemDto::TYPE_URL === $menuItemType) {
  141.             return $menuItemDto->getLinkUrl();
  142.         }
  143.         if (MenuItemDto::TYPE_LOGOUT === $menuItemType) {
  144.             return $this->logoutUrlGenerator->getLogoutPath();
  145.         }
  146.         if (MenuItemDto::TYPE_EXIT_IMPERSONATION === $menuItemType) {
  147.             // the switch parameter name can be changed, but this code assumes it's always
  148.             // the default one because Symfony doesn't provide a generic exitImpersonationUrlGenerator
  149.             return '?_switch_user=_exit';
  150.         }
  151.         return '';
  152.     }
  153. }