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

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