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

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