mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
handle form collection with type introspection
This commit is contained in:
parent
c5b1d538f8
commit
af30c6ac0e
@ -16,6 +16,7 @@ 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\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;
|
||||||
@ -77,98 +78,119 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry
|
|||||||
continue; // Type manually defined
|
continue; // Type manually defined
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($type = $config->getType(); null !== $type; $type = $type->getParent()) {
|
$this->findFormType($config, $property);
|
||||||
$blockPrefix = $type->getBlockPrefix();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ('text' === $blockPrefix) {
|
/**
|
||||||
$property->setType('string');
|
* Finds and sets the schema type on $property based on $config info.
|
||||||
|
*
|
||||||
|
* Returns true if a native Swagger type was found, false otherwise
|
||||||
|
*
|
||||||
|
* @param FormConfigBuilderInterface $config
|
||||||
|
* @param $property
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function findFormType(FormConfigBuilderInterface $config, $property): bool
|
||||||
|
{
|
||||||
|
for ($type = $config->getType(); null !== $type; $type = $type->getParent()) {
|
||||||
|
$blockPrefix = $type->getBlockPrefix();
|
||||||
|
|
||||||
break;
|
if ('text' === $blockPrefix) {
|
||||||
}
|
$property->setType('string');
|
||||||
|
|
||||||
if ('number' === $blockPrefix) {
|
return true;
|
||||||
$property->setType('number');
|
}
|
||||||
|
|
||||||
break;
|
if ('number' === $blockPrefix) {
|
||||||
}
|
$property->setType('number');
|
||||||
|
|
||||||
if ('integer' === $blockPrefix) {
|
return true;
|
||||||
$property->setType('integer');
|
}
|
||||||
|
|
||||||
break;
|
if ('integer' === $blockPrefix) {
|
||||||
}
|
$property->setType('integer');
|
||||||
|
|
||||||
if ('date' === $blockPrefix) {
|
return true;
|
||||||
$property->setType('string');
|
}
|
||||||
$property->setFormat('date');
|
|
||||||
|
|
||||||
break;
|
if ('date' === $blockPrefix) {
|
||||||
}
|
$property->setType('string');
|
||||||
|
$property->setFormat('date');
|
||||||
|
|
||||||
if ('datetime' === $blockPrefix) {
|
return true;
|
||||||
$property->setType('string');
|
}
|
||||||
$property->setFormat('date-time');
|
|
||||||
|
|
||||||
break;
|
if ('datetime' === $blockPrefix) {
|
||||||
}
|
$property->setType('string');
|
||||||
|
$property->setFormat('date-time');
|
||||||
|
|
||||||
if ('choice' === $blockPrefix) {
|
return true;
|
||||||
if ($config->getOption('multiple')) {
|
}
|
||||||
$property->setType('array');
|
|
||||||
} else {
|
|
||||||
$property->setType('string');
|
|
||||||
}
|
|
||||||
if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
|
|
||||||
$enums = array_values($choices);
|
|
||||||
$type = $this->isNumbersArray($enums) ? 'number' : 'string';
|
|
||||||
if ($config->getOption('multiple')) {
|
|
||||||
$property->getItems()->setType($type)->setEnum($enums);
|
|
||||||
} else {
|
|
||||||
$property->setType($type)->setEnum($enums);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
if ('choice' === $blockPrefix) {
|
||||||
}
|
if ($config->getOption('multiple')) {
|
||||||
|
|
||||||
if ('checkbox' === $blockPrefix) {
|
|
||||||
$property->setType('boolean');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('collection' === $blockPrefix) {
|
|
||||||
$subType = $config->getOption('entry_type');
|
|
||||||
$property->setType('array');
|
$property->setType('array');
|
||||||
|
} else {
|
||||||
$model = new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $subType), null);
|
$property->setType('string');
|
||||||
$property->getItems()->setRef($this->modelRegistry->register($model));
|
|
||||||
$property->setExample(sprintf('[{%s}]', $subType));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
|
||||||
if ('entity' === $blockPrefix) {
|
$enums = array_values($choices);
|
||||||
$entityClass = $config->getOption('class');
|
$type = $this->isNumbersArray($enums) ? 'number' : 'string';
|
||||||
|
|
||||||
if ($config->getOption('multiple')) {
|
if ($config->getOption('multiple')) {
|
||||||
$property->setFormat(sprintf('[%s id]', $entityClass));
|
$property->getItems()->setType($type)->setEnum($enums);
|
||||||
$property->setType('array');
|
|
||||||
} else {
|
} else {
|
||||||
$property->setType('string');
|
$property->setType($type)->setEnum($enums);
|
||||||
$property->setFormat(sprintf('%s id', $entityClass));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type->getInnerType() && ($formClass = get_class($type->getInnerType())) && !$this->isBuiltinType($formClass)) {
|
return true;
|
||||||
// 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));
|
|
||||||
|
|
||||||
break;
|
if ('checkbox' === $blockPrefix) {
|
||||||
|
$property->setType('boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('collection' === $blockPrefix) {
|
||||||
|
$subType = $config->getOption('entry_type');
|
||||||
|
$subOptions = $config->getOption('entry_options');
|
||||||
|
$subForm = $this->formFactory->create($subType, null, $subOptions);
|
||||||
|
|
||||||
|
$property->setType('array');
|
||||||
|
$itemsProp = $property->getItems();
|
||||||
|
|
||||||
|
if (!$this->findFormType($subForm->getConfig(), $itemsProp)) {
|
||||||
|
$property->setExample(sprintf('[{%s}]', $subType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('entity' === $blockPrefix) {
|
||||||
|
$entityClass = $config->getOption('class');
|
||||||
|
|
||||||
|
if ($config->getOption('multiple')) {
|
||||||
|
$property->setFormat(sprintf('[%s id]', $entityClass));
|
||||||
|
$property->setType('array');
|
||||||
|
} else {
|
||||||
|
$property->setType('string');
|
||||||
|
$property->setFormat(sprintf('%s id', $entityClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
22
Tests/Functional/Form/DummyEmptyType.php
Normal file
22
Tests/Functional/Form/DummyEmptyType.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?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\FormBuilderInterface;
|
||||||
|
|
||||||
|
class DummyEmptyType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Tests\Functional\Form;
|
|||||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
@ -22,10 +23,18 @@ class UserType extends AbstractType
|
|||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
|
->add('strings', CollectionType::class, [
|
||||||
|
'entry_type' => TextType::class,
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
->add('dummy', DummyType::class)
|
->add('dummy', DummyType::class)
|
||||||
->add('dummies', CollectionType::class, [
|
->add('dummies', CollectionType::class, [
|
||||||
'entry_type' => DummyType::class,
|
'entry_type' => DummyType::class,
|
||||||
])
|
])
|
||||||
|
->add('empty_dummies', CollectionType::class, [
|
||||||
|
'entry_type' => DummyEmptyType::class,
|
||||||
|
'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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Nelmio\ApiDocBundle\Tests\Functional;
|
namespace Nelmio\ApiDocBundle\Tests\Functional;
|
||||||
|
|
||||||
use EXSyst\Component\Swagger\Tag;
|
use EXSyst\Component\Swagger\Tag;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyEmptyType;
|
||||||
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
|
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
|
||||||
|
|
||||||
class FunctionalTest extends WebTestCase
|
class FunctionalTest extends WebTestCase
|
||||||
@ -223,12 +224,21 @@ class FunctionalTest extends WebTestCase
|
|||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
'type' => 'object',
|
'type' => 'object',
|
||||||
'properties' => [
|
'properties' => [
|
||||||
|
'strings' => [
|
||||||
|
'items' => ['type' => 'string'],
|
||||||
|
'type' => 'array',
|
||||||
|
],
|
||||||
'dummy' => ['$ref' => '#/definitions/DummyType'],
|
'dummy' => ['$ref' => '#/definitions/DummyType'],
|
||||||
'dummies' => [
|
'dummies' => [
|
||||||
'items' => ['$ref' => '#/definitions/DummyType'],
|
'items' => ['$ref' => '#/definitions/DummyType'],
|
||||||
'type' => 'array',
|
'type' => 'array',
|
||||||
'example' => sprintf('[{%s}]', DummyType::class),
|
'example' => sprintf('[{%s}]', DummyType::class),
|
||||||
],
|
],
|
||||||
|
'empty_dummies' => [
|
||||||
|
'items' => ['$ref' => '#/definitions/DummyEmptyType'],
|
||||||
|
'type' => 'array',
|
||||||
|
'example' => sprintf('[{%s}]', DummyEmptyType::class),
|
||||||
|
],
|
||||||
'quz' => [
|
'quz' => [
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => 'User type.',
|
'description' => 'User type.',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user