mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
commit
8c244f73e5
@ -19,9 +19,11 @@ use Symfony\Component\DependencyInjection\Definition;
|
|||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
use Symfony\Component\Routing\RouteCollection;
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder;
|
use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder;
|
||||||
|
use Nelmio\ApiDocBundle\ModelDescriber\FormModelDescriber;
|
||||||
|
|
||||||
final class NelmioApiDocExtension extends Extension implements PrependExtensionInterface
|
final class NelmioApiDocExtension extends Extension implements PrependExtensionInterface
|
||||||
{
|
{
|
||||||
@ -43,6 +45,13 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
|
|||||||
|
|
||||||
$loader->load('services.xml');
|
$loader->load('services.xml');
|
||||||
|
|
||||||
|
if (interface_exists(FormInterface::class)) {
|
||||||
|
$container->register('nelmio_api_doc.model_describers.form', FormModelDescriber::class)
|
||||||
|
->setPublic(false)
|
||||||
|
->addArgument(new Reference('form.factory'))
|
||||||
|
->addTag('nelmio_api_doc.model_describer', ['priority' => 10]);
|
||||||
|
}
|
||||||
|
|
||||||
// Filter routes
|
// Filter routes
|
||||||
$routesDefinition = (new Definition(RouteCollection::class))
|
$routesDefinition = (new Definition(RouteCollection::class))
|
||||||
->setFactory([new Reference('router'), 'getRouteCollection']);
|
->setFactory([new Reference('router'), 'getRouteCollection']);
|
||||||
|
102
ModelDescriber/FormModelDescriber.php
Normal file
102
ModelDescriber/FormModelDescriber.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?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\ModelDescriber;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Schema;
|
||||||
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
||||||
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
|
||||||
|
use Nelmio\ApiDocBundle\Model\Model;
|
||||||
|
use Symfony\Component\Form\FormFactoryInterface;
|
||||||
|
use Symfony\Component\Form\FormTypeInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class FormModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
|
||||||
|
{
|
||||||
|
use ModelRegistryAwareTrait;
|
||||||
|
|
||||||
|
private $formFactory;
|
||||||
|
|
||||||
|
public function __construct(FormFactoryInterface $formFactory = null)
|
||||||
|
{
|
||||||
|
$this->formFactory = $formFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describe(Model $model, Schema $schema)
|
||||||
|
{
|
||||||
|
if (method_exists('Symfony\Component\Form\AbstractType', 'setDefaultOptions')) {
|
||||||
|
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) {
|
||||||
|
throw new \LogicException('You need to enable forms in your application to use a form as a model.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema->setType('object');
|
||||||
|
$properties = $schema->getProperties();
|
||||||
|
|
||||||
|
$class = $model->getType()->getClassName();
|
||||||
|
|
||||||
|
$form = $this->formFactory->create($class, null, []);
|
||||||
|
$this->parseForm($schema, $form);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(Model $model): bool
|
||||||
|
{
|
||||||
|
return is_a($model->getType()->getClassName(), FormTypeInterface::class, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseForm(Schema $schema, $form)
|
||||||
|
{
|
||||||
|
$properties = $schema->getProperties();
|
||||||
|
foreach ($form as $name => $child) {
|
||||||
|
$config = $child->getConfig();
|
||||||
|
$property = $properties->get($name);
|
||||||
|
for ($type = $config->getType(); null !== $type; $type = $type->getParent()) {
|
||||||
|
$blockPrefix = $type->getBlockPrefix();
|
||||||
|
|
||||||
|
if ('text' === $blockPrefix) {
|
||||||
|
$property->setType('string');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ('date' === $blockPrefix) {
|
||||||
|
$property->setType('string');
|
||||||
|
$property->setFormat('date');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ('datetime' === $blockPrefix) {
|
||||||
|
$property->setType('string');
|
||||||
|
$property->setFormat('date-time');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ('choice' === $blockPrefix) {
|
||||||
|
$property->setType('string');
|
||||||
|
if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
|
||||||
|
$property->setEnum(array_values($choices));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ('collection' === $blockPrefix) {
|
||||||
|
$subType = $config->getOption('entry_type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config->getRequired()) {
|
||||||
|
$required = $schema->getRequired() ?? [];
|
||||||
|
$required[] = $name;
|
||||||
|
|
||||||
|
$schema->setRequired($required);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ use Nelmio\ApiDocBundle\Annotation\Model;
|
|||||||
use Nelmio\ApiDocBundle\Annotation\Operation;
|
use Nelmio\ApiDocBundle\Annotation\Operation;
|
||||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
||||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article;
|
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article;
|
||||||
|
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;
|
||||||
|
|
||||||
@ -120,4 +121,19 @@ class ApiController
|
|||||||
public function filteredAction()
|
public function filteredAction()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/form", methods={"POST"})
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* name="form",
|
||||||
|
* in="body",
|
||||||
|
* description="Request content",
|
||||||
|
* @Model(type=DummyType::class)
|
||||||
|
* )
|
||||||
|
* @SWG\Response(response="201", description="")
|
||||||
|
*/
|
||||||
|
public function formAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
Tests/Functional/Form/DummyType.php
Normal file
18
Tests/Functional/Form/DummyType.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Functional\Form;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||||
|
use Swagger\Annotations\Definition;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class DummyType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add('bar', TextType::class, ['required' => false]);
|
||||||
|
$builder->add('foo', ChoiceType::class, ['choices' => ['male', 'female']]);
|
||||||
|
}
|
||||||
|
}
|
@ -167,6 +167,23 @@ class FunctionalTest extends WebTestCase
|
|||||||
$this->assertEquals('#/definitions/User', $model->getItems()->getRef());
|
$this->assertEquals('#/definitions/User', $model->getItems()->getRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFormSupport()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => [
|
||||||
|
'bar' => [
|
||||||
|
'type' => 'string',
|
||||||
|
],
|
||||||
|
'foo' => [
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => ['male', 'female'],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'required' => ['foo'],
|
||||||
|
], $this->getModel('DummyType')->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
private function getSwaggerDefinition()
|
private function getSwaggerDefinition()
|
||||||
{
|
{
|
||||||
static::createClient();
|
static::createClient();
|
||||||
|
@ -63,6 +63,7 @@ class TestKernel extends Kernel
|
|||||||
'secret' => 'MySecretKey',
|
'secret' => 'MySecretKey',
|
||||||
'test' => null,
|
'test' => null,
|
||||||
'validation' => null,
|
'validation' => null,
|
||||||
|
'form' => null,
|
||||||
'templating' => [
|
'templating' => [
|
||||||
'engines' => ['twig'],
|
'engines' => ['twig'],
|
||||||
],
|
],
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"symfony/config": "^2.8|^3.0|^4.0",
|
"symfony/config": "^2.8|^3.0|^4.0",
|
||||||
"symfony/validator": "^2.8|^3.0|^4.0",
|
"symfony/validator": "^2.8|^3.0|^4.0",
|
||||||
"symfony/property-access": "^2.8|^3.0|^4.0",
|
"symfony/property-access": "^2.8|^3.0|^4.0",
|
||||||
|
"symfony/form": "^3.0.8|^4.0",
|
||||||
"symfony/dom-crawler": "^2.8|^3.0|^4.0",
|
"symfony/dom-crawler": "^2.8|^3.0|^4.0",
|
||||||
"symfony/browser-kit": "^2.8|^3.0|^4.0",
|
"symfony/browser-kit": "^2.8|^3.0|^4.0",
|
||||||
"symfony/cache": "^3.1|^4.0",
|
"symfony/cache": "^3.1|^4.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user