vendor/sonata-project/admin-bundle/src/Admin/Pool.php line 253

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the Sonata Project package.
  5.  *
  6.  * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Sonata\AdminBundle\Admin;
  12. use Psr\Container\ContainerInterface;
  13. use Sonata\AdminBundle\Exception\AdminClassNotFoundException;
  14. use Sonata\AdminBundle\Exception\AdminCodeNotFoundException;
  15. use Sonata\AdminBundle\Exception\TooManyAdminClassException;
  16. use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
  17. /**
  18.  * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  19.  *
  20.  * @phpstan-type Item = array{
  21.  *     label: string,
  22.  *     roles: list<string>,
  23.  *     route: string,
  24.  *     route_absolute: bool,
  25.  *     route_params: array<string, string>
  26.  * }|array{
  27.  *     admin: string,
  28.  *     roles: list<string>,
  29.  *     route_absolute: bool,
  30.  *     route_params: array<string, string>
  31.  * }
  32.  * NEXT_MAJOR: Remove the label_catalogue key.
  33.  * @phpstan-type Group = array{
  34.  *     label: string,
  35.  *     translation_domain: string,
  36.  *     label_catalogue?: string,
  37.  *     icon: string,
  38.  *     items: list<Item>,
  39.  *     keep_open: bool,
  40.  *     on_top: bool,
  41.  *     provider?: string,
  42.  *     roles: list<string>
  43.  * }
  44.  */
  45. final class Pool
  46. {
  47.     public const DEFAULT_ADMIN_KEY 'default';
  48.     /**
  49.      * @param string[]                            $adminServiceCodes
  50.      * @param array<string, array<string, mixed>> $adminGroups
  51.      * @param array<class-string, string[]>       $adminClasses
  52.      *
  53.      * @phpstan-param array<string, Group> $adminGroups
  54.      */
  55.     public function __construct(
  56.         private ContainerInterface $container,
  57.         private array $adminServiceCodes = [],
  58.         private array $adminGroups = [],
  59.         private array $adminClasses = [],
  60.     ) {
  61.     }
  62.     /**
  63.      * NEXT_MAJOR: Remove the label_catalogue key.
  64.      *
  65.      * @phpstan-return array<string, array{
  66.      *  label: string,
  67.      *  label_catalogue?: string,
  68.      *  translation_domain: string,
  69.      *  icon: string,
  70.      *  items: list<AdminInterface<object>>,
  71.      *  keep_open: bool,
  72.      *  on_top: bool,
  73.      *  provider?: string,
  74.      *  roles: list<string>
  75.      * }>
  76.      */
  77.     public function getDashboardGroups(): array
  78.     {
  79.         $groups = [];
  80.         foreach ($this->adminGroups as $name => $adminGroup) {
  81.             $items = [];
  82.             foreach ($adminGroup['items'] as $item) {
  83.                 // NEXT_MAJOR: Remove the '' check
  84.                 if (!isset($item['admin']) || '' === $item['admin']) {
  85.                     continue;
  86.                 }
  87.                 $admin $this->getInstance($item['admin']);
  88.                 // NEXT_MAJOR: Keep the "if" part.
  89.                 // @phpstan-ignore-next-line
  90.                 if (method_exists($admin'showInDashboard')) {
  91.                     if (!$admin->showInDashboard()) {
  92.                         continue;
  93.                     }
  94.                 } else {
  95.                     @trigger_error(\sprintf(
  96.                         'Not implementing "%s::showInDashboard()" is deprecated since sonata-project/admin-bundle 4.7'
  97.                         .' and will fail in 5.0.',
  98.                         AdminInterface::class
  99.                     ), \E_USER_DEPRECATED);
  100.                     /**
  101.                      * @psalm-suppress DeprecatedMethod, DeprecatedConstant
  102.                      */
  103.                     if (!$admin->showIn(AbstractAdmin::CONTEXT_DASHBOARD)) {
  104.                         continue;
  105.                     }
  106.                 }
  107.                 $items[] = $admin;
  108.             }
  109.             if ([] !== $items) {
  110.                 $groups[$name] = ['items' => $items] + $adminGroup;
  111.             }
  112.         }
  113.         return $groups;
  114.     }
  115.     /**
  116.      * Return the admin related to the given $class.
  117.      *
  118.      * @throws AdminClassNotFoundException if there is no admin class for the class provided
  119.      * @throws TooManyAdminClassException  if there is multiple admin class for the class provided
  120.      *
  121.      * @phpstan-param class-string $class
  122.      * @phpstan-return AdminInterface<object>
  123.      */
  124.     public function getAdminByClass(string $class): AdminInterface
  125.     {
  126.         if (!$this->hasAdminByClass($class)) {
  127.             throw new AdminClassNotFoundException(\sprintf('Pool has no admin for the class %s.'$class));
  128.         }
  129.         if (isset($this->adminClasses[$class][self::DEFAULT_ADMIN_KEY])) {
  130.             return $this->getInstance($this->adminClasses[$class][self::DEFAULT_ADMIN_KEY]);
  131.         }
  132.         if (!== \count($this->adminClasses[$class])) {
  133.             throw new TooManyAdminClassException(\sprintf(
  134.                 'Unable to find a valid admin for the class: %s, there are too many registered: %s.'
  135.                 .' Please define a default one with the tag attribute `default: true` in your admin configuration.',
  136.                 $class,
  137.                 implode(', '$this->adminClasses[$class])
  138.             ));
  139.         }
  140.         return $this->getInstance(reset($this->adminClasses[$class]));
  141.     }
  142.     /**
  143.      * @phpstan-param class-string $class
  144.      */
  145.     public function hasAdminByClass(string $class): bool
  146.     {
  147.         return isset($this->adminClasses[$class]) && \count($this->adminClasses[$class]) > 0;
  148.     }
  149.     /**
  150.      * Returns an admin class by its Admin code
  151.      * ie : sonata.news.admin.post|sonata.news.admin.comment => return the child class of post.
  152.      *
  153.      * @throws AdminCodeNotFoundException
  154.      *
  155.      * @return AdminInterface<object>
  156.      */
  157.     public function getAdminByAdminCode(string $adminCode): AdminInterface
  158.     {
  159.         $codes explode('|'$adminCode);
  160.         $rootCode trim(array_shift($codes));
  161.         $admin $this->getInstance($rootCode);
  162.         foreach ($codes as $code) {
  163.             if (!\in_array($code$this->adminServiceCodestrue)) {
  164.                 throw new AdminCodeNotFoundException(\sprintf(
  165.                     'Argument 1 passed to %s() must contain a valid admin reference, "%s" found at "%s".',
  166.                     __METHOD__,
  167.                     $code,
  168.                     $adminCode
  169.                 ));
  170.             }
  171.             if (!$admin->hasChild($code)) {
  172.                 throw new AdminCodeNotFoundException(\sprintf(
  173.                     'Argument 1 passed to %s() must contain a valid admin hierarchy,'
  174.                     .' "%s" is not a valid child for "%s"',
  175.                     __METHOD__,
  176.                     $code,
  177.                     $admin->getCode()
  178.                 ));
  179.             }
  180.             $admin $admin->getChild($code);
  181.         }
  182.         return $admin;
  183.     }
  184.     /**
  185.      * Checks if an admin with a certain admin code exists.
  186.      */
  187.     public function hasAdminByAdminCode(string $adminCode): bool
  188.     {
  189.         try {
  190.             $this->getAdminByAdminCode($adminCode);
  191.         } catch (\InvalidArgumentException) {
  192.             return false;
  193.         }
  194.         return true;
  195.     }
  196.     /**
  197.      * @throws AdminClassNotFoundException if there is no admin for the field description target model
  198.      * @throws TooManyAdminClassException  if there is too many admin for the field description target model
  199.      * @throws AdminCodeNotFoundException  if the admin_code option is invalid
  200.      *
  201.      * @return AdminInterface<object>
  202.      */
  203.     public function getAdminByFieldDescription(FieldDescriptionInterface $fieldDescription): AdminInterface
  204.     {
  205.         $adminCode $fieldDescription->getOption('admin_code');
  206.         if (\is_string($adminCode)) {
  207.             return $this->getAdminByAdminCode($adminCode);
  208.         }
  209.         $targetModel $fieldDescription->getTargetModel();
  210.         if (null === $targetModel) {
  211.             throw new \InvalidArgumentException('The field description has no target model.');
  212.         }
  213.         return $this->getAdminByClass($targetModel);
  214.     }
  215.     /**
  216.      * Returns a new admin instance depends on the given code.
  217.      *
  218.      * @throws AdminCodeNotFoundException if the code is not found in admin pool
  219.      *
  220.      * @return AdminInterface<object>
  221.      */
  222.     public function getInstance(string $code): AdminInterface
  223.     {
  224.         if ('' === $code) {
  225.             throw new \InvalidArgumentException(
  226.                 'Admin code must contain a valid admin reference, empty string given.'
  227.             );
  228.         }
  229.         if (!\in_array($code$this->adminServiceCodestrue)) {
  230.             $msg \sprintf('Admin service "%s" not found in admin pool.'$code);
  231.             $shortest = -1;
  232.             $closest null;
  233.             $alternatives = [];
  234.             foreach ($this->adminServiceCodes as $adminServiceCode) {
  235.                 $lev levenshtein($code$adminServiceCode);
  236.                 if ($lev <= $shortest || $shortest 0) {
  237.                     $closest $adminServiceCode;
  238.                     $shortest $lev;
  239.                 }
  240.                 if ($lev <= \strlen($adminServiceCode) / || str_contains($adminServiceCode$code)) {
  241.                     $alternatives[$adminServiceCode] = $lev;
  242.                 }
  243.             }
  244.             if (null !== $closest) {
  245.                 asort($alternatives);
  246.                 unset($alternatives[$closest]);
  247.                 $msg \sprintf(
  248.                     'Admin service "%s" not found in admin pool. Did you mean "%s" or one of those: [%s]?',
  249.                     $code,
  250.                     $closest,
  251.                     implode(', 'array_keys($alternatives))
  252.                 );
  253.             }
  254.             throw new AdminCodeNotFoundException($msg);
  255.         }
  256.         $admin $this->container->get($code);
  257.         if (!$admin instanceof AdminInterface) {
  258.             throw new \InvalidArgumentException(\sprintf('Found service "%s" is not a valid admin service'$code));
  259.         }
  260.         return $admin;
  261.     }
  262.     /**
  263.      * @return array<string, array<string, mixed>>
  264.      *
  265.      * @phpstan-return array<string, Group>
  266.      */
  267.     public function getAdminGroups(): array
  268.     {
  269.         return $this->adminGroups;
  270.     }
  271.     /**
  272.      * @return string[]
  273.      */
  274.     public function getAdminServiceCodes(): array
  275.     {
  276.         return $this->adminServiceCodes;
  277.     }
  278.     /**
  279.      * NEXT_MAJOR: Remove this method.
  280.      *
  281.      * @deprecated since sonata-project/admin-bundle 4.20 will be removed in 5.0 use getAdminServiceCodes() instead.
  282.      *
  283.      * @return string[]
  284.      */
  285.     public function getAdminServiceIds(): array
  286.     {
  287.         return $this->adminServiceCodes;
  288.     }
  289.     /**
  290.      * @return array<string, string[]>
  291.      *
  292.      * @phpstan-return array<class-string, string[]>
  293.      */
  294.     public function getAdminClasses(): array
  295.     {
  296.         return $this->adminClasses;
  297.     }
  298. }