vendor/sonata-project/admin-bundle/src/Builder/AbstractFormContractor.php line 187

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\Builder;
  12. use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
  13. use Sonata\AdminBundle\Form\Type\AdminType;
  14. use Sonata\AdminBundle\Form\Type\ModelAutocompleteType;
  15. use Sonata\AdminBundle\Form\Type\ModelHiddenType;
  16. use Sonata\AdminBundle\Form\Type\ModelListType;
  17. use Sonata\AdminBundle\Form\Type\ModelReferenceType;
  18. use Sonata\AdminBundle\Form\Type\ModelType;
  19. use Sonata\Form\Type\CollectionType;
  20. use Symfony\Component\Form\Extension\Core\Type\FormType;
  21. use Symfony\Component\Form\FormBuilderInterface;
  22. use Symfony\Component\Form\FormFactoryInterface;
  23. use Symfony\Component\Form\FormRegistryInterface;
  24. /**
  25.  * @psalm-suppress DeprecatedInterface
  26.  */
  27. abstract class AbstractFormContractor implements FormContractorInterface
  28. {
  29.     /**
  30.      * @var FormFactoryInterface
  31.      */
  32.     protected $formFactory;
  33.     /**
  34.      * @var FormRegistryInterface
  35.      */
  36.     protected $formRegistry;
  37.     public function __construct(FormFactoryInterface $formFactoryFormRegistryInterface $formRegistry)
  38.     {
  39.         $this->formFactory $formFactory;
  40.         $this->formRegistry $formRegistry;
  41.     }
  42.     final public function fixFieldDescription(FieldDescriptionInterface $fieldDescription): void
  43.     {
  44.         $fieldDescription->setOption('edit'$fieldDescription->getOption('edit''standard'));
  45.         if ($fieldDescription->describesAssociation() || null !== $fieldDescription->getOption('admin_code')) {
  46.             $fieldDescription->getAdmin()->attachAdminClass($fieldDescription);
  47.         }
  48.     }
  49.     final public function getFormFactory(): FormFactoryInterface
  50.     {
  51.         return $this->formFactory;
  52.     }
  53.     final public function getFormBuilder(string $name, array $formOptions = []): FormBuilderInterface
  54.     {
  55.         return $this->getFormFactory()->createNamedBuilder($nameFormType::class, null$formOptions);
  56.     }
  57.     final public function getDefaultOptions(
  58.         ?string $type,
  59.         FieldDescriptionInterface $fieldDescription,
  60.         array $formOptions = [],
  61.     ): array {
  62.         $options = [];
  63.         $options['sonata_field_description'] = $fieldDescription;
  64.         if ($this->isAnyInstanceOf($type, [
  65.             ModelType::class,
  66.             ModelListType::class,
  67.             ModelHiddenType::class,
  68.             ModelAutocompleteType::class,
  69.             ModelReferenceType::class,
  70.         ])) {
  71.             $options['class'] = $fieldDescription->getTargetModel();
  72.             $options['model_manager'] = $fieldDescription->getAdmin()->getModelManager();
  73.             if ($this->isAnyInstanceOf($type, [ModelAutocompleteType::class])) {
  74.                 if (!$fieldDescription->hasAssociationAdmin()) {
  75.                     throw new \InvalidArgumentException(\sprintf(
  76.                         'The current field `%s` is not linked to an admin.'
  77.                         .' Please create one for the target model: `%s`.',
  78.                         $fieldDescription->getName(),
  79.                         $fieldDescription->getTargetModel() ?? ''
  80.                     ));
  81.                 }
  82.             }
  83.         } elseif ($this->isAnyInstanceOf($type, [AdminType::class])) {
  84.             if (!$fieldDescription->hasAssociationAdmin()) {
  85.                 throw new \InvalidArgumentException(\sprintf(
  86.                     'The current field `%s` is not linked to an admin.'
  87.                     .' Please create one for the target model: `%s`.',
  88.                     $fieldDescription->getName(),
  89.                     $fieldDescription->getTargetModel() ?? ''
  90.                 ));
  91.             }
  92.             if (!$fieldDescription->describesSingleValuedAssociation()) {
  93.                 throw new \InvalidArgumentException(\sprintf(
  94.                     'You are trying to add `%s` field `%s` which is not a One-To-One or Many-To-One association.'
  95.                     .' You SHOULD use `%s` instead.',
  96.                     AdminType::class,
  97.                     $fieldDescription->getName(),
  98.                     CollectionType::class
  99.                 ));
  100.             }
  101.             // set sensitive default value to have a component working fine out of the box
  102.             $options['btn_add'] = false;
  103.             $options['delete'] = false;
  104.             $options['data_class'] = $fieldDescription->getAssociationAdmin()->getClass();
  105.             $options['empty_data'] = static fn (): object => $fieldDescription->getAssociationAdmin()->getNewInstance();
  106.             $fieldDescription->setOption('edit'$fieldDescription->getOption('edit''admin'));
  107.         } elseif ($this->isAnyInstanceOf($type, [
  108.             CollectionType::class,
  109.         ])) {
  110.             if (!$fieldDescription->hasAssociationAdmin()) {
  111.                 throw new \InvalidArgumentException(\sprintf(
  112.                     'The current field `%s` is not linked to an admin.'
  113.                     .' Please create one for the target model: `%s`.',
  114.                     $fieldDescription->getName(),
  115.                     $fieldDescription->getTargetModel() ?? ''
  116.                 ));
  117.             }
  118.             $options['type'] = AdminType::class;
  119.             $options['modifiable'] = true;
  120.             $options['type_options'] = $this->getDefaultAdminTypeOptions($fieldDescription$formOptions);
  121.         }
  122.         return $options;
  123.     }
  124.     /**
  125.      * @param string[] $classes
  126.      *
  127.      * @phpstan-param class-string[] $classes
  128.      */
  129.     private function isAnyInstanceOf(?string $type, array $classes): bool
  130.     {
  131.         if (null === $type) {
  132.             return false;
  133.         }
  134.         foreach ($classes as $class) {
  135.             if (is_a($type$classtrue)) {
  136.                 return true;
  137.             }
  138.         }
  139.         // handle form type inheritance and check all parent types
  140.         $resolvedType $this->formRegistry->getType($type);
  141.         $parentType $resolvedType->getParent();
  142.         if (null !== $parentType) {
  143.             $parentType $parentType->getInnerType()::class;
  144.             // all types have "Symfony\Component\Form\Extension\Core\Type\FormType" as parent
  145.             // so we ignore it here for performance reasons
  146.             if (FormType::class !== $parentType) {
  147.                 return $this->isAnyInstanceOf($parentType$classes);
  148.             }
  149.         }
  150.         return false;
  151.     }
  152.     /**
  153.      * @param array<string, mixed> $formOptions
  154.      *
  155.      * @return array<string, mixed>
  156.      */
  157.     private function getDefaultAdminTypeOptions(FieldDescriptionInterface $fieldDescription, array $formOptions): array
  158.     {
  159.         $typeOptions = [
  160.             'sonata_field_description' => $fieldDescription,
  161.             'data_class' => $fieldDescription->getAssociationAdmin()->getClass(),
  162.             'empty_data' => static fn (): object => $fieldDescription->getAssociationAdmin()->getNewInstance(),
  163.         ];
  164.         if (isset($formOptions['by_reference'])) {
  165.             $typeOptions['collection_by_reference'] = $formOptions['by_reference'];
  166.         }
  167.         return $typeOptions;
  168.     }
  169. }