vendor/sonata-project/admin-bundle/src/Admin/Pool.php line 106
<?php
declare(strict_types=1);
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\AdminBundle\Admin;
use Psr\Container\ContainerInterface;
use Sonata\AdminBundle\Exception\AdminClassNotFoundException;
use Sonata\AdminBundle\Exception\AdminCodeNotFoundException;
use Sonata\AdminBundle\Exception\TooManyAdminClassException;
use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* @phpstan-type Item = array{
* label: string,
* roles: list<string>,
* route: string,
* route_absolute: bool,
* route_params: array<string, string>
* }|array{
* admin: string,
* roles: list<string>,
* route_absolute: bool,
* route_params: array<string, string>
* }
* NEXT_MAJOR: Remove the label_catalogue key.
* @phpstan-type Group = array{
* label: string,
* translation_domain: string,
* label_catalogue?: string,
* icon: string,
* items: list<Item>,
* keep_open: bool,
* on_top: bool,
* provider?: string,
* roles: list<string>
* }
*/
final class Pool
{
public const DEFAULT_ADMIN_KEY = 'default';
private ContainerInterface $container;
/**
* @var string[]
*/
private array $adminServiceCodes = [];
/**
* @var array<string, array<string, mixed>>
*
* @phpstan-var array<string, Group>
*/
private array $adminGroups = [];
/**
* @var array<string, string[]>
*
* @phpstan-var array<class-string, string[]>
*/
private array $adminClasses = [];
/**
* @param string[] $adminServices
* @param array<string, array<string, mixed>> $adminGroups
* @param array<class-string, string[]> $adminClasses
*
* @phpstan-param array<string, Group> $adminGroups
*/
public function __construct(
ContainerInterface $container,
array $adminServices = [],
array $adminGroups = [],
array $adminClasses = []
) {
$this->container = $container;
$this->adminServiceCodes = $adminServices;
$this->adminGroups = $adminGroups;
$this->adminClasses = $adminClasses;
}
/**
* @phpstan-return array<string, array{
* label: string,
* translation_domain: string,
* icon: string,
* items: list<AdminInterface<object>>,
* keep_open: bool,
* on_top: bool,
* provider?: string,
* roles: list<string>
* }>
*/
public function getDashboardGroups(): array
{
$groups = [];
foreach ($this->adminGroups as $name => $adminGroup) {
$items = array_values(array_filter(array_map(function (array $item): ?AdminInterface {
// NEXT_MAJOR: Remove the '' check
if (!isset($item['admin']) || '' === $item['admin']) {
return null;
}
$admin = $this->getInstance($item['admin']);
// NEXT_MAJOR: Keep the if part.
// @phpstan-ignore-next-line
if (method_exists($admin, 'showInDashboard')) {
if (!$admin->showInDashboard()) {
return null;
}
} else {
@trigger_error(sprintf(
'Not implementing "%s::showInDashboard()" is deprecated since sonata-project/admin-bundle 4.7'
.' and will fail in 5.0.',
AdminInterface::class
), \E_USER_DEPRECATED);
if (!$admin->showIn(AbstractAdmin::CONTEXT_DASHBOARD)) {
return null;
}
}
return $admin;
}, $adminGroup['items'])));
if ([] !== $items) {
$groups[$name] = ['items' => $items] + $adminGroup;
}
}
return $groups;
}
/**
* Return the admin related to the given $class.
*
* @throws AdminClassNotFoundException if there is no admin class for the class provided
* @throws TooManyAdminClassException if there is multiple admin class for the class provided
*
* @phpstan-param class-string $class
* @phpstan-return AdminInterface<object>
*/
public function getAdminByClass(string $class): AdminInterface
{
if (!$this->hasAdminByClass($class)) {
throw new AdminClassNotFoundException(sprintf('Pool has no admin for the class %s.', $class));
}
if (isset($this->adminClasses[$class][self::DEFAULT_ADMIN_KEY])) {
return $this->getInstance($this->adminClasses[$class][self::DEFAULT_ADMIN_KEY]);
}
if (1 !== \count($this->adminClasses[$class])) {
throw new TooManyAdminClassException(sprintf(
'Unable to find a valid admin for the class: %s, there are too many registered: %s.'
.' Please define a default one with the tag attribute `default: true` in your admin configuration.',
$class,
implode(', ', $this->adminClasses[$class])
));
}
return $this->getInstance(reset($this->adminClasses[$class]));
}
/**
* @phpstan-param class-string $class
*/
public function hasAdminByClass(string $class): bool
{
return isset($this->adminClasses[$class]) && \count($this->adminClasses[$class]) > 0;
}
/**
* Returns an admin class by its Admin code
* ie : sonata.news.admin.post|sonata.news.admin.comment => return the child class of post.
*
* @throws AdminCodeNotFoundException
*
* @return AdminInterface<object>
*/
public function getAdminByAdminCode(string $adminCode): AdminInterface
{
$codes = explode('|', $adminCode);
$rootCode = trim(array_shift($codes));
$admin = $this->getInstance($rootCode);
foreach ($codes as $code) {
if (!\in_array($code, $this->adminServiceCodes, true)) {
throw new AdminCodeNotFoundException(sprintf(
'Argument 1 passed to %s() must contain a valid admin reference, "%s" found at "%s".',
__METHOD__,
$code,
$adminCode
));
}
if (!$admin->hasChild($code)) {
throw new AdminCodeNotFoundException(sprintf(
'Argument 1 passed to %s() must contain a valid admin hierarchy,'
.' "%s" is not a valid child for "%s"',
__METHOD__,
$code,
$admin->getCode()
));
}
$admin = $admin->getChild($code);
}
return $admin;
}
/**
* Checks if an admin with a certain admin code exists.
*/
public function hasAdminByAdminCode(string $adminCode): bool
{
try {
$this->getAdminByAdminCode($adminCode);
} catch (\InvalidArgumentException $e) {
return false;
}
return true;
}
/**
* @throws AdminClassNotFoundException if there is no admin for the field description target model
* @throws TooManyAdminClassException if there is too many admin for the field description target model
* @throws AdminCodeNotFoundException if the admin_code option is invalid
*
* @return AdminInterface<object>
*/
public function getAdminByFieldDescription(FieldDescriptionInterface $fieldDescription): AdminInterface
{
$adminCode = $fieldDescription->getOption('admin_code');
if (\is_string($adminCode)) {
return $this->getAdminByAdminCode($adminCode);
}
$targetModel = $fieldDescription->getTargetModel();
if (null === $targetModel) {
throw new \InvalidArgumentException('The field description has no target model.');
}
return $this->getAdminByClass($targetModel);
}
/**
* Returns a new admin instance depends on the given code.
*
* @throws AdminCodeNotFoundException if the code is not found in admin pool
*
* @return AdminInterface<object>
*/
public function getInstance(string $code): AdminInterface
{
if ('' === $code) {
throw new \InvalidArgumentException(
'Admin code must contain a valid admin reference, empty string given.'
);
}
if (!\in_array($code, $this->adminServiceCodes, true)) {
$msg = sprintf('Admin service "%s" not found in admin pool.', $code);
$shortest = -1;
$closest = null;
$alternatives = [];
foreach ($this->adminServiceCodes as $adminServiceCode) {
$lev = levenshtein($code, $adminServiceCode);
if ($lev <= $shortest || $shortest < 0) {
$closest = $adminServiceCode;
$shortest = $lev;
}
if ($lev <= \strlen($adminServiceCode) / 3 || false !== strpos($adminServiceCode, $code)) {
$alternatives[$adminServiceCode] = $lev;
}
}
if (null !== $closest) {
asort($alternatives);
unset($alternatives[$closest]);
$msg = sprintf(
'Admin service "%s" not found in admin pool. Did you mean "%s" or one of those: [%s]?',
$code,
$closest,
implode(', ', array_keys($alternatives))
);
}
throw new AdminCodeNotFoundException($msg);
}
$admin = $this->container->get($code);
if (!$admin instanceof AdminInterface) {
throw new \InvalidArgumentException(sprintf('Found service "%s" is not a valid admin service', $code));
}
return $admin;
}
/**
* @return array<string, array<string, mixed>>
*
* @phpstan-return array<string, Group>
*/
public function getAdminGroups(): array
{
return $this->adminGroups;
}
/**
* @return string[]
*/
public function getAdminServiceCodes(): array
{
return $this->adminServiceCodes;
}
/**
* NEXT_MAJOR: Remove this method.
*
* @deprecated since sonata-project/admin-bundle 4.20 will be removed in 5.0 use getAdminServiceCodes() instead.
*
* @return string[]
*/
public function getAdminServiceIds(): array
{
return $this->adminServiceCodes;
}
/**
* @return array<string, string[]>
*
* @phpstan-return array<class-string, string[]>
*/
public function getAdminClasses(): array
{
return $this->adminClasses;
}
}