From dc20b249e772bdd8cd7d55292b355b2678578f2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Reuille?= <sreuille.externe@m6web.fr>
Date: Mon, 10 Nov 2014 16:38:40 +0100
Subject: [PATCH] fix memory limit issue wirh entity list

Allow to disable mapping of entity list to avoid memory limit error on long list
---
 DependencyInjection/Configuration.php         |  1 +
 DependencyInjection/NelmioApiDocExtension.php |  1 +
 Parser/FormTypeParser.php                     | 16 ++++--
 Resources/config/services.form.xml            |  1 +
 Resources/doc/index.md                        |  4 ++
 Tests/Fixtures/Form/EntityType.php            | 44 +++++++++++++++
 Tests/Fixtures/Form/ImprovedTestType.php      |  1 +
 Tests/Fixtures/Model/EntityTest.php           | 19 +++++++
 Tests/Fixtures/Model/ImprovedTest.php         |  1 +
 Tests/Parser/FormTypeParserTest.php           | 53 ++++++++++++++++++-
 10 files changed, 137 insertions(+), 4 deletions(-)
 create mode 100644 Tests/Fixtures/Form/EntityType.php
 create mode 100644 Tests/Fixtures/Model/EntityTest.php

diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 09c776d..7b491d7 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -132,6 +132,7 @@ class Configuration implements ConfigurationInterface
                                 })
                             ->end()
                         ->end()
+                        ->booleanNode('entity_to_choice')->defaultValue(true)->end()
                     ->end()
                 ->end()
                 ->arrayNode('swagger')
diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php
index 63e45c8..5af3f32 100644
--- a/DependencyInjection/NelmioApiDocExtension.php
+++ b/DependencyInjection/NelmioApiDocExtension.php
@@ -41,6 +41,7 @@ class NelmioApiDocExtension extends Extension
         $container->setParameter('nelmio_api_doc.sandbox.request_format.method', $config['sandbox']['request_format']['method']);
         $container->setParameter('nelmio_api_doc.sandbox.request_format.default_format', $config['sandbox']['request_format']['default_format']);
         $container->setParameter('nelmio_api_doc.sandbox.request_format.formats', $config['sandbox']['request_format']['formats']);
+        $container->setParameter('nelmio_api_doc.sandbox.entity_to_choice', $config['sandbox']['entity_to_choice']);
 
         $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
         $loader->load('formatters.xml');
diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php
index 1a3d8b3..1f766ea 100644
--- a/Parser/FormTypeParser.php
+++ b/Parser/FormTypeParser.php
@@ -35,6 +35,11 @@ class FormTypeParser implements ParserInterface
      */
     protected $formRegistry;
 
+    /**
+     * @var boolean
+     */
+    protected $entityToChoice;
+
     /**
      * @var array
      */
@@ -52,9 +57,10 @@ class FormTypeParser implements ParserInterface
         'file'      => DataTypes::FILE,
     );
 
