vendor/sonata-project/admin-bundle/src/Admin/AbstractAdmin.php line 1827
<?phpdeclare(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 Knp\Menu\ItemInterface;use Sonata\AdminBundle\BCLayer\BCHelper;use Sonata\AdminBundle\Datagrid\DatagridInterface;use Sonata\AdminBundle\Datagrid\DatagridMapper;use Sonata\AdminBundle\Datagrid\ListMapper;use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;use Sonata\AdminBundle\DependencyInjection\Admin\AbstractTaggedAdmin;use Sonata\AdminBundle\Exception\AdminClassNotFoundException;use Sonata\AdminBundle\FieldDescription\FieldDescriptionCollection;use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;use Sonata\AdminBundle\Form\FormMapper;use Sonata\AdminBundle\Form\Type\ModelHiddenType;use Sonata\AdminBundle\Manipulator\ObjectManipulator;use Sonata\AdminBundle\Model\ProxyResolverInterface;use Sonata\AdminBundle\Object\Metadata;use Sonata\AdminBundle\Object\MetadataInterface;use Sonata\AdminBundle\Route\RouteCollection;use Sonata\AdminBundle\Route\RouteCollectionInterface;use Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap;use Sonata\AdminBundle\Security\Handler\AclSecurityHandlerInterface;use Sonata\AdminBundle\Show\ShowMapper;use Sonata\AdminBundle\Util\Instantiator;use Sonata\AdminBundle\Util\ParametersManipulator;use Symfony\Component\Form\Extension\Core\Type\HiddenType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\Form\FormEvent;use Symfony\Component\Form\FormEvents;use Symfony\Component\Form\FormInterface;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;use Symfony\Component\PropertyAccess\PropertyAccess;use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface;use Symfony\Component\Security\Acl\Model\DomainObjectInterface;use Symfony\Component\Security\Core\Exception\AccessDeniedException;/*** @author Thomas Rabaix <thomas.rabaix@sonata-project.org>** @phpstan-template T of object* @phpstan-extends AbstractTaggedAdmin<T>* @phpstan-implements AdminInterface<T>*/abstract class AbstractAdmin extends AbstractTaggedAdmin implements AdminInterface, DomainObjectInterface, AdminTreeInterface{// NEXT_MAJOR: Remove the CONTEXT constants./** @deprecated */public const CONTEXT_MENU = 'menu';/** @deprecated */public const CONTEXT_DASHBOARD = 'dashboard';public const CLASS_REGEX ='@(?:([A-Za-z0-9]*)\\\)? # vendor name / app name(Bundle\\\)? # optional bundle directory([A-Za-z0-9]+?)(?:Bundle)?\\\ # bundle name, with optional suffix(Entity|Document|Model|PHPCR|CouchDocument|Phpcr|Doctrine\\\Orm|Doctrine\\\Phpcr|Doctrine\\\MongoDB|Doctrine\\\CouchDB)\\\(.*)@x';private const ACTION_TREE = 1;private const ACTION_SHOW = 2;private const ACTION_EDIT = 4;private const ACTION_DELETE = 8;private const ACTION_ACL = 16;private const ACTION_HISTORY = 32;private const ACTION_LIST = 64;private const ACTION_BATCH = 128;private const INTERNAL_ACTIONS = ['tree' => self::ACTION_TREE,'show' => self::ACTION_SHOW,'edit' => self::ACTION_EDIT,'delete' => self::ACTION_DELETE,'acl' => self::ACTION_ACL,'history' => self::ACTION_HISTORY,'list' => self::ACTION_LIST,'batch' => self::ACTION_BATCH,];private const MASK_OF_ACTION_CREATE = self::ACTION_TREE | self::ACTION_SHOW | self::ACTION_EDIT | self::ACTION_DELETE | self::ACTION_LIST | self::ACTION_BATCH;private const MASK_OF_ACTION_SHOW = self::ACTION_EDIT | self::ACTION_HISTORY | self::ACTION_ACL;private const MASK_OF_ACTION_EDIT = self::ACTION_SHOW | self::ACTION_DELETE | self::ACTION_ACL | self::ACTION_HISTORY;private const MASK_OF_ACTION_HISTORY = self::ACTION_SHOW | self::ACTION_EDIT | self::ACTION_ACL;private const MASK_OF_ACTION_ACL = self::ACTION_EDIT | self::ACTION_HISTORY;private const MASK_OF_ACTION_LIST = self::ACTION_SHOW | self::ACTION_EDIT | self::ACTION_DELETE | self::ACTION_ACL | self::ACTION_BATCH;private const MASK_OF_ACTIONS_USING_OBJECT = self::MASK_OF_ACTION_SHOW | self::MASK_OF_ACTION_EDIT | self::MASK_OF_ACTION_HISTORY | self::MASK_OF_ACTION_ACL;private const DEFAULT_LIST_PER_PAGE_RESULTS = 25;private const DEFAULT_LIST_PER_PAGE_OPTIONS = [10, 25, 50, 100, 250];/*** @deprecated since sonata-project/admin-bundle 4.15, will be removed in 5.0.** The base route name used to generate the routing information.** @var string|null*/protected $baseRouteName;/*** @deprecated since sonata-project/admin-bundle 4.15, will be removed in 5.0.** The base route pattern used to generate the routing information.** @var string|null*/protected $baseRoutePattern;/*** The label class name (used in the title/breadcrumb ...).** @var string|null*/protected $classnameLabel;/*** Setting to true will enable preview mode for* the entity and show a preview button in the* edit/create forms.** @var bool*/protected $supportsPreviewMode = false;/*** The list FieldDescription constructed from the configureListField method.** @var array<string, FieldDescriptionInterface>*/private array $listFieldDescriptions = [];/*** The show FieldDescription constructed from the configureShowFields method.** @var FieldDescriptionInterface[]*/private array $showFieldDescriptions = [];/*** The list FieldDescription constructed from the configureFormField method.** @var FieldDescriptionInterface[]*/private array $formFieldDescriptions = [];/*** The filter FieldDescription constructed from the configureFilterField method.** @var FieldDescriptionInterface[]*/private array $filterFieldDescriptions = [];/*** The maximum number of page numbers to display in the list.*/private int $maxPageLinks = 25;/*** The translation domain to be used to translate messages.*/private string $translationDomain = 'messages';/*** Array of routes related to this admin.*/private ?RouteCollectionInterface $routes = null;/*** The subject only set in edit/update/create mode.** @phpstan-var T|null*/private ?object $subject = null;/*** Define a Collection of child admin, ie /admin/order/{id}/order-element/{childId}.** @var array<string, AdminInterface<object>>*/private array $children = [];/*** Reference the parent admin.** @var AdminInterface<object>|null*/private ?AdminInterface $parent = null;/*** Reference the parent FieldDescription related to this admin* only set for FieldDescription which is associated to an Sub Admin instance.*/private ?FieldDescriptionInterface $parentFieldDescription = null;/*** If true then the current admin is part of the nested admin set (from the url).*/private bool $currentChild = false;/*** The uniqId is used to avoid clashing with 2 admin related to the code* ie: a Block linked to a Block.*/private ?string $uniqId = null;/*** The current request object.*/private ?Request $request = null;/*** @phpstan-var DatagridInterface<ProxyQueryInterface<T>>|null*/private ?DatagridInterface $datagrid = null;private ?ItemInterface $menu = null;/*** @var string[]*/private array $formTheme = [];/*** @var string[]*/private array $filterTheme = [];/*** @var AdminExtensionInterface[]** @phpstan-var array<AdminExtensionInterface<T>>*/private array $extensions = [];/*** @var array<string, bool>*/private array $cacheIsGranted = [];/*** @var array<string, string|null>*/private array $parentAssociationMapping = [];/*** The subclasses supported by the admin class.** @var string[]** @phpstan-var array<string, class-string<T>>*/private array $subClasses = [];/*** The list collection.** @var FieldDescriptionCollection<FieldDescriptionInterface>|null*/private ?FieldDescriptionCollection $list = null;/*** @var FieldDescriptionCollection<FieldDescriptionInterface>|null*/private ?FieldDescriptionCollection $show = null;private ?FormInterface $form = null;/*** The cached base route name.*/private ?string $cachedBaseRouteName = null;/*** The cached base route pattern.*/private ?string $cachedBaseRoutePattern = null;/*** The form group disposition.** @var array<string, array<string, mixed>>*/private array $formGroups = [];/*** The form tabs disposition.** @var array<string, array<string, mixed>>*/private array $formTabs = [];/*** The view group disposition.** @var array<string, array<string, mixed>>*/private array $showGroups = [];/*** The view tab disposition.** @var array<string, array<string, mixed>>*/private array $showTabs = [];/*** @var array<string, bool>*/private array $loaded = ['routes' => false,'tab_menu' => false,'show' => false,'list' => false,'form' => false,'datagrid' => false,];public function getExportFormats(): array{return [];}final public function getExportFields(): array{$fields = $this->configureExportFields();foreach ($this->getExtensions() as $extension) {$fields = $extension->configureExportFields($this, $fields);}return $fields;}final public function getDataSourceIterator(): \Iterator{$datagrid = $this->getDatagrid();$datagrid->buildPager();$fields = [];foreach ($this->getExportFields() as $key => $field) {if (!\is_string($key)) {$label = $this->getTranslationLabel($field, 'export', 'label');$key = $this->getTranslator()->trans($label, [], $this->getTranslationDomain());}$fields[$key] = $field;}$query = $datagrid->getQuery();return $this->getDataSource()->createIterator($query, $fields);}final public function initialize(): void{if (null === $this->classnameLabel) {$namespaceSeparatorPos = strrpos($this->getClass(), '\\');$this->classnameLabel = false !== $namespaceSeparatorPos? substr($this->getClass(), $namespaceSeparatorPos + 1): $this->getClass();}$this->configure();foreach ($this->getExtensions() as $extension) {$extension->configure($this);}}final public function update(object $object): object{$this->preUpdate($object);foreach ($this->getExtensions() as $extension) {$extension->preUpdate($this, $object);}$this->getModelManager()->update($object);$this->postUpdate($object);foreach ($this->getExtensions() as $extension) {$extension->postUpdate($this, $object);}return $object;}final public function create(object $object): object{$this->prePersist($object);foreach ($this->getExtensions() as $extension) {$extension->prePersist($this, $object);}$this->getModelManager()->create($object);$this->postPersist($object);foreach ($this->getExtensions() as $extension) {$extension->postPersist($this, $object);}$this->createObjectSecurity($object);return $object;}final public function delete(object $object): void{$this->preRemove($object);foreach ($this->getExtensions() as $extension) {$extension->preRemove($this, $object);}$this->getSecurityHandler()->deleteObjectSecurity($this, $object);$this->getModelManager()->delete($object);$this->postRemove($object);foreach ($this->getExtensions() as $extension) {$extension->postRemove($this, $object);}}public function preBatchAction(string $actionName, ProxyQueryInterface $query, array &$idx, bool $allElements = false): void{}final public function getDefaultFilterParameters(): array{return array_merge($this->getDefaultSortValues(),$this->getDefaultFilterValues());}final public function getFilterParameters(): array{$parameters = $this->getDefaultFilterParameters();// build the values arrayif ($this->hasRequest()) {$bag = $this->getRequest()->query;$filters = $bag->all('filter');if (isset($filters[DatagridInterface::PAGE])) {$filters[DatagridInterface::PAGE] = (int) $filters[DatagridInterface::PAGE];}if (isset($filters[DatagridInterface::PER_PAGE])) {$filters[DatagridInterface::PER_PAGE] = (int) $filters[DatagridInterface::PER_PAGE];}// if filter persistence is configuredif ($this->hasFilterPersister()) {// if reset filters is asked, remove from storageif ('reset' === $this->getRequest()->query->get('filters')) {$this->getFilterPersister()->reset($this->getCode());}// if no filters, fetch from storage// otherwise save to storageif ([] === $filters) {$filters = $this->getFilterPersister()->get($this->getCode());} else {$this->getFilterPersister()->set($this->getCode(), $filters);}}$parameters = ParametersManipulator::merge($parameters, $filters);// always force the parent valueif ($this->isChild()) {$parentAssociationMapping = $this->getParentAssociationMapping();if (null !== $parentAssociationMapping) {$name = str_replace('.', '__', $parentAssociationMapping);$parameters[$name] = ['value' => $this->getRequest()->get($this->getParent()->getIdParameter())];}}}if (!isset($parameters[DatagridInterface::PER_PAGE])|| !\is_int($parameters[DatagridInterface::PER_PAGE])|| !$this->determinedPerPageValue($parameters[DatagridInterface::PER_PAGE])) {$parameters[DatagridInterface::PER_PAGE] = $this->getMaxPerPage();}$parameters = $this->configureFilterParameters($parameters);foreach ($this->getExtensions() as $extension) {$parameters = $extension->configureFilterParameters($this, $parameters);}return $parameters;}/*** Returns the name of the parent related field, so the field can be use to set the default* value (ie the parent object) or to filter the object.** @throws \LogicException*/final public function getParentAssociationMapping(): ?string{if (!$this->isChild()) {throw new \LogicException(sprintf('Admin "%s" has no parent.',static::class));}$parent = $this->getParent()->getCode();return $this->parentAssociationMapping[$parent];}final public function getBaseRoutePattern(): string{if (null !== $this->cachedBaseRoutePattern) {return $this->cachedBaseRoutePattern;}if ($this->isChild()) { // the admin class is a child, prefix it with the parent route pattern$this->cachedBaseRoutePattern = sprintf('%s/%s/%s',$this->getParent()->getBaseRoutePattern(),$this->getParent()->getRouterIdParameter(),$this->generateBaseRoutePattern(true));} else {$this->cachedBaseRoutePattern = $this->generateBaseRoutePattern();}return $this->cachedBaseRoutePattern;}/*** Returns the baseRouteName used to generate the routing information.** @return string the baseRouteName used to generate the routing information*/final public function getBaseRouteName(): string{if (null !== $this->cachedBaseRouteName) {return $this->cachedBaseRouteName;}if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name$this->cachedBaseRouteName = sprintf('%s_%s',$this->getParent()->getBaseRouteName(),$this->generateBaseRouteName(true));} else {$this->cachedBaseRouteName = $this->generateBaseRouteName();}return $this->cachedBaseRouteName;}final public function getClass(): string{if ($this->hasActiveSubClass()) {if ($this->hasParentFieldDescription()) {throw new \LogicException('Feature not implemented: an embedded admin cannot have subclass');}$subClass = $this->getRequest()->query->get('subclass');\assert(\is_string($subClass));if (!$this->hasSubClass($subClass)) {throw new \LogicException(sprintf('Subclass "%s" is not defined.', $subClass));}return $this->getSubClass($subClass);}// Do not use `$this->hasSubject()` and `$this->getSubject()` here to avoid infinite loop.// `getSubject` use `hasSubject()` which use `getObject()` which use `getClass()`.if (null !== $this->subject) {$modelManager = $this->getModelManager();/** @phpstan-var class-string<T> $class */$class = $modelManager instanceof ProxyResolverInterface? $modelManager->getRealClass($this->subject)// NEXT_MAJOR: Change to `\get_class($this->subject)` instead: BCHelper::getClass($this->subject);return $class;}return $this->getModelClass();}final public function getSubClasses(): array{return $this->subClasses;}final public function setSubClasses(array $subClasses): void{$this->subClasses = $subClasses;}final public function hasSubClass(string $name): bool{return isset($this->subClasses[$name]);}final public function hasActiveSubClass(): bool{if (\count($this->subClasses) > 0 && $this->hasRequest()) {return \is_string($this->getRequest()->query->get('subclass'));}return false;}final public function getActiveSubClass(): string{if (!$this->hasActiveSubClass()) {throw new \LogicException(sprintf('Admin "%s" has no active subclass.',static::class));}return $this->getSubClass($this->getActiveSubclassCode());}final public function getActiveSubclassCode(): string{if (!$this->hasActiveSubClass()) {throw new \LogicException(sprintf('Admin "%s" has no active subclass.',static::class));}$subClass = (string) $this->getRequest()->query->get('subclass');if (!$this->hasSubClass($subClass)) {throw new \LogicException(sprintf('Admin "%s" has no active subclass.',static::class));}return $subClass;}final public function getBatchActions(): array{if (!$this->hasRoute('batch')) {return [];}$actions = [];if ($this->hasRoute('delete') && $this->hasAccess('delete')) {$actions['delete'] = ['label' => 'action_delete','translation_domain' => 'SonataAdminBundle','ask_confirmation' => true, // by default always true];}$actions = $this->configureBatchActions($actions);foreach ($this->getExtensions() as $extension) {$actions = $extension->configureBatchActions($this, $actions);}foreach ($actions as $name => &$action) {if (!\array_key_exists('label', $action)) {$action['label'] = $this->getTranslationLabel($name, 'batch', 'label');}if (!\array_key_exists('translation_domain', $action)) {$action['translation_domain'] = $this->getTranslationDomain();}}return $actions;}final public function getRoutes(): RouteCollectionInterface{$routes = $this->buildRoutes();if (null === $routes) {throw new \LogicException('Cannot access routes during the building process.');}return $routes;}public function getRouterIdParameter(): string{return sprintf('{%s}', $this->getIdParameter());}public function getIdParameter(): string{$parameter = 'id';for ($i = 0; $i < $this->getChildDepth(); ++$i) {$parameter = sprintf('child%s', ucfirst($parameter));}return $parameter;}final public function hasRoute(string $name): bool{return $this->getRouteGenerator()->hasAdminRoute($this, $name);}final public function isCurrentRoute(string $name, ?string $adminCode = null): bool{if (!$this->hasRequest()) {return false;}$request = $this->getRequest();$route = $request->get('_route');if (null !== $adminCode) {$pool = $this->getConfigurationPool();if ($pool->hasAdminByAdminCode($adminCode)) {$admin = $pool->getAdminByAdminCode($adminCode);} else {return false;}} else {$admin = $this;}return $admin->getRoutes()->getRouteName($name) === $route;}final public function generateObjectUrl(string $name, object $object, array $parameters = [], int $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH): string{$parameters[$this->getIdParameter()] = $this->getUrlSafeIdentifier($object);return $this->generateUrl($name, $parameters, $referenceType);}final public function generateUrl(string $name, array $parameters = [], int $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH): string{return $this->getRouteGenerator()->generateUrl($this, $name, $parameters, $referenceType);}final public function generateMenuUrl(string $name, array $parameters = [], int $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH): array{return $this->getRouteGenerator()->generateMenuUrl($this, $name, $parameters, $referenceType);}final public function getNewInstance(): object{$object = $this->createNewInstance();$this->alterNewInstance($object);foreach ($this->getExtensions() as $extension) {$extension->alterNewInstance($this, $object);}return $object;}final public function getFormBuilder(): FormBuilderInterface{$formBuilder = $this->getFormContractor()->getFormBuilder($this->getUniqId(),['data_class' => $this->getClass()] + $this->getFormOptions(),);$this->defineFormBuilder($formBuilder);return $formBuilder;}/*** This method is being called by the main admin class and the child class,* the getFormBuilder is only call by the main admin class.*/final public function defineFormBuilder(FormBuilderInterface $formBuilder): void{if (!$this->hasSubject()) {throw new \LogicException(sprintf('Admin "%s" has no subject.',static::class));}$mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);$this->configureFormFields($mapper);foreach ($this->getExtensions() as $extension) {$extension->configureFormFields($mapper);}}final public function attachAdminClass(FieldDescriptionInterface $fieldDescription): void{$pool = $this->getConfigurationPool();try {$admin = $pool->getAdminByFieldDescription($fieldDescription);} catch (AdminClassNotFoundException) {// Using a fieldDescription with no admin class for the target model is a valid case.// Since there is no easy way to check for this case, we catch the exception instead.return;}if ($this->hasRequest()) {$admin->setRequest($this->getRequest());}$fieldDescription->setAssociationAdmin($admin);}/*** @param string|int|null $id** @phpstan-return T|null*/final public function getObject($id): ?object{if (null === $id) {return null;}$object = $this->getModelManager()->find($this->getClass(), $id);if (null === $object) {return null;}$this->alterObject($object);foreach ($this->getExtensions() as $extension) {$extension->alterObject($this, $object);}return $object;}final public function getForm(): FormInterface{$form = $this->buildForm();if (null === $form) {throw new \LogicException('Cannot access form during the building process.');}return $form;}final public function getList(): FieldDescriptionCollection{$list = $this->buildList();if (null === $list) {throw new \LogicException('Cannot access list during the building process.');}return $list;}final public function createQuery(): ProxyQueryInterface{$query = $this->getModelManager()->createQuery($this->getClass());$query = $this->configureQuery($query);foreach ($this->getExtensions() as $extension) {$extension->configureQuery($this, $query);}return $query;}final public function getDatagrid(): DatagridInterface{$datagrid = $this->buildDatagrid();if (null === $datagrid) {throw new \LogicException('Cannot access datagrid during the building process.');}return $datagrid;}final public function getSideMenu(string $action, ?AdminInterface $childAdmin = null): ItemInterface{if ($this->isChild()) {return $this->getParent()->getSideMenu($action, $this);}$menu = $this->buildTabMenu($action, $childAdmin);if (null === $menu) {throw new \LogicException('Cannot access menu during the building process.');}return $menu;}final public function getRootCode(): string{return $this->getRoot()->getCode();}final public function getRoot(): AdminInterface{if (!$this->hasParentFieldDescription()) {return $this;}return $this->getParentFieldDescription()->getAdmin()->getRoot();}final public function getMaxPerPage(): int{$sortValues = $this->getDefaultSortValues();return $sortValues[DatagridInterface::PER_PAGE] ?? self::DEFAULT_LIST_PER_PAGE_RESULTS;}final public function setMaxPageLinks(int $maxPageLinks): void{$this->maxPageLinks = $maxPageLinks;}final public function getMaxPageLinks(): int{return $this->maxPageLinks;}final public function getFormGroups(): array{return $this->formGroups;}final public function setFormGroups(array $formGroups): void{$this->formGroups = $formGroups;}final public function removeFieldFromFormGroup(string $key): void{foreach ($this->formGroups as $name => $_formGroup) {unset($this->formGroups[$name]['fields'][$key]);if ([] === $this->formGroups[$name]['fields']) {unset($this->formGroups[$name]);}}}final public function reorderFormGroup(string $group, array $keys): void{$formGroups = $this->getFormGroups();$formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);$this->setFormGroups($formGroups);}final public function getFormTabs(): array{return $this->formTabs;}final public function setFormTabs(array $formTabs): void{$this->formTabs = $formTabs;}final public function getShowTabs(): array{return $this->showTabs;}final public function setShowTabs(array $showTabs): void{$this->showTabs = $showTabs;}final public function getShowGroups(): array{return $this->showGroups;}final public function setShowGroups(array $showGroups): void{$this->showGroups = $showGroups;}final public function removeFieldFromShowGroup(string $key): void{foreach ($this->showGroups as $name => $_showGroup) {unset($this->showGroups[$name]['fields'][$key]);if ([] === $this->showGroups[$name]['fields']) {unset($this->showGroups[$name]);}}}final public function reorderShowGroup(string $group, array $keys): void{$showGroups = $this->getShowGroups();$showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);$this->setShowGroups($showGroups);}final public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription): void{$this->parentFieldDescription = $parentFieldDescription;}final public function getParentFieldDescription(): FieldDescriptionInterface{if (!$this->hasParentFieldDescription()) {throw new \LogicException(sprintf('Admin "%s" has no parent field description.',static::class));}return $this->parentFieldDescription;}/*** @phpstan-assert-if-true !null $this->parentFieldDescription*/final public function hasParentFieldDescription(): bool{return null !== $this->parentFieldDescription;}final public function setSubject(?object $subject): void{if (null !== $subject && !is_a($subject, $this->getModelClass(), true)) {throw new \LogicException(sprintf('Admin "%s" does not allow this subject: %s, use the one register with this admin class %s',static::class,$subject::class,$this->getModelClass()));}$this->subject = $subject;}final public function getSubject(): object{if (!$this->hasSubject()) {throw new \LogicException(sprintf('Admin "%s" has no subject.',static::class));}return $this->subject;}/*** @phpstan-assert-if-true !null $this->subject*/final public function hasSubject(): bool{if (null === $this->subject && $this->hasRequest() && !$this->hasParentFieldDescription()) {$id = $this->getRequest()->get($this->getIdParameter());if (null !== $id) {$this->subject = $this->getObject($id);}}return null !== $this->subject;}final public function getFormFieldDescriptions(): array{$this->buildForm();return $this->formFieldDescriptions;}final public function getFormFieldDescription(string $name): FieldDescriptionInterface{$this->buildForm();if (!$this->hasFormFieldDescription($name)) {throw new \LogicException(sprintf('Admin "%s" has no form field description for the field %s.',static::class,$name));}return $this->formFieldDescriptions[$name];}/*** Returns true if the admin has a FieldDescription with the given $name.*/final public function hasFormFieldDescription(string $name): bool{$this->buildForm();return \array_key_exists($name, $this->formFieldDescriptions);}final public function addFormFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void{$this->formFieldDescriptions[$name] = $fieldDescription;}/*** remove a FieldDescription.*/final public function removeFormFieldDescription(string $name): void{unset($this->formFieldDescriptions[$name]);}/*** build and return the collection of form FieldDescription.** @return FieldDescriptionInterface[] collection of form FieldDescription*/final public function getShowFieldDescriptions(): array{$this->buildShow();return $this->showFieldDescriptions;}/*** Returns the form FieldDescription with the given $name.*/final public function getShowFieldDescription(string $name): FieldDescriptionInterface{$this->buildShow();if (!$this->hasShowFieldDescription($name)) {throw new \LogicException(sprintf('Admin "%s" has no show field description for the field %s.',static::class,$name));}return $this->showFieldDescriptions[$name];}final public function hasShowFieldDescription(string $name): bool{$this->buildShow();return \array_key_exists($name, $this->showFieldDescriptions);}final public function addShowFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void{$this->showFieldDescriptions[$name] = $fieldDescription;}final public function removeShowFieldDescription(string $name): void{unset($this->showFieldDescriptions[$name]);}final public function getListFieldDescriptions(): array{$this->buildList();return $this->listFieldDescriptions;}final public function getListFieldDescription(string $name): FieldDescriptionInterface{$this->buildList();if (!$this->hasListFieldDescription($name)) {throw new \LogicException(sprintf('Admin "%s" has no list field description for %s.',static::class,$name));}return $this->listFieldDescriptions[$name];}final public function hasListFieldDescription(string $name): bool{$this->buildList();return \array_key_exists($name, $this->listFieldDescriptions);}final public function addListFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void{$this->listFieldDescriptions[$name] = $fieldDescription;}final public function removeListFieldDescription(string $name): void{unset($this->listFieldDescriptions[$name]);}final public function getFilterFieldDescription(string $name): FieldDescriptionInterface{$this->buildDatagrid();if (!$this->hasFilterFieldDescription($name)) {throw new \LogicException(sprintf('Admin "%s" has no filter field description for the field %s.',static::class,$name));}return $this->filterFieldDescriptions[$name];}final public function hasFilterFieldDescription(string $name): bool{$this->buildDatagrid();return \array_key_exists($name, $this->filterFieldDescriptions);}final public function addFilterFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void{$this->filterFieldDescriptions[$name] = $fieldDescription;}final public function removeFilterFieldDescription(string $name): void{unset($this->filterFieldDescriptions[$name]);}final public function getFilterFieldDescriptions(): array{$this->buildDatagrid();return $this->filterFieldDescriptions;}/*** @psalm-suppress PossiblyNullArgument Will be solved in NEXT_MAJOR*/final public function addChild(AdminInterface $child, ?string $field = null): void{$parentAdmin = $this;while ($parentAdmin->isChild() && $parentAdmin->getCode() !== $child->getCode()) {$parentAdmin = $parentAdmin->getParent();}if ($parentAdmin->getCode() === $child->getCode()) {throw new \LogicException(sprintf('Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',$child->getCode(),$this->getCode()));}$this->children[$child->getCode()] = $child;// @phpstan-ignore-next-line Will be solved in NEXT_MAJOR$child->setParent($this, $field);}final public function hasChild(string $code): bool{return isset($this->children[$code]);}final public function getChildren(): array{return $this->children;}final public function getChild(string $code): AdminInterface{if (!$this->hasChild($code)) {throw new \LogicException(sprintf('Admin "%s" has no child for the code %s.',static::class,$code));}return $this->getChildren()[$code];}final public function setParent(AdminInterface $parent, ?string $parentAssociationMapping = null): void{$this->parent = $parent;$this->parentAssociationMapping[$parent->getCode()] = $parentAssociationMapping;}final public function getParent(): AdminInterface{if (null === $this->parent) {throw new \LogicException(sprintf('Admin "%s" has no parent.',static::class));}return $this->parent;}final public function getRootAncestor(): AdminInterface{$parent = $this;while ($parent->isChild()) {$parent = $parent->getParent();}return $parent;}final public function getChildDepth(): int{$parent = $this;$depth = 0;while ($parent->isChild()) {$parent = $parent->getParent();++$depth;}return $depth;}final public function getCurrentLeafChildAdmin(): ?AdminInterface{$child = $this->getCurrentChildAdmin();if (null === $child) {return null;}for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {$child = $c;}return $child;}final public function isChild(): bool{return $this->parent instanceof AdminInterface;}/*** Returns true if the admin has children, false otherwise.** @phpstan-assert-if-true non-empty-array $this->children*/final public function hasChildren(): bool{return \count($this->children) > 0;}final public function setUniqId(string $uniqId): void{$this->uniqId = $uniqId;}final public function getUniqId(): string{if (null === $this->uniqId) {$this->uniqId = sprintf('s%s', uniqid());}return $this->uniqId;}final public function getClassnameLabel(): string{if (null === $this->classnameLabel) {throw new \LogicException(sprintf('Admin "%s" has no classname label. Did you forgot to initialize the admin ?',static::class));}return $this->classnameLabel;}final public function getPersistentParameters(): array{$parameters = $this->configurePersistentParameters();foreach ($this->getExtensions() as $extension) {$parameters = $extension->configurePersistentParameters($this, $parameters);}return $parameters;}final public function getPersistentParameter(string $name, $default = null){$parameters = $this->getPersistentParameters();return $parameters[$name] ?? $default;}final public function setCurrentChild(bool $currentChild): void{$this->currentChild = $currentChild;}final public function isCurrentChild(): bool{return $this->currentChild;}final public function getCurrentChildAdmin(): ?AdminInterface{foreach ($this->getChildren() as $child) {if ($child->isCurrentChild()) {return $child;}}return null;}final public function setTranslationDomain(string $translationDomain): void{$this->translationDomain = $translationDomain;}final public function getTranslationDomain(): string{return $this->translationDomain;}final public function getTranslationLabel(string $label, string $context = '', string $type = ''): string{return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);}final public function setRequest(Request $request): void{$this->request = $request;foreach ($this->getChildren() as $children) {$children->setRequest($request);}}final public function getRequest(): Request{if (!$this->hasRequest()) {throw new \LogicException('The Request object has not been set');}return $this->request;}/*** @phpstan-assert-if-true !null $this->request*/final public function hasRequest(): bool{return null !== $this->request;}final public function getBaseCodeRoute(): string{if ($this->isChild()) {return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();}return $this->getCode();}/*** @return string*/public function getObjectIdentifier(){return $this->getCode();}public function showInDashboard(): bool{// NEXT_MAJOR: Remove those lines and uncomment the last one.$permissionShow = $this->getPermissionsShow(self::CONTEXT_DASHBOARD, 'sonata_deprecation_mute');$permission = 1 === \count($permissionShow) ? reset($permissionShow) : $permissionShow;return $this->isGranted($permission);// return $this->isGranted('LIST');}/*** NEXT_MAJOR: Remove this method.** @deprecated since sonata-project/admin-bundle version 4.7 use showInDashboard instead*/final public function showIn(string $context): bool{if ('sonata_deprecation_mute' !== (\func_get_args()[1] ?? null)) {@trigger_error(sprintf('The "%s()" method is deprecated since sonata-project/admin-bundle version 4.7 and will be'.' removed in 5.0 version. Use showInDashboard() instead.',__METHOD__), \E_USER_DEPRECATED);}$permissionShow = $this->getPermissionsShow($context, 'sonata_deprecation_mute');// Avoid isGranted deprecation if there is only one permission show.$permission = 1 === \count($permissionShow) ? reset($permissionShow) : $permissionShow;return $this->isGranted($permission);}final public function createObjectSecurity(object $object): void{$this->getSecurityHandler()->createObjectSecurity($this, $object);}final public function isGranted($name, ?object $object = null): bool{if (\is_array($name)) {@trigger_error(sprintf('Passing an array as argument 1 of "%s()" is deprecated since sonata-project/admin-bundle 4.6'.' and will throw an error in 5.0. You MUST pass a string instead.',__METHOD__),\E_USER_DEPRECATED);}$objectRef = null !== $object ? sprintf('/%s#%s', spl_object_hash($object), $this->id($object) ?? '') : '';$key = md5(json_encode($name, \JSON_THROW_ON_ERROR).$objectRef);if (!\array_key_exists($key, $this->cacheIsGranted)) {$this->cacheIsGranted[$key] = $this->getSecurityHandler()->isGranted($this, $name, $object ?? $this);}return $this->cacheIsGranted[$key];}final public function getUrlSafeIdentifier(object $model): ?string{return $this->getModelManager()->getUrlSafeIdentifier($model);}final public function getNormalizedIdentifier(object $model): ?string{return $this->getModelManager()->getNormalizedIdentifier($model);}public function id(object $model): ?string{return $this->getNormalizedIdentifier($model);}final public function getShow(): FieldDescriptionCollection{$show = $this->buildShow();if (null === $show) {throw new \LogicException('Cannot access show during the building process.');}return $show;}final public function setFormTheme(array $formTheme): void{$this->formTheme = $formTheme;}final public function getFormTheme(): array{return $this->formTheme;}final public function setFilterTheme(array $filterTheme): void{$this->filterTheme = $filterTheme;}final public function getFilterTheme(): array{return $this->filterTheme;}final public function addExtension(AdminExtensionInterface $extension): void{$this->extensions[] = $extension;}/*** @phpstan-param AdminExtensionInterface<T> $extension*/final public function removeExtension(AdminExtensionInterface $extension): void{$key = array_search($extension, $this->extensions, true);if (false === $key) {throw new \InvalidArgumentException(sprintf('The extension "%s" was not set to the "%s" admin.', $extension::class, self::class));}unset($this->extensions[$key]);}final public function getExtensions(): array{return $this->extensions;}public function toString(object $object): string{if (method_exists($object, '__toString') && null !== $object->__toString()) {return $object->__toString();}$modelManager = $this->getModelManager();if ($modelManager instanceof ProxyResolverInterface) {$class = $modelManager->getRealClass($object);} else {// NEXT_MAJOR: Change to `\get_class($object)`$class = BCHelper::getClass($object);}return sprintf('%s:%s', $class, spl_object_hash($object));}final public function supportsPreviewMode(): bool{return $this->supportsPreviewMode;}/*** Returns predefined per page options.** @return array<int>*/public function getPerPageOptions(): array{$perPageOptions = self::DEFAULT_LIST_PER_PAGE_OPTIONS;$perPageOptions[] = $this->getMaxPerPage();$perPageOptions = array_unique($perPageOptions);sort($perPageOptions);return $perPageOptions;}/*** Returns true if the per page value is allowed, false otherwise.*/final public function determinedPerPageValue(int $perPage): bool{return \in_array($perPage, $this->getPerPageOptions(), true);}final public function isAclEnabled(): bool{return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;}public function getObjectMetadata(object $object): MetadataInterface{return new Metadata($this->toString($object));}final public function setListMode(string $mode): void{$this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);}final public function getListMode(): string{$defaultListMode = array_keys($this->getListModes())[0];if (!$this->hasRequest() || !$this->getRequest()->hasSession()) {return $defaultListMode;}return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), $defaultListMode);}final public function checkAccess(string $action, ?object $object = null): void{$access = $this->getAccess();if (!\array_key_exists($action, $access)) {throw new \InvalidArgumentException(sprintf('Action "%s" could not be found in access mapping.'.' Please make sure your action is defined into your admin class accessMapping property.',$action));}if (!\is_array($access[$action])) {$access[$action] = [$access[$action]];}foreach ($access[$action] as $role) {if (false === $this->isGranted($role, $object)) {throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));}}}final public function hasAccess(string $action, ?object $object = null): bool{$access = $this->getAccess();if (!\array_key_exists($action, $access)) {return false;}if (!\is_array($access[$action])) {$access[$action] = [$access[$action]];}foreach ($access[$action] as $role) {if (false === $this->isGranted($role, $object)) {return false;}}return true;}/*** @return array<string, array<string, mixed>>** @phpstan-param T|null $object*/final public function getActionButtons(string $action, ?object $object = null): array{$defaultButtonList = $this->getDefaultActionButtons($action, $object);$buttonList = $this->configureActionButtons($defaultButtonList, $action, $object);foreach ($this->getExtensions() as $extension) {$buttonList = $extension->configureActionButtons($this, $buttonList, $action, $object);}return $buttonList;}/*** Get the list of actions that can be accessed directly from the dashboard.** @return array<string, array<string, mixed>>*/final public function getDashboardActions(): array{$actions = [];if ($this->hasRoute('create') && $this->hasAccess('create')) {$actions['create'] = ['label' => 'link_add','translation_domain' => 'SonataAdminBundle','template' => $this->getTemplateRegistry()->getTemplate('action_create'),'url' => $this->generateUrl('create'),'icon' => 'fas fa-plus-circle',];}if ($this->hasRoute('list') && $this->hasAccess('list')) {$actions['list'] = ['label' => 'link_list','translation_domain' => 'SonataAdminBundle','url' => $this->generateUrl('list'),'icon' => 'fas fa-list',];}$actions = $this->configureDashboardActions($actions);foreach ($this->getExtensions() as $extension) {$actions = $extension->configureDashboardActions($this, $actions);}return $actions;}final public function createFieldDescription(string $propertyName, array $options = []): FieldDescriptionInterface{$fieldDescriptionFactory = $this->getFieldDescriptionFactory();$fieldDescription = $fieldDescriptionFactory->create($this->getClass(), $propertyName, $options);$fieldDescription->setAdmin($this);return $fieldDescription;}/*** Hook to run after initialization.*/protected function configure(): void{}protected function generateBaseRoutePattern(bool $isChildAdmin = false): string{// NEXT_MAJOR: Remove this codeif (null !== $this->baseRoutePattern) {@trigger_error(sprintf('Overriding the baseRoutePattern property is deprecated since sonata-project/admin-bundle 4.15.'.' You MUST override the method %s() instead.',__METHOD__), \E_USER_DEPRECATED);return $this->baseRoutePattern;}preg_match(self::CLASS_REGEX, $this->getModelClass(), $matches);if ([] === $matches) {throw new \LogicException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`',static::class));}if ($isChildAdmin) {return $this->urlize($matches[5], '-');}return sprintf('/%s%s/%s','' === $matches[1] ? '' : $this->urlize($matches[1], '-').'/',$this->urlize($matches[3], '-'),$this->urlize($matches[5], '-'));}protected function generateBaseRouteName(bool $isChildAdmin = false): string{// NEXT_MAJOR: Remove this codeif (null !== $this->baseRouteName) {@trigger_error(sprintf('Overriding the baseRouteName property is deprecated since sonata-project/admin-bundle 4.15.'.' You MUST override the method %s() instead.',__METHOD__), \E_USER_DEPRECATED);return $this->baseRouteName;}preg_match(self::CLASS_REGEX, $this->getModelClass(), $matches);if ([] === $matches) {throw new \LogicException(sprintf('Cannot automatically determine base route name,'.' please define a default `baseRouteName` value for the admin class `%s`',static::class));}if ($isChildAdmin) {return $this->urlize($matches[5]);}return sprintf('admin_%s%s_%s','' === $matches[1] ? '' : $this->urlize($matches[1]).'_',$this->urlize($matches[3]),$this->urlize($matches[5]));}/*** @phpstan-return T*/protected function createNewInstance(): object{$object = Instantiator::instantiate($this->getClass());$this->appendParentObject($object);return $object;}/*** @phpstan-param T $object*/protected function alterNewInstance(object $object): void{}/*** @phpstan-param T $object*/protected function alterObject(object $object): void{}/*** @phpstan-param T $object*/protected function preValidate(object $object): void{}/*** @phpstan-param T $object*/protected function preUpdate(object $object): void{}/*** @phpstan-param T $object*/protected function postUpdate(object $object): void{}/*** @phpstan-param T $object*/protected function prePersist(object $object): void{}/*** @phpstan-param T $object*/protected function postPersist(object $object): void{}/*** @phpstan-param T $object*/protected function preRemove(object $object): void{}/*** @phpstan-param T $object*/protected function postRemove(object $object): void{}/*** @return array<string, mixed>*/protected function configurePersistentParameters(): array{return [];}/*** @return string[]*/protected function configureExportFields(): array{return $this->getModelManager()->getExportFields($this->getClass());}/*** @param ProxyQueryInterface<T> $query** @return ProxyQueryInterface<T>*/protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface{return $query;}/*** urlize the given word.** @param string $sep the separator*/final protected function urlize(string $word, string $sep = '_'): string{return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word) ?? '');}/*** @param array<string, mixed> $parameters** @return array<string, mixed>*/protected function configureFilterParameters(array $parameters): array{return $parameters;}/*** Returns a list of default sort values.** @phpstan-return array{* _page?: int,* _per_page?: int,* _sort_by?: string,* _sort_order?: string* }*/final protected function getDefaultSortValues(): array{$defaultSortValues = [DatagridInterface::PAGE => 1, DatagridInterface::PER_PAGE => self::DEFAULT_LIST_PER_PAGE_RESULTS];$this->configureDefaultSortValues($defaultSortValues);foreach ($this->getExtensions() as $extension) {$extension->configureDefaultSortValues($this, $defaultSortValues);}return $defaultSortValues;}/*** Returns a list of default filters.** @return array<string, array<string, mixed>>*/final protected function getDefaultFilterValues(): array{$defaultFilterValues = [];$this->configureDefaultFilterValues($defaultFilterValues);foreach ($this->getExtensions() as $extension) {$extension->configureDefaultFilterValues($this, $defaultFilterValues);}return $defaultFilterValues;}/*** @return array<string, mixed>*/final protected function getFormOptions(): array{$formOptions = [];$this->configureFormOptions($formOptions);foreach ($this->getExtensions() as $extension) {$extension->configureFormOptions($this, $formOptions);}return $formOptions;}/*** @phpstan-param FormMapper<T> $form*/protected function configureFormFields(FormMapper $form): void{}/*** @phpstan-param ListMapper<T> $list*/protected function configureListFields(ListMapper $list): void{}/*** @phpstan-param DatagridMapper<T> $filter*/protected function configureDatagridFilters(DatagridMapper $filter): void{}/*** @phpstan-param ShowMapper<T> $show*/protected function configureShowFields(ShowMapper $show): void{}protected function configureRoutes(RouteCollectionInterface $collection): void{}/*** @param array<string, array<string, mixed>> $buttonList** @return array<string, array<string, mixed>>** @phpstan-param T|null $object*/protected function configureActionButtons(array $buttonList, string $action, ?object $object = null): array{return $buttonList;}/*** @param array<string, array<string, mixed>> $actions** @return array<string, array<string, mixed>>*/protected function configureDashboardActions(array $actions): array{return $actions;}/*** Allows you to customize batch actions.** @param array<string, array<string, mixed>> $actions** @return array<string, array<string, mixed>>*/protected function configureBatchActions(array $actions): array{return $actions;}/*** Configures the tab menu in your admin.** @phpstan-template TChild of object* @phpstan-param AdminInterface<TChild>|null $childAdmin*/protected function configureTabMenu(ItemInterface $menu, string $action, ?AdminInterface $childAdmin = null): void{}/*** Gets the subclass corresponding to the given name.** @phpstan-return class-string<T>*/protected function getSubClass(string $name): string{if ($this->hasSubClass($name)) {return $this->subClasses[$name];}throw new \LogicException(sprintf('Unable to find the subclass `%s` for admin `%s`', $name, static::class));}/*** Return list routes with permissions name.** @return array<string, string|string[]>*/final protected function getAccess(): array{$access = array_merge(['acl' => AdminPermissionMap::PERMISSION_MASTER,'export' => AdminPermissionMap::PERMISSION_EXPORT,'historyCompareRevisions' => AdminPermissionMap::PERMISSION_HISTORY,'historyViewRevision' => AdminPermissionMap::PERMISSION_HISTORY,'history' => AdminPermissionMap::PERMISSION_HISTORY,'edit' => AdminPermissionMap::PERMISSION_EDIT,'show' => AdminPermissionMap::PERMISSION_VIEW,'create' => AdminPermissionMap::PERMISSION_CREATE,'delete' => AdminPermissionMap::PERMISSION_DELETE,'batchDelete' => AdminPermissionMap::PERMISSION_DELETE,'list' => AdminPermissionMap::PERMISSION_LIST,], $this->getAccessMapping());foreach ($this->getExtensions() as $extension) {$access = array_merge($access, $extension->getAccessMapping($this));}return $access;}/*** @return array<string, string|string[]> [action1 => requiredRole1, action2 => [requiredRole2, requiredRole3]]*/protected function getAccessMapping(): array{return [];}/*** Return the list of permissions the user should have in order to display the admin.** NEXT_MAJOR: Remove this method.** @deprecated since sonata-project/admin-bundle version 4.7** @return string[]*/protected function getPermissionsShow(string $context): array{if ('sonata_deprecation_mute' !== (\func_get_args()[1] ?? null)) {@trigger_error(sprintf('The "%s()" method is deprecated since sonata-project/admin-bundle version 4.7 and will be'.' removed in 5.0 version.',__METHOD__), \E_USER_DEPRECATED);}return ['LIST'];}/*** Configures a list of default filters.** @param array<string, array<string, mixed>> $filterValues*/protected function configureDefaultFilterValues(array &$filterValues): void{}/*** Configures a list of form options.** @param array<string, mixed> $formOptions*/protected function configureFormOptions(array &$formOptions): void{}/*** Configures a list of default sort values.** Example:* $sortValues[DatagridInterface::SORT_BY] = 'foo'* $sortValues[DatagridInterface::SORT_ORDER] = 'DESC'** @param array<string, string|int> $sortValues** @phpstan-param array{* _page?: int,* _per_page?: int,* _sort_by?: string,* _sort_order?: string* } $sortValues*/protected function configureDefaultSortValues(array &$sortValues): void{}/*** Set the parent object, if any, to the provided object.** @phpstan-param T $object*/final protected function appendParentObject(object $object): void{if ($this->isChild()) {$parentAssociationMapping = $this->getParentAssociationMapping();if (null !== $parentAssociationMapping) {$parentAdmin = $this->getParent();$parentObject = $parentAdmin->getObject($this->getRequest()->get($parentAdmin->getIdParameter()));if (null !== $parentObject) {$propertyAccessor = PropertyAccess::createPropertyAccessor();try {$value = $propertyAccessor->getValue($object, $parentAssociationMapping);} catch (UninitializedPropertyException) {$value = null;}if (\is_array($value) || $value instanceof \ArrayAccess) {$value[] = $parentObject;$propertyAccessor->setValue($object, $parentAssociationMapping, $value);} else {$propertyAccessor->setValue($object, $parentAssociationMapping, $parentObject);}}return;}}if ($this->hasParentFieldDescription()) {$parentAdmin = $this->getParentFieldDescription()->getAdmin();$parentObject = $parentAdmin->getObject($this->getRequest()->get($parentAdmin->getIdParameter()));if (null !== $parentObject) {ObjectManipulator::setObject($object, $parentObject, $this->getParentFieldDescription());}}}/*** @return array<string, array<string, mixed>>** @phpstan-param T|null $object*/private function getDefaultActionButtons(string $action, ?object $object = null): array{// nothing to do for non-internal actionsif (!isset(self::INTERNAL_ACTIONS[$action])) {return [];}$buttonList = [];$actionBit = self::INTERNAL_ACTIONS[$action];if (0 !== (self::MASK_OF_ACTION_CREATE & $actionBit)&& $this->hasRoute('create')&& $this->hasAccess('create')) {$buttonList['create'] = ['template' => $this->getTemplateRegistry()->getTemplate('button_create'),];}$canAccessObject = 0 !== (self::MASK_OF_ACTIONS_USING_OBJECT & $actionBit)&& null !== $object&& null !== $this->id($object);if ($canAccessObject&& 0 !== (self::MASK_OF_ACTION_EDIT & $actionBit)&& $this->hasRoute('edit')&& $this->hasAccess('edit', $object)) {$buttonList['edit'] = ['template' => $this->getTemplateRegistry()->getTemplate('button_edit'),];}if ($canAccessObject&& 0 !== (self::MASK_OF_ACTION_HISTORY & $actionBit)&& $this->hasRoute('history')&& $this->hasAccess('history', $object)) {$buttonList['history'] = ['template' => $this->getTemplateRegistry()->getTemplate('button_history'),];}if ($canAccessObject&& 0 !== (self::MASK_OF_ACTION_ACL & $actionBit)&& $this->isAclEnabled()&& $this->hasRoute('acl')&& $this->hasAccess('acl', $object)) {$buttonList['acl'] = ['template' => $this->getTemplateRegistry()->getTemplate('button_acl'),];}if ($canAccessObject&& 0 !== (self::MASK_OF_ACTION_SHOW & $actionBit)&& $this->hasRoute('show')&& $this->hasAccess('show', $object)&& \count($this->getShow()) > 0) {$buttonList['show'] = ['template' => $this->getTemplateRegistry()->getTemplate('button_show'),];}if (0 !== (self::MASK_OF_ACTION_LIST & $actionBit)&& $this->hasRoute('list')&& $this->hasAccess('list')) {$buttonList['list'] = ['template' => $this->getTemplateRegistry()->getTemplate('button_list'),];}return $buttonList;}/*** @return DatagridInterface<ProxyQueryInterface<T>>|null*/private function buildDatagrid(): ?DatagridInterface{if ($this->loaded['datagrid']) {return $this->datagrid;}$this->loaded['datagrid'] = true;$filterParameters = $this->getFilterParameters();// transform DatagridInterface::SORT_BY filter parameter from a string to a FieldDescriptionInterface for the datagrid.if (isset($filterParameters[DatagridInterface::SORT_BY]) && \is_string($filterParameters[DatagridInterface::SORT_BY])) {if ($this->hasListFieldDescription($filterParameters[DatagridInterface::SORT_BY])) {$filterParameters[DatagridInterface::SORT_BY] = $this->getListFieldDescription($filterParameters[DatagridInterface::SORT_BY]);} else {$filterParameters[DatagridInterface::SORT_BY] = $this->createFieldDescription($filterParameters[DatagridInterface::SORT_BY]);$this->getListBuilder()->buildField(null, $filterParameters[DatagridInterface::SORT_BY]);}}// initialize the datagrid$this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);$this->datagrid->getPager()->setMaxPageLinks($this->getMaxPageLinks());/** @psalm-suppress InvalidArgument https://github.com/vimeo/psalm/issues/8423 */$mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);// build the datagrid filter$this->configureDatagridFilters($mapper);// ok, try to limit to add parent filterif ($this->isChild()) {$parentAssociationMapping = $this->getParentAssociationMapping();if (null !== $parentAssociationMapping && !$mapper->has($parentAssociationMapping)) {$mapper->add($parentAssociationMapping, null, ['show_filter' => false,'label' => false,'field_type' => ModelHiddenType::class,'field_options' => ['model_manager' => $this->getParent()->getModelManager(),'class' => $this->getParent()->getClass(),],'operator_type' => HiddenType::class,], ['admin_code' => $this->getParent()->getCode(),]);}}foreach ($this->getExtensions() as $extension) {$extension->configureDatagridFilters($mapper);}return $this->datagrid;}/*** @return FieldDescriptionCollection<FieldDescriptionInterface>|null*/private function buildShow(): ?FieldDescriptionCollection{if ($this->loaded['show']) {return $this->show;}$this->loaded['show'] = true;$this->show = $this->getShowBuilder()->getBaseList();$mapper = new ShowMapper($this->getShowBuilder(), $this->show, $this);$this->configureShowFields($mapper);foreach ($this->getExtensions() as $extension) {$extension->configureShowFields($mapper);}return $this->show;}/*** @return FieldDescriptionCollection<FieldDescriptionInterface>|null*/private function buildList(): ?FieldDescriptionCollection{if ($this->loaded['list']) {return $this->list;}$this->loaded['list'] = true;$this->list = $this->getListBuilder()->getBaseList();$mapper = new ListMapper($this->getListBuilder(), $this->list, $this);if (\count($this->getBatchActions()) > 0 && $this->hasRequest() && !$this->getRequest()->isXmlHttpRequest()) {$mapper->add(ListMapper::NAME_BATCH, ListMapper::TYPE_BATCH, ['label' => 'batch','sortable' => false,'virtual_field' => true,'template' => $this->getTemplateRegistry()->getTemplate('batch'),]);}$this->configureListFields($mapper);foreach ($this->getExtensions() as $extension) {$extension->configureListFields($mapper);}if ($this->hasRequest()&& $this->getRequest()->isXmlHttpRequest()&& $this->getRequest()->query->getBoolean('select', true) // NEXT_MAJOR: Change the default value to `false` in version 5) {$mapper->add(ListMapper::NAME_SELECT, ListMapper::TYPE_SELECT, ['label' => false,'sortable' => false,'virtual_field' => false,'template' => $this->getTemplateRegistry()->getTemplate('select'),]);}return $this->list;}private function buildForm(): ?FormInterface{if ($this->loaded['form']) {return $this->form;}$this->loaded['form'] = true;$formBuilder = $this->getFormBuilder();$formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event): void {/** @phpstan-var T $data */$data = $event->getData();$this->preValidate($data);}, 100);$this->form = $formBuilder->getForm();return $this->form;}private function buildRoutes(): ?RouteCollectionInterface{if ($this->loaded['routes']) {return $this->routes;}$this->loaded['routes'] = true;$routes = new RouteCollection($this->getBaseCodeRoute(),$this->getBaseRouteName(),$this->getBaseRoutePattern(),$this->getBaseControllerName());$this->getRouteBuilder()->build($this, $routes);$this->configureRoutes($routes);foreach ($this->getExtensions() as $extension) {$extension->configureRoutes($this, $routes);}$this->routes = $routes;return $this->routes;}/*** @phpstan-template TChild of object* @phpstan-param AdminInterface<TChild>|null $childAdmin*/private function buildTabMenu(string $action, ?AdminInterface $childAdmin = null): ?ItemInterface{if ($this->loaded['tab_menu']) {return $this->menu;}$this->loaded['tab_menu'] = true;$menu = $this->getMenuFactory()->createItem('root');$menu->setChildrenAttribute('class', 'nav navbar-nav');$menu->setExtra('translation_domain', $this->getTranslationDomain());$this->configureTabMenu($menu, $action, $childAdmin);foreach ($this->getExtensions() as $extension) {$extension->configureTabMenu($this, $menu, $action, $childAdmin);}$this->menu = $menu;return $this->menu;}}