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

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