vendor/sonata-project/admin-bundle/src/Form/FormMapper.php line 109

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\Form;
  12. use Sonata\AdminBundle\Admin\AdminInterface;
  13. use Sonata\AdminBundle\Builder\FormContractorInterface;
  14. use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
  15. use Sonata\AdminBundle\Form\Type\CollectionType;
  16. use Sonata\AdminBundle\Mapper\BaseGroupedMapper;
  17. use Sonata\BlockBundle\Form\Mapper\FormMapper as BlockFormMapper;
  18. use Symfony\Component\Form\Extension\Core\Type\CollectionType as SymfonyCollectionType;
  19. use Symfony\Component\Form\FormBuilderInterface;
  20. use Symfony\Component\Form\FormTypeInterface;
  21. /**
  22.  * This class is used to simulate the Form API.
  23.  *
  24.  * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  25.  *
  26.  * @phpstan-import-type FieldDescriptionOptions from FieldDescriptionInterface
  27.  *
  28.  * @phpstan-template T of object
  29.  * @phpstan-extends BaseGroupedMapper<T>
  30.  */
  31. final class FormMapper extends BaseGroupedMapper implements BlockFormMapper
  32. {
  33.     /**
  34.      * @param AdminInterface<object> $admin
  35.      *
  36.      * @phpstan-param AdminInterface<T> $admin
  37.      */
  38.     public function __construct(
  39.         private FormContractorInterface $builder,
  40.         private FormBuilderInterface $formBuilder,
  41.         private AdminInterface $admin
  42.     ) {
  43.     }
  44.     public function getAdmin(): AdminInterface
  45.     {
  46.         return $this->admin;
  47.     }
  48.     public function reorder(array $keys): static
  49.     {
  50.         $this->getAdmin()->reorderFormGroup($this->getCurrentGroupName(), $keys);
  51.         return $this;
  52.     }
  53.     /**
  54.      * @param array<string, mixed> $options
  55.      *
  56.      * @phpstan-param class-string|null $type
  57.      * @phpstan-param FieldDescriptionOptions $fieldDescriptionOptions
  58.      */
  59.     public function add(string $name, ?string $type null, array $options = [], array $fieldDescriptionOptions = []): static
  60.     {
  61.         if (!$this->shouldApply()) {
  62.             return $this;
  63.         }
  64.         if (
  65.             isset($fieldDescriptionOptions['role'])
  66.             && \is_string($fieldDescriptionOptions['role'])
  67.             && !$this->getAdmin()->isGranted($fieldDescriptionOptions['role'])
  68.         ) {
  69.             return $this;
  70.         }
  71.         if (SymfonyCollectionType::class === $type) {
  72.             $type CollectionType::class;
  73.         }
  74.         // We're accessing form fields with the name added to the group.
  75.         // Since the sanitized name is used by the form builder, the group keep a reference to it.
  76.         $sanitizedName $this->sanitizeFieldName($name);
  77.         $group $this->addFieldToCurrentGroup($name$sanitizedName);
  78.         if (!isset($fieldDescriptionOptions['type']) && \is_string($type)) {
  79.             $fieldDescriptionOptions['type'] = $type;
  80.         }
  81.         if (!isset($fieldDescriptionOptions['translation_domain'])) {
  82.             $fieldDescriptionOptions['translation_domain'] = $group['translation_domain'] ?? null;
  83.         }
  84.         $fieldDescription $this->getAdmin()->createFieldDescription(
  85.             $name,
  86.             $fieldDescriptionOptions
  87.         );
  88.         // Note that the builder var is actually the formContractor:
  89.         $this->builder->fixFieldDescription($fieldDescription);
  90.         // Note that the builder var is actually the formContractor:
  91.         $options array_replace_recursive(
  92.             $this->builder->getDefaultOptions($type$fieldDescription$options),
  93.             $options
  94.         );
  95.         // be compatible with mopa if not installed, avoid generating an exception for invalid option
  96.         // force the default to false ...
  97.         if (!isset($options['label_render'])) {
  98.             $options['label_render'] = false;
  99.         }
  100.         if (!isset($options['label'])) {
  101.             $options['label'] = $this->getAdmin()->getLabelTranslatorStrategy()->getLabel($fieldDescription->getName(), 'form''label');
  102.         }
  103.         // "Dot" notation is not allowed as form name, but can be used as property path to access nested data.
  104.         if (!isset($options['property_path'])) {
  105.             $options['property_path'] = $name;
  106.         }
  107.         $this->getAdmin()->addFormFieldDescription($fieldDescription->getName(), $fieldDescription);
  108.         $this->formBuilder->add($sanitizedName$type$options);
  109.         return $this;
  110.     }
  111.     public function get(string $key): FormBuilderInterface
  112.     {
  113.         $name $this->sanitizeFieldName($key);
  114.         return $this->formBuilder->get($name);
  115.     }
  116.     public function has(string $key): bool
  117.     {
  118.         $key $this->sanitizeFieldName($key);
  119.         return $this->formBuilder->has($key);
  120.     }
  121.     public function keys(): array
  122.     {
  123.         return array_keys($this->formBuilder->all());
  124.     }
  125.     public function remove(string $key): static
  126.     {
  127.         $this->getAdmin()->removeFormFieldDescription($key);
  128.         $this->getAdmin()->removeFieldFromFormGroup($key);
  129.         $sanitizedKey $this->sanitizeFieldName($key);
  130.         $this->formBuilder->remove($sanitizedKey);
  131.         return $this;
  132.     }
  133.     public function getFormBuilder(): FormBuilderInterface
  134.     {
  135.         return $this->formBuilder;
  136.     }
  137.     /**
  138.      * NEXT_MAJOR: Remove this method.
  139.      *
  140.      * @param class-string<FormTypeInterface>|null $type
  141.      * @param array<string, mixed>                 $options
  142.      */
  143.     public function create(string $name, ?string $type null, array $options = []): FormBuilderInterface
  144.     {
  145.         @trigger_error(sprintf(
  146.             'The "%s()" method is deprecated since sonata-project/admin-bundle version 4.15 and will be'
  147.             .' removed in 5.0 version.',
  148.             __METHOD__
  149.         ), \E_USER_DEPRECATED);
  150.         return $this->formBuilder->create($name$type$options);
  151.     }
  152.     protected function getGroups(): array
  153.     {
  154.         return $this->getAdmin()->getFormGroups();
  155.     }
  156.     protected function setGroups(array $groups): void
  157.     {
  158.         $this->getAdmin()->setFormGroups($groups);
  159.     }
  160.     protected function getTabs(): array
  161.     {
  162.         return $this->getAdmin()->getFormTabs();
  163.     }
  164.     protected function setTabs(array $tabs): void
  165.     {
  166.         $this->getAdmin()->setFormTabs($tabs);
  167.     }
  168.     protected function getName(): string
  169.     {
  170.         return 'form';
  171.     }
  172.     /**
  173.      * Symfony default form class can't handle form element with dots in its
  174.      * name (when data get bound, the default dataMapper is a PropertyPathMapper).
  175.      * So use this trick to avoid any issue.
  176.      */
  177.     private function sanitizeFieldName(string $fieldName): string
  178.     {
  179.         return str_replace(['__''.'], ['____''__'], $fieldName);
  180.     }
  181. }