vendor/easycorp/easyadmin-bundle/src/Router/AdminUrlGenerator.php line 264

Open in your IDE?
  1. <?php
  2. namespace EasyCorp\Bundle\EasyAdminBundle\Router;
  3. use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
  4. use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
  5. use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA;
  6. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Controller\DashboardControllerInterface;
  7. use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
  8. use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
  9. use EasyCorp\Bundle\EasyAdminBundle\Registry\DashboardControllerRegistry;
  10. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  11. /**
  12.  * @author Javier Eguiluz <javier.eguiluz@gmail.com>
  13.  */
  14. final class AdminUrlGenerator implements AdminUrlGeneratorInterface
  15. {
  16.     private bool $isInitialized false;
  17.     private AdminContextProvider $adminContextProvider;
  18.     private UrlGeneratorInterface $urlGenerator;
  19.     private DashboardControllerRegistry $dashboardControllerRegistry;
  20.     private AdminRouteGenerator $adminRouteGenerator;
  21.     private ?string $dashboardRoute null;
  22.     private ?bool $includeReferrer null;
  23.     private array $routeParameters = [];
  24.     private ?string $currentPageReferrer null;
  25.     private ?string $customPageReferrer null;
  26.     public function __construct(AdminContextProvider $adminContextProviderUrlGeneratorInterface $urlGeneratorDashboardControllerRegistry $dashboardControllerRegistryAdminRouteGenerator $adminRouteGenerator)
  27.     {
  28.         $this->adminContextProvider $adminContextProvider;
  29.         $this->urlGenerator $urlGenerator;
  30.         $this->dashboardControllerRegistry $dashboardControllerRegistry;
  31.         $this->adminRouteGenerator $adminRouteGenerator;
  32.     }
  33.     /**
  34.      * @return AdminUrlGenerator
  35.      */
  36.     public function setDashboard(string $dashboardControllerFqcn): AdminUrlGeneratorInterface
  37.     {
  38.         $this->setRouteParameter(EA::DASHBOARD_CONTROLLER_FQCN$dashboardControllerFqcn);
  39.         return $this;
  40.     }
  41.     /**
  42.      * @return AdminUrlGenerator
  43.      */
  44.     public function setController(string $crudControllerFqcn): AdminUrlGeneratorInterface
  45.     {
  46.         $this->setRouteParameter(EA::CRUD_CONTROLLER_FQCN$crudControllerFqcn);
  47.         $this->unset(EA::ROUTE_NAME);
  48.         $this->unset(EA::ROUTE_PARAMS);
  49.         return $this;
  50.     }
  51.     /**
  52.      * @return AdminUrlGenerator
  53.      */
  54.     public function setAction(string $action): AdminUrlGeneratorInterface
  55.     {
  56.         $this->setRouteParameter(EA::CRUD_ACTION$action);
  57.         $this->unset(EA::ROUTE_NAME);
  58.         $this->unset(EA::ROUTE_PARAMS);
  59.         return $this;
  60.     }
  61.     /**
  62.      * @return AdminUrlGenerator
  63.      */
  64.     public function setRoute(string $routeName, array $routeParameters = []): AdminUrlGeneratorInterface
  65.     {
  66.         $this->unsetAllExcept(EA::DASHBOARD_CONTROLLER_FQCN);
  67.         $this->setRouteParameter(EA::ROUTE_NAME$routeName);
  68.         $this->setRouteParameter(EA::ROUTE_PARAMS$routeParameters);
  69.         return $this;
  70.     }
  71.     /**
  72.      * @return AdminUrlGenerator
  73.      */
  74.     public function setEntityId($entityId): AdminUrlGeneratorInterface
  75.     {
  76.         $this->setRouteParameter(EA::ENTITY_ID$entityId);
  77.         return $this;
  78.     }
  79.     public function get(string $paramName): mixed
  80.     {
  81.         if (false === $this->isInitialized) {
  82.             $this->initialize();
  83.         }
  84.         return $this->routeParameters[$paramName] ?? null;
  85.     }
  86.     /**
  87.      * @return AdminUrlGenerator
  88.      */
  89.     public function set(string $paramName$paramValue): AdminUrlGeneratorInterface
  90.     {
  91.         if (\in_array($paramName, [EA::MENU_INDEXEA::SUBMENU_INDEX], true)) {
  92.             trigger_deprecation(
  93.                 'easycorp/easyadmin-bundle',
  94.                 '4.5.0',
  95.                 'Using the "%s" query parameter is deprecated. Menu items are now highlighted automatically based on the Request data, so you don\'t have to deal with menu items manually anymore.',
  96.                 $paramName,
  97.             );
  98.         }
  99.         $this->setRouteParameter($paramName$paramValue);
  100.         return $this;
  101.     }
  102.     /**
  103.      * @return AdminUrlGenerator
  104.      */
  105.     public function setAll(array $routeParameters): AdminUrlGeneratorInterface
  106.     {
  107.         foreach ($routeParameters as $paramName => $paramValue) {
  108.             $this->setRouteParameter($paramName$paramValue);
  109.         }
  110.         return $this;
  111.     }
  112.     /**
  113.      * @return AdminUrlGenerator
  114.      */
  115.     public function unset(string $paramName): AdminUrlGeneratorInterface
  116.     {
  117.         if (false === $this->isInitialized) {
  118.             $this->initialize();
  119.         }
  120.         unset($this->routeParameters[$paramName]);
  121.         return $this;
  122.     }
  123.     /**
  124.      * @return AdminUrlGenerator
  125.      */
  126.     public function unsetAll(): AdminUrlGeneratorInterface
  127.     {
  128.         if (false === $this->isInitialized) {
  129.             $this->initialize();
  130.         }
  131.         $this->routeParameters = [];
  132.         return $this;
  133.     }
  134.     /**
  135.      * @return AdminUrlGenerator
  136.      */
  137.     public function unsetAllExcept(string ...$namesOfParamsToKeep): AdminUrlGeneratorInterface
  138.     {
  139.         if (false === $this->isInitialized) {
  140.             $this->initialize();
  141.         }
  142.         $this->routeParameters array_intersect_key($this->routeParametersarray_flip($namesOfParamsToKeep));
  143.         return $this;
  144.     }
  145.     /**
  146.      * @return AdminUrlGenerator
  147.      */
  148.     public function includeReferrer(): AdminUrlGeneratorInterface
  149.     {
  150.         trigger_deprecation(
  151.             'easycorp/easyadmin-bundle',
  152.             '4.9.0',
  153.             'Adding the referrer argument in the admin URLs via the AdminUrlGenerator::includeReferrer() method is deprecated and it will be removed in 5.0.0. The referrer will now be determined automatically based on the current request.',
  154.         );
  155.         if (false === $this->isInitialized) {
  156.             $this->initialize();
  157.         }
  158.         $this->includeReferrer true;
  159.         return $this;
  160.     }
  161.     /**
  162.      * @return AdminUrlGenerator
  163.      */
  164.     public function removeReferrer(): AdminUrlGeneratorInterface
  165.     {
  166.         if (false === $this->isInitialized) {
  167.             $this->initialize();
  168.         }
  169.         $this->includeReferrer false;
  170.         return $this;
  171.     }
  172.     /**
  173.      * @return AdminUrlGenerator
  174.      */
  175.     public function setReferrer(string $referrer): AdminUrlGeneratorInterface
  176.     {
  177.         trigger_deprecation(
  178.             'easycorp/easyadmin-bundle',
  179.             '4.9.0',
  180.             'Adding the referrer argument in the admin URLs via the AdminUrlGenerator::setReferrer() method is deprecated and it will be removed in 5.0.0. The referrer will now be determined automatically based on the current request.',
  181.         );
  182.         if (false === $this->isInitialized) {
  183.             $this->initialize();
  184.         }
  185.         $this->includeReferrer true;
  186.         $this->customPageReferrer $referrer;
  187.         return $this;
  188.     }
  189.     /**
  190.      * @return AdminUrlGenerator
  191.      */
  192.     public function addSignature(bool $addSignature true): AdminUrlGeneratorInterface
  193.     {
  194.         trigger_deprecation(
  195.             'easycorp/easyadmin-bundle',
  196.             '4.1.0',
  197.             'EasyAdmin URLs no longer include signatures because they don\'t provide any additional security. Calling the "%s" method has no effect, so you can stop calling it. This method will be removed in future EasyAdmin versions.',
  198.             __METHOD__,
  199.         );
  200.         return $this;
  201.     }
  202.     public function getSignature(): string
  203.     {
  204.         trigger_deprecation(
  205.             'easycorp/easyadmin-bundle',
  206.             '4.1.0',
  207.             'EasyAdmin URLs no longer include signatures because they don\'t provide any additional security. Calling the "%s" method will always return an empty string, so you can stop calling it. This method will be removed in future EasyAdmin versions.',
  208.             __METHOD__,
  209.         );
  210.         return '';
  211.     }
  212.     // this method allows to omit the 'generateUrl()' call in templates, making code more concise
  213.     public function __toString(): string
  214.     {
  215.         return $this->generateUrl();
  216.     }
  217.     public function generateUrl(): string
  218.     {
  219.         if (false === $this->isInitialized) {
  220.             $this->initialize();
  221.         }
  222.         if (true === $this->includeReferrer) {
  223.             $this->setRouteParameter(EA::REFERRER$this->customPageReferrer ?? $this->currentPageReferrer);
  224.         }
  225.         // this avoids forcing users to always be explicit about the action to execute
  226.         if (null !== $this->get(EA::CRUD_CONTROLLER_FQCN) && null === $this->get(EA::CRUD_ACTION)) {
  227.             $this->set(EA::CRUD_ACTIONAction::INDEX);
  228.         }
  229.         // if the Dashboard FQCN is defined, find its route and use it to override
  230.         // the current route (this is needed to allow generating links to different dashboards)
  231.         if (null !== $dashboardControllerFqcn $this->get(EA::DASHBOARD_CONTROLLER_FQCN)) {
  232.             if (null === $dashboardRoute $this->dashboardControllerRegistry->getRouteByControllerFqcn($dashboardControllerFqcn)) {
  233.                 throw new \InvalidArgumentException(sprintf('The given "%s" class is not a valid Dashboard controller. Make sure it extends from "%s" or implements "%s".'$dashboardControllerFqcnAbstractDashboardController::class, DashboardControllerInterface::class));
  234.             }
  235.             $this->dashboardRoute $dashboardRoute;
  236.             $this->unset(EA::DASHBOARD_CONTROLLER_FQCN);
  237.         }
  238.         // if the current action is 'index' and an entity ID is defined, remove the entity ID to prevent exceptions automatically
  239.         if (Action::INDEX === $this->get(EA::CRUD_ACTION) && null !== $this->get(EA::ENTITY_ID)) {
  240.             $this->unset(EA::ENTITY_ID);
  241.         }
  242.         // this happens when generating URLs from outside EasyAdmin (AdminContext is null) and
  243.         // no Dashboard FQCN has been defined explicitly
  244.         if (null === $this->dashboardRoute) {
  245.             if ($this->dashboardControllerRegistry->getNumberOfDashboards() > 1) {
  246.                 throw new \RuntimeException('When generating admin URLs from outside EasyAdmin or without a related HTTP request (e.g. in tests, console commands, etc.), if your application has more than one Dashboard, you must associate the URL to a specific Dashboard using the "setDashboard()" method.');
  247.             }
  248.             $this->setDashboard($this->dashboardControllerRegistry->getFirstDashboardFqcn());
  249.             $this->dashboardRoute $this->dashboardControllerRegistry->getFirstDashboardRoute();
  250.         }
  251.         // if present, remove the suffix of i18n route names (it's the content after the last dot
  252.         // in the route name; e.g. 'dashboard.en' -> remove '.en', 'admin.index.en_US' -> remove '.en_US')
  253.         $this->dashboardRoute preg_replace('~\.[a-z]{2}(_[A-Z]{2})?$~'''$this->dashboardRoute);
  254.         // this removes any parameter with a NULL value
  255.         $routeParameters array_filter(
  256.             $this->routeParameters,
  257.             static fn ($parameterValue): bool => null !== $parameterValue
  258.         );
  259.         ksort($routeParameters\SORT_STRING);
  260.         $context $this->adminContextProvider->getContext();
  261.         $usePrettyUrls null !== $context && $context->usePrettyUrls();
  262.         $urlType null !== $context && false === $context->getAbsoluteUrls() ? UrlGeneratorInterface::ABSOLUTE_PATH UrlGeneratorInterface::ABSOLUTE_URL;
  263.         if (null !== $this->get(EA::ROUTE_NAME)) {
  264.             return $this->urlGenerator->generate($this->dashboardRoute$routeParameters$urlType);
  265.         }
  266.         if ($usePrettyUrls) {
  267.             $dashboardControllerFqcn $this->get(EA::DASHBOARD_CONTROLLER_FQCN) ?? $context->getRequest()->attributes->get(EA::DASHBOARD_CONTROLLER_FQCN) ?? $this->dashboardControllerRegistry->getFirstDashboardFqcn();
  268.             $crudControllerFqcn $this->get(EA::CRUD_CONTROLLER_FQCN) ?? $context->getRequest()->attributes->get(EA::CRUD_CONTROLLER_FQCN);
  269.             $actionName $this->get(EA::CRUD_ACTION) ?? $context->getRequest()->attributes->get(EA::CRUD_ACTION);
  270.             if (null === $crudControllerFqcn || null === $routeName $this->adminRouteGenerator->findRouteName($dashboardControllerFqcn$crudControllerFqcn$actionName)) {
  271.                 $routeName $this->dashboardRoute;
  272.             } else {
  273.                 // remove these parameters so they don't appear in the query string when using pretty URLs
  274.                 unset($routeParameters[EA::DASHBOARD_CONTROLLER_FQCN]);
  275.                 unset($routeParameters[EA::CRUD_CONTROLLER_FQCN]);
  276.                 unset($routeParameters[EA::CRUD_ACTION]);
  277.                 unset($routeParameters[EA::ENTITY_FQCN]);
  278.             }
  279.         } else {
  280.             $routeName $this->dashboardRoute;
  281.         }
  282.         if (!$usePrettyUrls && \in_array($routeParameters[EA::CRUD_ACTION] ?? Action::INDEXCrud::ACTION_NAMEStrue)) {
  283.             trigger_deprecation(
  284.                 'easycorp/easyadmin-bundle',
  285.                 '4.14.0',
  286.                 'Not using pretty admin URLs is deprecated because they will become the only available URLs starting from EasyAdmin 5.0.0. Read the docs to learn how to enable pretty URLs in your application.',
  287.             );
  288.         }
  289.         $url $this->urlGenerator->generate($routeName$routeParameters$urlType);
  290.         $url '' === $url '?' $url;
  291.         // this is important to start the generation of each URL from the same initial state
  292.         // otherwise, some parameters used when generating some URL could leak to other URLs
  293.         $this->isInitialized false;
  294.         return $url;
  295.     }
  296.     private function setRouteParameter(string $paramName$paramValue): void
  297.     {
  298.         if (false === $this->isInitialized) {
  299.             $this->initialize();
  300.         }
  301.         if (\is_resource($paramValue)) {
  302.             throw new \InvalidArgumentException(sprintf('The value of the "%s" parameter is a PHP resource, which is not supported as a route parameter.'$paramName));
  303.         }
  304.         if (\is_object($paramValue)) {
  305.             if (method_exists($paramValue'__toString')) {
  306.                 $paramValue = (string) $paramValue;
  307.             } else {
  308.                 throw new \InvalidArgumentException(sprintf('The object passed as the value of the "%s" parameter must implement the "__toString()" method to allow using its value as a route parameter.'$paramName));
  309.             }
  310.         }
  311.         $this->routeParameters[$paramName] = $paramValue;
  312.     }
  313.     private function initialize(): void
  314.     {
  315.         $this->isInitialized true;
  316.         $adminContext $this->adminContextProvider->getContext();
  317.         if (null === $adminContext) {
  318.             $this->dashboardRoute null;
  319.             $currentRouteParameters $routeParametersForReferrer = [];
  320.             $this->currentPageReferrer null;
  321.         } else {
  322.             $this->dashboardRoute $adminContext->getDashboardRouteName();
  323.             $currentRouteParameters $routeParametersForReferrer $adminContext->getRequest()->query->all();
  324.             unset($routeParametersForReferrer[EA::REFERRER]);
  325.             $this->currentPageReferrer sprintf('%s%s?%s'$adminContext->getRequest()->getBaseUrl(), $adminContext->getRequest()->getPathInfo(), http_build_query($routeParametersForReferrer));
  326.         }
  327.         $this->includeReferrer null;
  328.         $this->customPageReferrer null;
  329.         $this->routeParameters $currentRouteParameters;
  330.     }
  331. }