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

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 array_values(array_filter(array_map(function (array $item): ?AdminInterface {
  82.                 // NEXT_MAJOR: Remove the '' check
  83.                 if (!isset($item['admin']) || '' === $item['admin']) {
  84.                     return null;
  85.                 }
  86.                 $admin $this->getInstance($item['admin']);
  87.                 // NEXT_MAJOR: Keep the if part.
  88.                 // @phpstan-ignore-next-line
  89.                 if (method_exists($admin'showInDashboard')) {
  90.                     if (!$admin->showInDashboard()) {
  91.                         return null;
  92.                     }
  93.                 } else {
  94.                     @trigger_error(sprintf(
  95.                         'Not implementing "%s::showInDashboard()" is deprecated since sonata-project/admin-bundle 4.7'
  96.                         .' and will fail in 5.0.',
  97.                         AdminInterface::class
  98.                     ), \E_USER_DEPRECATED);
  99.                     if (!$admin->showIn(AbstractAdmin::CONTEXT_DASHBOARD)) {
  100.                         return null;
  101.                     }
  102.                 }
  103.                 return $admin;
  104.             }, $adminGroup['items'])));
  105.             if ([] !== $items) {
  106.                 $groups[$name] = ['items' => $items] + $adminGroup;
  107.             }
  108.         }
  109.         return $groups;
  110.     }
  111.     /**
  112.      * Return the admin related to the given $class.
  113.      *
  114.      * @throws AdminClassNotFoundException if there is no admin class for the class provided
  115.      * @throws TooManyAdminClassException  if there is multiple admin class for the class provided
  116.      *
  117.      * @phpstan-param class-string $class
  118.      * @phpstan-return AdminInterface<object>
  119.      */
  120.     public function getAdminByClass(string $class): AdminInterface
  121.     {
  122.         if (!$this->hasAdminByClass($class)) {
  123.             throw new AdminClassNotFoundException(sprintf('Pool has no admin for the class %s.'$class));
  124.         }
  125.         if (isset($this->adminClasses[$class][self::DEFAULT_ADMIN_KEY])) {
  126.             return $this->getInstance($this->adminClasses[$class][self::DEFAULT_ADMIN_KEY]);
  127.         }
  128.         if (!== \count($this->adminClasses[$class])) {
  129.             throw new TooManyAdminClassException(sprintf(
  130.                 'Unable to find a valid admin for the class: %s, there are too many registered: %s.'
  131.                 .' Please define a default one with the tag attribute `default: true` in your admin configuration.',
  132.                 $class,
  133.                 implode(', '$this->adminClasses[$class])
  134.             ));
  135.         }
  136.         return $this->getInstance(reset($this->adminClasses[$class]));
  137.     }
  138.     /**
  139.      * @phpstan-param class-string $class
  140.      */
  141.     public function hasAdminByClass(string $class): bool
  142.     {
  143.         return isset($this->adminClasses[$class]) && \count($this->adminClasses[$class]) > 0;
  144.     }
  145.     /**
  146.      * Returns an admin class by its Admin code
  147.      * ie : sonata.news.admin.post|sonata.news.admin.comment => return the child class of post.
  148.      *
  149.      * @throws AdminCodeNotFoundException
  150.      *
  151.      * @return AdminInterface<object>
  152.      */
  153.     public function getAdminByAdminCode(string $adminCode): AdminInterface
  154.     {
  155.         $codes explode('|'$adminCode);
  156.         $rootCode trim(array_shift($codes));
  157.         $admin $this->getInstance($rootCode);
  158.         foreach ($codes as $code) {
  159.             if (!\in_array($code$this->adminServiceCodestrue)) {
  160.                 throw new AdminCodeNotFoundException(sprintf(
  161.                     'Argument 1 passed to %s() must contain a valid admin reference, "%s" found at "%s".',
  162.                     __METHOD__,
  163.                     $code,
  164.                     $adminCode
  165.                 ));
  166.             }
  167.             if (!$admin->hasChild($code)) {
  168.                 throw new AdminCodeNotFoundException(sprintf(
  169.                     'Argument 1 passed to %s() must contain a valid admin hierarchy,'
  170.                     .' "%s" is not a valid child for "%s"',
  171.                     __METHOD__,
  172.                     $code,
  173.                     $admin->getCode()
  174.                 ));
  175.             }
  176.             $admin $admin->getChild($code);
  177.         }
  178.         return $admin;
  179.     }
  180.     /**
  181.      * Checks if an admin with a certain admin code exists.
  182.      */
  183.     public function hasAdminByAdminCode(string $adminCode): bool
  184.     {
  185.         try {
  186.             $this->getAdminByAdminCode($adminCode);
  187.         } catch (\InvalidArgumentException) {
  188.             return false;
  189.         }
  190.         return true;
  191.     }
  192.     /**
  193.      * @throws AdminClassNotFoundException if there is no admin for the field description target model
  194.      * @throws TooManyAdminClassException  if there is too many admin for the field description target model
  195.      * @throws AdminCodeNotFoundException  if the admin_code option is invalid
  196.      *
  197.      * @return AdminInterface<object>
  198.      */
  199.     public function getAdminByFieldDescription(FieldDescriptionInterface $fieldDescription): AdminInterface
  200.     {
  201.         $adminCode $fieldDescription->getOption('admin_code');
  202.         if (\is_string($adminCode)) {
  203.             return $this->getAdminByAdminCode($adminCode);
  204.         }
  205.         $targetModel $fieldDescription->getTargetModel();
  206.         if (null === $targetModel) {
  207.             throw new \InvalidArgumentException('The field description has no target model.');
  208.         }
  209.         return $this->getAdminByClass($targetModel);
  210.     }
  211.     /**
  212.      * Returns a new admin instance depends on the given code.
  213.      *
  214.      * @throws AdminCodeNotFoundException if the code is not found in admin pool
  215.      *
  216.      * @return AdminInterface<object>
  217.      */
  218.     public function getInstance(string $code): AdminInterface
  219.     {
  220.         if ('' === $code) {
  221.             throw new \InvalidArgumentException(
  222.                 'Admin code must contain a valid admin reference, empty string given.'
  223.             );
  224.         }
  225.         if (!\in_array($code$this->adminServiceCodestrue)) {
  226.             $msg sprintf('Admin service "%s" not found in admin pool.'$code);
  227.             $shortest = -1;
  228.             $closest null;
  229.             $alternatives = [];
  230.             foreach ($this->adminServiceCodes as $adminServiceCode) {
  231.                 $lev levenshtein($code$adminServiceCode);
  232.                 if ($lev <= $shortest || $shortest 0) {
  233.                     $closest $adminServiceCode;
  234.                     $shortest $lev;
  235.                 }
  236.                 if ($lev <= \strlen($adminServiceCode) / || str_contains($adminServiceCode$code)) {
  237.                     $alternatives[$adminServiceCode] = $lev;
  238.                 }
  239.             }
  240.             if (null !== $closest) {
  241.                 asort($alternatives);
  242.                 unset($alternatives[$closest]);
  243.                 $msg sprintf(
  244.                     'Admin service "%s" not found in admin pool. Did you mean "%s" or one of those: [%s]?',
  245.                     $code,
  246.                     $closest,
  247.                     implode(', 'array_keys($alternatives))
  248.                 );
  249.             }
  250.             throw new AdminCodeNotFoundException($msg);
  251.         }
  252.         $admin $this->container->get($code);
  253.         if (!$admin instanceof AdminInterface) {
  254.             throw new \InvalidArgumentException(sprintf('Found service "%s" is not a valid admin service'$code));
  255.         }
  256.         return $admin;
  257.     }
  258.     /**
  259.      * @return array<string, array<string, mixed>>
  260.      *
  261.      * @phpstan-return array<string, Group>
  262.      */
  263.     public function getAdminGroups(): array
  264.     {
  265.         return $this->adminGroups;
  266.     }
  267.     /**
  268.      * @return string[]
  269.      */
  270.     public function getAdminServiceCodes(): array
  271.     {
  272.         return $this->adminServiceCodes;
  273.     }
  274.     /**
  275.      * NEXT_MAJOR: Remove this method.
  276.      *
  277.      * @deprecated since sonata-project/admin-bundle 4.20 will be removed in 5.0 use getAdminServiceCodes() instead.
  278.      *
  279.      * @return string[]
  280.      */
  281.     public function getAdminServiceIds(): array
  282.     {
  283.         return $this->adminServiceCodes;
  284.     }
  285.     /**
  286.      * @return array<string, string[]>
  287.      *
  288.      * @phpstan-return array<class-string, string[]>
  289.      */
  290.     public function getAdminClasses(): array
  291.     {
  292.         return $this->adminClasses;
  293.     }
  294. }