diff --git a/Annotation/ApiDoc.php b/Annotation/ApiDoc.php
index 031814b..1c9c9d0 100644
--- a/Annotation/ApiDoc.php
+++ b/Annotation/ApiDoc.php
@@ -26,7 +26,7 @@ class ApiDoc
/**
* @var string
*/
- private $formType = null;
+ private $input = null;
/**
* @var string
@@ -70,8 +70,8 @@ class ApiDoc
public function __construct(array $data)
{
- if (isset($data['formType'])) {
- $this->formType = $data['formType'];
+ if (isset($data['input'])) {
+ $this->input = $data['input'];
} elseif (isset($data['filters'])) {
foreach ($data['filters'] as $filter) {
if (!isset($filter['name'])) {
@@ -121,9 +121,9 @@ class ApiDoc
/**
* @return string|null
*/
- public function getFormType()
+ public function getInput()
{
- return $this->formType;
+ return $this->input;
}
/**
diff --git a/DependencyInjection/RegisterExtractorParsersPass.php b/DependencyInjection/RegisterExtractorParsersPass.php
new file mode 100644
index 0000000..f9d4564
--- /dev/null
+++ b/DependencyInjection/RegisterExtractorParsersPass.php
@@ -0,0 +1,39 @@
+hasDefinition('nelmio_api_doc.extractor.api_doc_extractor')) {
+ return;
+ }
+
+ $definition = $container->getDefinition('nelmio_api_doc.extractor.api_doc_extractor');
+
+ //find registered parsers and sort by priority
+ $sortedParsers = array();
+ foreach ($container->findTaggedServiceIds('nelmio_api_doc.extractor.parser') as $id => $tagAttributes) {
+ foreach ($tagAttributes as $attributes) {
+ $priority = isset($attributes['priority']) ? $attributes['priority'] : 0;
+ $sortedParsers[$priority][] = $id;
+ }
+ }
+
+ //add parsers if any
+ if (!empty($sortedParsers)) {
+ krsort($sortedParsers);
+ $sortedParsers = call_user_func_array('array_merge', $sortedParsers);
+
+ //add method call for each registered parsers
+ foreach ($sortedParsers as $id) {
+ $definition->addMethodCall('addParser', array(new Reference($id)));
+ }
+ }
+ }
+}
diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php
index a45dcf6..dcc8af1 100644
--- a/Extractor/ApiDocExtractor.php
+++ b/Extractor/ApiDocExtractor.php
@@ -13,7 +13,7 @@ namespace Nelmio\ApiDocBundle\Extractor;
use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
-use Nelmio\ApiDocBundle\Parser\FormTypeParser;
+use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -41,16 +41,15 @@ class ApiDocExtractor
private $reader;
/**
- * @var \Nelmio\ApiDocBundle\Parser\FormTypeParser
+ * @var array \Nelmio\ApiDocBundle\Parser\ParserInterface
*/
- private $parser;
+ private $parsers = array();
- public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader, FormTypeParser $parser)
+ public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader)
{
$this->container = $container;
$this->router = $router;
$this->reader = $reader;
- $this->parser = $parser;
}
/**
@@ -122,8 +121,6 @@ class ApiDocExtractor
return strcmp($a['resource'], $b['resource']);
});
-
-
return $array;
}
@@ -179,6 +176,16 @@ class ApiDocExtractor
return null;
}
+ /**
+ * Registers a class parser to use for parsing input class metadata
+ *
+ * @param ParserInterface $parser
+ */
+ public function addParser(ParserInterface $parser)
+ {
+ $this->parsers[] = $parser;
+ }
+
/**
* Returns a new ApiDoc instance with more data.
*
@@ -215,9 +222,15 @@ class ApiDocExtractor
// doc
$annotation->setDocumentation($this->getDocCommentText($method));
- // formType
- if (null !== $formType = $annotation->getFormType()) {
- $parameters = $this->parser->parse($formType);
+ // input
+ if (null !== $input = $annotation->getInput()) {
+ $parameters = array();
+
+ foreach ($this->parsers as $parser) {
+ if ($parser->supports($input)) {
+ $parameters = $parser->parse($input);
+ }
+ }
if ('PUT' === $method) {
// All parameters are optional with PUT (update)
@@ -280,7 +293,7 @@ class ApiDocExtractor
}
/**
- * @param Reflector $reflected
+ * @param Reflector $reflected
* @return string
*/
protected function getDocComment(\Reflector $reflected)
@@ -300,7 +313,7 @@ class ApiDocExtractor
}
/**
- * @param Reflector $reflected
+ * @param Reflector $reflected
* @return string
*/
protected function getDocCommentText(\Reflector $reflected)
diff --git a/Formatter/FormatterInterface.php b/Formatter/FormatterInterface.php
index dcbadd5..1a3302f 100644
--- a/Formatter/FormatterInterface.php
+++ b/Formatter/FormatterInterface.php
@@ -26,7 +26,7 @@ interface FormatterInterface
/**
* Format documentation data for one route.
*
- * @param ApiDoc $annotation
+ * @param ApiDoc $annotation
* return string|array
*/
public function formatOne(ApiDoc $annotation);
diff --git a/NelmioApiDocBundle.php b/NelmioApiDocBundle.php
index b42aba2..fbfe504 100644
--- a/NelmioApiDocBundle.php
+++ b/NelmioApiDocBundle.php
@@ -3,7 +3,15 @@
namespace Nelmio\ApiDocBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Nelmio\ApiDocBundle\DependencyInjection\RegisterExtractorParsersPass;
class NelmioApiDocBundle extends Bundle
{
+ public function build(ContainerBuilder $container)
+ {
+ parent::build($container);
+
+ $container->addCompilerPass(new RegisterExtractorParsersPass());
+ }
}
diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php
index 8f62eab..6a25257 100644
--- a/Parser/FormTypeParser.php
+++ b/Parser/FormTypeParser.php
@@ -11,10 +11,10 @@
namespace Nelmio\ApiDocBundle\Parser;
-use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormFactoryInterface;
+use Symfony\Component\Form\Exception\FormException;
-class FormTypeParser
+class FormTypeParser implements ParserInterface
{
/**
* @var \Symfony\Component\Form\FormFactoryInterface
@@ -42,13 +42,25 @@ class FormTypeParser
}
/**
- * Returns an array of data where each data is an array with the following keys:
- * - dataType
- * - required
- * - description
- *
- * @param string|\Symfony\Component\Form\FormTypeInterface $type
- * @return array
+ * {@inheritdoc}
+ */
+ public function supports($item)
+ {
+ try {
+ if (is_string($item) && class_exists($item)) {
+ $item = new $item();
+ }
+
+ $form = $this->formFactory->create($item);
+ } catch (FormException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
*/
public function parse($type)
{
@@ -93,6 +105,7 @@ class FormTypeParser
'dataType' => $bestType,
'required' => $config->getRequired(),
'description' => $config->getAttribute('description'),
+ 'readonly' => $config->getDisabled(),
);
}
diff --git a/Parser/ParserInterface.php b/Parser/ParserInterface.php
new file mode 100644
index 0000000..8df98f5
--- /dev/null
+++ b/Parser/ParserInterface.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Parser;
+
+/**
+ * This is the interface parsers must implement in order to be registered in the ApiDocExtractor.
+ */
+interface ParserInterface
+{
+ /**
+ * Return true/false whether this class supports parsing the given class.
+ *
+ * @param string $item The string type of input to parse.
+ * @return boolean
+ */
+ public function supports($item);
+
+ /**
+ * Returns an array of class property metadata where each item is a key (the property name) and
+ * an array of data with the following keys:
+ * - dataType string
+ * - required boolean
+ * - description string
+ * - readonly boolean
+ *
+ * @param string $item The string type of input to parse.
+ * @return array
+ */
+ public function parse($item);
+
+}
diff --git a/README.md b/README.md
index 56772e9..ae19234 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ class YourController extends Controller
/**
* @ApiDoc(
* description="Create a new Object",
- * formType="Your\Namespace\Form\Type\YourType"
+ * input="Your\Namespace\Form\Type\YourType"
* )
*/
public function postAction()
@@ -99,16 +99,16 @@ The following properties are available:
* `filters`: an array of filters;
-* `formType`: the Form Type associated to the method, useful for POST|PUT methods, either as FQCN or
+* `input`: the input type associated to the method, currently this only supports Form Types, useful for POST|PUT methods, either as FQCN or
as form type (if it is registered in the form factory in the container)
Each _filter_ has to define a `name` parameter, but other parameters are free. Filters are often optional
parameters, and you can document them as you want, but keep in mind to be consistent for the whole documentation.
-If you set a `formType`, then the bundle automatically extracts parameters based on the given type,
+If you set `input`, then the bundle automatically extracts parameters based on the given type,
and determines for each parameter its data type, and if it's required or not.
-You can add an extra option named `description` on each field:
+For Form Types, you can add an extra option named `description` on each field:
``` php
+
-
diff --git a/Tests/Annotation/ApiDocTest.php b/Tests/Annotation/ApiDocTest.php
index d1904a6..d8f3212 100644
--- a/Tests/Annotation/ApiDocTest.php
+++ b/Tests/Annotation/ApiDocTest.php
@@ -27,7 +27,7 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource());
$this->assertFalse(isset($array['description']));
- $this->assertNull($annot->getFormType());
+ $this->assertNull($annot->getInput());
}
public function testConstructWithInvalidData()
@@ -44,7 +44,7 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource());
$this->assertFalse(isset($array['description']));
- $this->assertNull($annot->getFormType());
+ $this->assertNull($annot->getInput());
}
public function testConstruct()
@@ -60,14 +60,14 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource());
$this->assertEquals($data['description'], $array['description']);
- $this->assertNull($annot->getFormType());
+ $this->assertNull($annot->getInput());
}
public function testConstructDefinesAFormType()
{
$data = array(
'description' => 'Heya',
- 'formType' => 'My\Form\Type',
+ 'input' => 'My\Form\Type',
);
$annot = new ApiDoc($data);
@@ -77,7 +77,7 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource());
$this->assertEquals($data['description'], $array['description']);
- $this->assertEquals($data['formType'], $annot->getFormType());
+ $this->assertEquals($data['input'], $annot->getInput());
}
public function testConstructMethodIsResource()
@@ -85,7 +85,7 @@ class ApiDocTest extends TestCase
$data = array(
'resource' => true,
'description' => 'Heya',
- 'formType' => 'My\Form\Type',
+ 'input' => 'My\Form\Type',
);
$annot = new ApiDoc($data);
@@ -95,7 +95,7 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertTrue($annot->isResource());
$this->assertEquals($data['description'], $array['description']);
- $this->assertEquals($data['formType'], $annot->getFormType());
+ $this->assertEquals($data['input'], $annot->getInput());
}
public function testConstructMethodResourceIsFalse()
@@ -103,7 +103,7 @@ class ApiDocTest extends TestCase
$data = array(
'resource' => false,
'description' => 'Heya',
- 'formType' => 'My\Form\Type',
+ 'input' => 'My\Form\Type',
);
$annot = new ApiDoc($data);
@@ -113,7 +113,7 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource());
$this->assertEquals($data['description'], $array['description']);
- $this->assertEquals($data['formType'], $annot->getFormType());
+ $this->assertEquals($data['input'], $annot->getInput());
}
public function testConstructMethodHasFilters()
@@ -135,7 +135,7 @@ class ApiDocTest extends TestCase
$this->assertEquals(array('a-filter' => array()), $array['filters']);
$this->assertTrue($annot->isResource());
$this->assertEquals($data['description'], $array['description']);
- $this->assertNull($annot->getFormType());
+ $this->assertNull($annot->getInput());
}
/**
@@ -158,7 +158,7 @@ class ApiDocTest extends TestCase
$data = array(
'resource' => true,
'description' => 'Heya',
- 'formType' => 'My\Form\Type',
+ 'input' => 'My\Form\Type',
'filters' => array(
array('name' => 'a-filter'),
),
@@ -171,6 +171,6 @@ class ApiDocTest extends TestCase
$this->assertFalse(isset($array['filters']));
$this->assertTrue($annot->isResource());
$this->assertEquals($data['description'], $array['description']);
- $this->assertEquals($data['formType'], $annot->getFormType());
+ $this->assertEquals($data['input'], $annot->getInput());
}
}
diff --git a/Tests/Extractor/ApiDocExtratorTest.php b/Tests/Extractor/ApiDocExtratorTest.php
index a71a955..b092643 100644
--- a/Tests/Extractor/ApiDocExtratorTest.php
+++ b/Tests/Extractor/ApiDocExtratorTest.php
@@ -39,28 +39,28 @@ class ApiDocExtractorTest extends WebTestCase
$this->assertTrue($a1->isResource());
$this->assertEquals('index action', $a1->getDescription());
$this->assertTrue(is_array($array1['filters']));
- $this->assertNull($a1->getFormType());
+ $this->assertNull($a1->getInput());
$a1 = $data[1]['annotation'];
$array1 = $a1->toArray();
$this->assertTrue($a1->isResource());
$this->assertEquals('index action', $a1->getDescription());
$this->assertTrue(is_array($array1['filters']));
- $this->assertNull($a1->getFormType());
+ $this->assertNull($a1->getInput());
$a2 = $data[2]['annotation'];
$array2 = $a2->toArray();
$this->assertFalse($a2->isResource());
$this->assertEquals('create test', $a2->getDescription());
$this->assertFalse(isset($array2['filters']));
- $this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getFormType());
+ $this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
$a2 = $data[3]['annotation'];
$array2 = $a2->toArray();
$this->assertFalse($a2->isResource());
$this->assertEquals('create test', $a2->getDescription());
$this->assertFalse(isset($array2['filters']));
- $this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getFormType());
+ $this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
}
public function testGet()
@@ -76,7 +76,7 @@ class ApiDocExtractorTest extends WebTestCase
$array = $annotation->toArray();
$this->assertTrue(is_array($array['filters']));
- $this->assertNull($annotation->getFormType());
+ $this->assertNull($annotation->getInput());
$annotation2 = $extractor->get('nemlio.test.controller:indexAction', 'test_service_route_1');
$annotation2->getRoute()
diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php
index 1995a77..d39a02a 100644
--- a/Tests/Fixtures/Controller/TestController.php
+++ b/Tests/Fixtures/Controller/TestController.php
@@ -35,7 +35,7 @@ class TestController
/**
* @ApiDoc(
* description="create test",
- * formType="Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType"
+ * input="Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType"
* )
*/
public function postTestAction()
@@ -76,7 +76,7 @@ class TestController
/**
* @ApiDoc(
* description="create another test",
- * formType="dependency_type"
+ * input="dependency_type"
* )
*/
public function anotherPostAction()
diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php
index f717228..9a1e93a 100644
--- a/Tests/Formatter/SimpleFormatterTest.php
+++ b/Tests/Formatter/SimpleFormatterTest.php
@@ -81,18 +81,21 @@ class SimpleFormatterTest extends WebTestCase
'dataType' => 'string',
'required' => true,
'description' => 'A nice description',
+ 'readonly' => false
),
'b' =>
array(
'dataType' => 'string',
'required' => false,
'description' => '',
+ 'readonly' => false
),
'c' =>
array(
'dataType' => 'boolean',
'required' => true,
'description' => '',
+ 'readonly' => false
),
),
'description' => 'create test',
@@ -108,18 +111,21 @@ class SimpleFormatterTest extends WebTestCase
'dataType' => 'string',
'required' => true,
'description' => 'A nice description',
+ 'readonly' => false
),
'b' =>
array(
'dataType' => 'string',
'required' => false,
'description' => '',
+ 'readonly' => false
),
'c' =>
array(
'dataType' => 'boolean',
'required' => true,
'description' => '',
+ 'readonly' => false
),
),
'description' => 'create test',
@@ -138,6 +144,7 @@ class SimpleFormatterTest extends WebTestCase
'dataType' => 'string',
'required' => true,
'description' => 'A nice description',
+ 'readonly' => false
),
),
'description' => 'create another test',