-    public function __construct(FormFactoryInterface $formFactory)
+    public function __construct(FormFactoryInterface $formFactory, $entityToChoice)
     {
-        $this->formFactory  = $formFactory;
+        $this->formFactory    = $formFactory;
+        $this->entityToChoice = (boolean) $entityToChoice;
     }
 
     /**
@@ -254,7 +260,11 @@ class FormTypeParser implements ParserInterface
                     if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
                         $parameters[$name]['format'] = json_encode($choices);
                     } elseif (($choiceList = $config->getOption('choice_list')) && $choiceList instanceof ChoiceListInterface) {
-                        $choices = $this->handleChoiceListValues($choiceList);
+                        if (('entity' === $config->getType()->getName() && false === $this->entityToChoice)) {
+                            $choices = array();
+                        } else {
+                            $choices = $this->handleChoiceListValues($choiceList);
+                        }
                         if (is_array($choices) && count($choices)) {
                             $parameters[$name]['format'] = json_encode($choices);
                         }
diff --git a/Resources/config/services.form.xml b/Resources/config/services.form.xml
index e83b7de..133ddc8 100644
--- a/Resources/config/services.form.xml
+++ b/Resources/config/services.form.xml
@@ -10,6 +10,7 @@
     <services>
         <service id="nelmio_api_doc.parser.form_type_parser" class="%nelmio_api_doc.parser.form_type_parser.class%">
             <argument type="service" id="form.factory" />
+            <argument>%nelmio_api_doc.sandbox.entity_to_choice%</argument>
             <tag name="nelmio_api_doc.extractor.parser" />
         </service>
     </services>
diff --git a/Resources/doc/index.md b/Resources/doc/index.md
index 70290a3..6f83ac8 100644
--- a/Resources/doc/index.md
+++ b/Resources/doc/index.md
@@ -370,6 +370,9 @@ nelmio_api_doc:
 
             default_format: json    # default is `json`,
                                     # default content format to request (see formats)
+                                    
+        entity_to_choice: false     # default is `true`, if `false`, entity collection 
+                                    # will not be mapped as choice
 ```
 ### Command
 
@@ -543,6 +546,7 @@ nelmio_api_doc:
             # Required if http delivery is selected.
             type:                 ~ # One of "basic"; "bearer"
             custom_endpoint:      false
+        entity_to_choice:         true
     swagger:
         api_base_path:        /api
         swagger_version:      '1.2'
diff --git a/Tests/Fixtures/Form/EntityType.php b/Tests/Fixtures/Form/EntityType.php
new file mode 100644
index 0000000..50ae694
--- /dev/null
+++ b/Tests/Fixtures/Form/EntityType.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle.
+ *
+ * (c) Nelmio <hello@nelm.io>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+class EntityType extends AbstractType
+{
+
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setDefaultOptions(OptionsResolverInterface $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\EntityTest',
+        ));
+
+        return;
+    }
+
+    public function getParent()
+    {
+        return 'choice';
+    }
+
+    public function getName()
+    {
+        return 'entity';
+    }
+}
diff --git a/Tests/Fixtures/Form/ImprovedTestType.php b/Tests/Fixtures/Form/ImprovedTestType.php
index 7963eb2..604984a 100644
--- a/Tests/Fixtures/Form/ImprovedTestType.php
+++ b/Tests/Fixtures/Form/ImprovedTestType.php
@@ -26,6 +26,7 @@ class ImprovedTestType extends AbstractType
             ->add('c2', 'choice', array('choices' => array('m' => 'Male', 'f' => 'Female'), 'multiple' => true))
             ->add('c3', 'choice', array('choices' => array()))
             ->add('c4', 'choice', array('choice_list' => new SimpleChoiceList(array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz')))))
+            ->add('e1', new EntityType(), array('choice_list' => new SimpleChoiceList(array('foo' => 'bar', 'bazgroup' => array('baz' => 'Buzz')))))
         ;
     }
 
diff --git a/Tests/Fixtures/Model/EntityTest.php b/Tests/Fixtures/Model/EntityTest.php
new file mode 100644
index 0000000..b90c64e
--- /dev/null
+++ b/Tests/Fixtures/Model/EntityTest.php
@@ -0,0 +1,19 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle.
+ *
+ * (c) Nelmio <hello@nelm.io>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Tests\Fixtures\Model;
+
+use Symfony\Component\Validator\Constraints as Assert;
+
+class EntityTest
+{
+
+}
diff --git a/Tests/Fixtures/Model/ImprovedTest.php b/Tests/Fixtures/Model/ImprovedTest.php
index 2339276..bf279e6 100644
--- a/Tests/Fixtures/Model/ImprovedTest.php
+++ b/Tests/Fixtures/Model/ImprovedTest.php
@@ -26,4 +26,5 @@ class ImprovedTest
     public $c2;
     public $c3;
     public $c4;
+    public $e1;
 }
diff --git a/Tests/Parser/FormTypeParserTest.php b/Tests/Parser/FormTypeParserTest.php
index 4c0b5fc..60985ba 100644
--- a/Tests/Parser/FormTypeParserTest.php
+++ b/Tests/Parser/FormTypeParserTest.php
@@ -24,10 +24,33 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
         $formFactoryBuilder->addExtension(new CoreExtension());
         $formFactoryBuilder->addTypeExtension(new DescriptionFormTypeExtension());
         $formFactory = $formFactoryBuilder->getFormFactory();
-        $formTypeParser = new FormTypeParser($formFactory);
+        $formTypeParser = new FormTypeParser($formFactory, $entityToChoice = true);
 
         set_error_handler(array('Nelmio\ApiDocBundle\Tests\WebTestCase', 'handleDeprecation'));
         trigger_error('test', E_USER_DEPRECATED);
+
+        $output = $formTypeParser->parse($typeName);
+        restore_error_handler();
+
+        $this->assertEquals($expected, $output);
+    }
+
+    /**
+     * @dataProvider dataTestParseWithoutEntity
+     */
+    public function testParseWithoutEntity($typeName, $expected)
+    {
+        $resolvedTypeFactory = new ResolvedFormTypeFactory();
+        $formFactoryBuilder = new FormFactoryBuilder();
+        $formFactoryBuilder->setResolvedTypeFactory($resolvedTypeFactory);
+        $formFactoryBuilder->addExtension(new CoreExtension());
+        $formFactoryBuilder->addTypeExtension(new DescriptionFormTypeExtension());
+        $formFactory = $formFactoryBuilder->getFormFactory();
+        $formTypeParser = new FormTypeParser($formFactory, $entityToChoice = false);
+
+        set_error_handler(array('Nelmio\ApiDocBundle\Tests\WebTestCase', 'handleDeprecation'));
+        trigger_error('test', E_USER_DEPRECATED);
+
         $output = $formTypeParser->parse($typeName);
         restore_error_handler();
 
@@ -36,6 +59,31 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
 
     public function dataTestParse()
     {
+
+        return $this->expectedData(true);
+    }
+
+    public function dataTestParseWithoutEntity()
+    {
+        return $this->expectedData(false);
+    }
+
+    protected function expectedData($entityToChoice)
+    {
+        $entityData = array(
+            'dataType' => 'choice',
+            'actualType' => DataTypes::ENUM,
+            'subType' => null,
+            'default' => null,
+            'required' => true,
+            'description' => '',
+            'readonly' => false
+        );
+
+        if ($entityToChoice) {
+            $entityData['format'] = json_encode(array('foo' => 'bar', 'baz' => 'Buzz'));
+        }
+
         return array(
             array(
                 array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', 'options' => array()),
@@ -389,6 +437,7 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                         'readonly' => false,
                         'format' => json_encode(array('foo' => 'bar', 'baz' => 'Buzz')),
                     ),
+                    'e1' => $entityData
                 ),
             ),
             array(
@@ -531,5 +580,7 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                 ),
             ),
         );
+
     }
+
 }