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

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