Support for extended builtin form types

This commit is contained in:
Marek Štípek 2018-05-09 23:30:21 +02:00
parent bf52f136ce
commit b4ba46f9c2
4 changed files with 75 additions and 15 deletions

View File

@ -16,10 +16,12 @@ use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait; use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
use Nelmio\ApiDocBundle\Model\Model; use Nelmio\ApiDocBundle\Model\Model;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormConfigBuilderInterface; use Symfony\Component\Form\FormConfigBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormTypeInterface; use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\Form\ResolvedFormTypeInterface;
use Symfony\Component\PropertyInfo\Type; use Symfony\Component\PropertyInfo\Type;
/** /**
@ -94,8 +96,18 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry
*/ */
private function findFormType(FormConfigBuilderInterface $config, $property): bool private function findFormType(FormConfigBuilderInterface $config, $property): bool
{ {
for ($type = $config->getType(); null !== $type; $type = $type->getParent()) { $type = $config->getType();
$blockPrefix = $type->getBlockPrefix();
if (!$builtinFormType = $this->getBuiltinFormType($type)) {
// if form type is not builtin in Form component.
$model = new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, get_class($type->getInnerType())));
$property->setRef($this->modelRegistry->register($model));
return false;
}
do {
$blockPrefix = $builtinFormType->getBlockPrefix();
if ('text' === $blockPrefix) { if ('text' === $blockPrefix) {
$property->setType('string'); $property->setType('string');
@ -150,6 +162,8 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry
if ('checkbox' === $blockPrefix) { if ('checkbox' === $blockPrefix) {
$property->setType('boolean'); $property->setType('boolean');
return true;
} }
if ('collection' === $blockPrefix) { if ('collection' === $blockPrefix) {
@ -180,15 +194,7 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry
return true; return true;
} }
} while ($builtinFormType = $builtinFormType->getParent());
if ($type->getInnerType() && ($formClass = get_class($type->getInnerType())) && !$this->isBuiltinType($formClass)) {
// if form type is not builtin in Form component.
$model = new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $formClass));
$property->setRef($this->modelRegistry->register($model));
return false;
}
}
return false; return false;
} }
@ -209,8 +215,21 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry
return true; return true;
} }
private function isBuiltinType(string $type): bool /**
* @param ResolvedFormTypeInterface $type
*
* @return ResolvedFormTypeInterface|null
*/
private function getBuiltinFormType(ResolvedFormTypeInterface $type)
{ {
return 0 === strpos($type, 'Symfony\Component\Form\Extension\Core\Type'); do {
$class = get_class($type->getInnerType());
if (FormType::class !== $class && 0 === strpos($class, 'Symfony\Component\Form\Extension\Core\Type\\')) {
return $type;
}
} while ($type = $type->getParent());
return null;
} }
} }

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\Functional\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ExtendedBuiltinType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefaults([
'choices' => [
'foo' => 'foo',
'bar' => 'bar',
],
])
->setRequired('required_option');
}
public function getParent(): string
{
return ChoiceType::class;
}
}

View File

@ -35,7 +35,8 @@ class UserType extends AbstractType
'entry_type' => DummyEmptyType::class, 'entry_type' => DummyEmptyType::class,
'required' => false, 'required' => false,
]) ])
->add('quz', DummyType::class, ['documentation' => ['type' => 'string', 'description' => 'User type.'], 'required' => false]); ->add('quz', DummyType::class, ['documentation' => ['type' => 'string', 'description' => 'User type.'], 'required' => false])
->add('extended_builtin', ExtendedBuiltinType::class, ['required_option' => 'foo']);
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)

View File

@ -243,8 +243,12 @@ class FunctionalTest extends WebTestCase
'type' => 'string', 'type' => 'string',
'description' => 'User type.', 'description' => 'User type.',
], ],
'extended_builtin' => [
'type' => 'string',
'enum' => ['foo', 'bar'],
],
], ],
'required' => ['dummy', 'dummies'], 'required' => ['dummy', 'dummies', 'extended_builtin'],
], $this->getModel('UserType')->toArray()); ], $this->getModel('UserType')->toArray());
$this->assertEquals([ $this->assertEquals([