mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-10 11:39:25 +03:00
Added support for EntityType in FormModel Describer. Reading form recursively down. Modified formSupport test
This commit is contained in:
parent
3adf8c3d97
commit
6e41a20e40
@ -15,8 +15,14 @@ use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
|||||||
|
|
||||||
trait ModelRegistryAwareTrait
|
trait ModelRegistryAwareTrait
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var ModelRegistry
|
||||||
|
*/
|
||||||
private $modelRegistry;
|
private $modelRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ModelRegistry $modelRegistry
|
||||||
|
*/
|
||||||
public function setModelRegistry(ModelRegistry $modelRegistry)
|
public function setModelRegistry(ModelRegistry $modelRegistry)
|
||||||
{
|
{
|
||||||
$this->modelRegistry = $modelRegistry;
|
$this->modelRegistry = $modelRegistry;
|
||||||
|
@ -12,15 +12,22 @@
|
|||||||
namespace Nelmio\ApiDocBundle\ModelDescriber;
|
namespace Nelmio\ApiDocBundle\ModelDescriber;
|
||||||
|
|
||||||
use EXSyst\Component\Swagger\Schema;
|
use EXSyst\Component\Swagger\Schema;
|
||||||
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
||||||
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
|
||||||
use Nelmio\ApiDocBundle\Model\Model;
|
use Nelmio\ApiDocBundle\Model\Model;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormFactoryInterface;
|
use Symfony\Component\Form\FormFactoryInterface;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Form\FormTypeInterface;
|
use Symfony\Component\Form\FormTypeInterface;
|
||||||
|
use Symfony\Component\PropertyInfo\Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
final class FormModelDescriber implements ModelDescriberInterface
|
final class FormModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
|
||||||
{
|
{
|
||||||
|
use ModelRegistryAwareTrait;
|
||||||
|
|
||||||
private $formFactory;
|
private $formFactory;
|
||||||
|
|
||||||
public function __construct(FormFactoryInterface $formFactory = null)
|
public function __construct(FormFactoryInterface $formFactory = null)
|
||||||
@ -30,7 +37,7 @@ final class FormModelDescriber implements ModelDescriberInterface
|
|||||||
|
|
||||||
public function describe(Model $model, Schema $schema)
|
public function describe(Model $model, Schema $schema)
|
||||||
{
|
{
|
||||||
if (method_exists('Symfony\Component\Form\AbstractType', 'setDefaultOptions')) {
|
if (method_exists(AbstractType::class, 'setDefaultOptions')) {
|
||||||
throw new \LogicException('symfony/form < 3.0 is not supported, please upgrade to an higher version to use a form as a model.');
|
throw new \LogicException('symfony/form < 3.0 is not supported, please upgrade to an higher version to use a form as a model.');
|
||||||
}
|
}
|
||||||
if (null === $this->formFactory) {
|
if (null === $this->formFactory) {
|
||||||
@ -51,9 +58,10 @@ final class FormModelDescriber implements ModelDescriberInterface
|
|||||||
return is_a($model->getType()->getClassName(), FormTypeInterface::class, true);
|
return is_a($model->getType()->getClassName(), FormTypeInterface::class, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseForm(Schema $schema, $form)
|
private function parseForm(Schema $schema, FormInterface $form)
|
||||||
{
|
{
|
||||||
$properties = $schema->getProperties();
|
$properties = $schema->getProperties();
|
||||||
|
|
||||||
foreach ($form as $name => $child) {
|
foreach ($form as $name => $child) {
|
||||||
$config = $child->getConfig();
|
$config = $child->getConfig();
|
||||||
$property = $properties->get($name);
|
$property = $properties->get($name);
|
||||||
@ -64,16 +72,24 @@ final class FormModelDescriber implements ModelDescriberInterface
|
|||||||
$property->setType('string');
|
$property->setType('string');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('number' === $blockPrefix) {
|
||||||
|
$property->setType('number');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if ('date' === $blockPrefix) {
|
if ('date' === $blockPrefix) {
|
||||||
$property->setType('string');
|
$property->setType('string');
|
||||||
$property->setFormat('date');
|
$property->setFormat('date');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('datetime' === $blockPrefix) {
|
if ('datetime' === $blockPrefix) {
|
||||||
$property->setType('string');
|
$property->setType('string');
|
||||||
$property->setFormat('date-time');
|
$property->setFormat('date-time');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('choice' === $blockPrefix) {
|
if ('choice' === $blockPrefix) {
|
||||||
$property->setType('string');
|
$property->setType('string');
|
||||||
if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
|
if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
|
||||||
@ -85,6 +101,30 @@ final class FormModelDescriber implements ModelDescriberInterface
|
|||||||
if ('collection' === $blockPrefix) {
|
if ('collection' === $blockPrefix) {
|
||||||
$subType = $config->getOption('entry_type');
|
$subType = $config->getOption('entry_type');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('entity' === $blockPrefix) {
|
||||||
|
$entityClass = $config->getOption('class');
|
||||||
|
|
||||||
|
if ($config->getOption('multiple')) {
|
||||||
|
$property->setFormat(sprintf('[%s id]', $entityClass));
|
||||||
|
$property->setType('array');
|
||||||
|
$property->setExample('[1, 2, 3]');
|
||||||
|
} else {
|
||||||
|
$property->setType('string');
|
||||||
|
$property->setFormat(sprintf('%s id', $entityClass));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
($formType = $type->getInnerType()) &&
|
||||||
|
($formClass = get_class($formType)) &&
|
||||||
|
!$this->isBuiltinType($formClass) //don't check builtin types in Form component.
|
||||||
|
) {
|
||||||
|
$model = new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $formClass));
|
||||||
|
$property->setRef($this->modelRegistry->register($model));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config->getRequired()) {
|
if ($config->getRequired()) {
|
||||||
@ -95,4 +135,14 @@ final class FormModelDescriber implements ModelDescriberInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isBuiltinType(string $type) : bool
|
||||||
|
{
|
||||||
|
return strpos($type, 'Symfony\Component\Form\Extension\Core\Type') !== false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
|||||||
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
|
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||||
use Swagger\Annotations as SWG;
|
use Swagger\Annotations as SWG;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\Functional\Form\UserType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/api")
|
* @Route("/api")
|
||||||
@ -71,6 +72,27 @@ class ApiController
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/test/users/{user}", methods={"POST"}, schemes={"https"}, requirements={"user"="/foo/"})
|
||||||
|
* @SWG\Response(
|
||||||
|
* response="201",
|
||||||
|
* description="Operation automatically detected",
|
||||||
|
* @Model(type=User::class)
|
||||||
|
* )
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* name="foo",
|
||||||
|
* in="body",
|
||||||
|
* description="This is a parameter",
|
||||||
|
* @SWG\Schema(
|
||||||
|
* type="array",
|
||||||
|
* @Model(type=UserType::class)
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function submitUserTypeAction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/test/{user}", methods={"GET"}, schemes={"https"}, requirements={"user"="/foo/"})
|
* @Route("/test/{user}", methods={"GET"}, schemes={"https"}, requirements={"user"="/foo/"})
|
||||||
* @Operation(
|
* @Operation(
|
||||||
|
23
Tests/Functional/Form/UserType.php
Normal file
23
Tests/Functional/Form/UserType.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Functional\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
||||||
|
|
||||||
|
class UserType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add('dummy', DummyType::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -176,6 +176,14 @@ class FunctionalTest extends WebTestCase
|
|||||||
|
|
||||||
public function testFormSupport()
|
public function testFormSupport()
|
||||||
{
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => [
|
||||||
|
'dummy' => ['$ref' => '#/definitions/DummyType']
|
||||||
|
],
|
||||||
|
'required' => ['dummy'],
|
||||||
|
], $this->getModel('UserType')->toArray());
|
||||||
|
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
'type' => 'object',
|
'type' => 'object',
|
||||||
'properties' => [
|
'properties' => [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user