From 06e3a2256bd89800c03b0478d5f26e61625a050f Mon Sep 17 00:00:00 2001 From: Evan Villemez Date: Thu, 19 Jul 2012 17:56:49 -0400 Subject: [PATCH] added a ParserInterface, refactored how Parsers are registered in the ApiDocExtractor, changed formType to inputClass in ApiDocExtractor --- Annotation/ApiDoc.php | 14 ++++--- .../RegisterExtractorClassParsersPass.php | 23 +++++++++++ Extractor/ApiDocExtractor.php | 32 +++++++++++---- NelmioApiDocBundle.php | 8 ++++ Parser/FormTypeParser.php | 24 +++++++---- Parser/ParserInterface.php | 40 +++++++++++++++++++ Resources/config/formatters.xml | 1 + Resources/config/services.xml | 1 - Tests/Annotation/ApiDocTest.php | 24 +++++------ Tests/Extractor/ApiDocExtratorTest.php | 10 ++--- Tests/Fixtures/Controller/TestController.php | 4 +- 11 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 DependencyInjection/RegisterExtractorClassParsersPass.php create mode 100644 Parser/ParserInterface.php diff --git a/Annotation/ApiDoc.php b/Annotation/ApiDoc.php index 031814b..603227e 100644 --- a/Annotation/ApiDoc.php +++ b/Annotation/ApiDoc.php @@ -26,7 +26,7 @@ class ApiDoc /** * @var string */ - private $formType = null; + private $inputClass = 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['inputClass'])) { + $this->inputClass = $data['inputClass']; } 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 getInputClass() { - return $this->formType; + return $this->inputClass; } /** @@ -227,6 +227,10 @@ class ApiDoc if ($requirements = $this->requirements) { $data['requirements'] = $requirements; } + + if ($inputClass = $this->inputClass) { + $data['inputClass'] = $inputClass; + } return $data; } diff --git a/DependencyInjection/RegisterExtractorClassParsersPass.php b/DependencyInjection/RegisterExtractorClassParsersPass.php new file mode 100644 index 0000000..a7467fd --- /dev/null +++ b/DependencyInjection/RegisterExtractorClassParsersPass.php @@ -0,0 +1,23 @@ +hasDefinition('nelmio_api_doc.extractor.api_doc_extractor')) { + return; + } + + $definition = $container->getDefinition('nelmio_api_doc.extractor.api_doc_extractor'); + + foreach ($container->findTaggedServiceIds('nelmio_api_doc.extractor.class_parser') as $id => $attributes) { + $definition->addMethodCall('registerParser', array(new Reference($id))); + } + } +} \ No newline at end of file diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index a45dcf6..3678320 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; } /** @@ -178,6 +177,17 @@ class ApiDocExtractor return null; } + + /** + * Registers a class parser to use for parsing input class metadata + * + * @param ParserInterface $parser + * @return void + */ + public function registerParser(ParserInterface $parser) + { + $this->parsers[] = $parser; + } /** * Returns a new ApiDoc instance with more data. @@ -215,9 +225,15 @@ class ApiDocExtractor // doc $annotation->setDocumentation($this->getDocCommentText($method)); - // formType - if (null !== $formType = $annotation->getFormType()) { - $parameters = $this->parser->parse($formType); + // inputClass + if (null !== $inputClass = $annotation->getInputClass()) { + $parameters = array(); + + foreach ($this->parsers as $parser) { + if ($parser->supportsClass($inputClass)) { + $parameters = $parser->parse($inputClass); + } + } if ('PUT' === $method) { // All parameters are optional with PUT (update) diff --git a/NelmioApiDocBundle.php b/NelmioApiDocBundle.php index b42aba2..4c3a0a5 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\RegisterExtractorClassParsersPass; class NelmioApiDocBundle extends Bundle { + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new RegisterExtractorClassParsersPass()); + } } diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php index 4b0b11a..142f73d 100644 --- a/Parser/FormTypeParser.php +++ b/Parser/FormTypeParser.php @@ -14,7 +14,7 @@ namespace Nelmio\ApiDocBundle\Parser; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormFactoryInterface; -class FormTypeParser +class FormTypeParser implements ParserInterface { /** * @var \Symfony\Component\Form\FormFactoryInterface @@ -39,15 +39,22 @@ class FormTypeParser { $this->formFactory = $formFactory; } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + if (is_string($class) && class_exists($class)) { + $ref = new \ReflectionClass($class); + return ($ref->implementsInterface('Nelmio\ApiDocBundle\Parser\ParserInterface')); + } + + return false; + } /** - * 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 parse($type) { @@ -71,6 +78,7 @@ class FormTypeParser 'dataType' => $bestType, 'required' => $config->getRequired(), 'description' => $config->getAttribute('description'), + 'readonly' => false, ); } diff --git a/Parser/ParserInterface.php b/Parser/ParserInterface.php new file mode 100644 index 0000000..e22fb4d --- /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 name of the class to parse. + * @return boolean + */ + function supportsClass($className); + + /** + * 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 $class The string name of the class to parse. + * @return array + */ + function parse($className); + +} \ No newline at end of file diff --git a/Resources/config/formatters.xml b/Resources/config/formatters.xml index d4e7984..6079c67 100644 --- a/Resources/config/formatters.xml +++ b/Resources/config/formatters.xml @@ -14,6 +14,7 @@ + - diff --git a/Tests/Annotation/ApiDocTest.php b/Tests/Annotation/ApiDocTest.php index d1904a6..993bd74 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->getInputClass()); } 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->getInputClass()); } 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->getInputClass()); } public function testConstructDefinesAFormType() { $data = array( 'description' => 'Heya', - 'formType' => 'My\Form\Type', + 'inputClass' => '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['inputClass'], $annot->getInputClass()); } public function testConstructMethodIsResource() @@ -85,7 +85,7 @@ class ApiDocTest extends TestCase $data = array( 'resource' => true, 'description' => 'Heya', - 'formType' => 'My\Form\Type', + 'inputClass' => '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['inputClass'], $annot->getInputClass()); } public function testConstructMethodResourceIsFalse() @@ -103,7 +103,7 @@ class ApiDocTest extends TestCase $data = array( 'resource' => false, 'description' => 'Heya', - 'formType' => 'My\Form\Type', + 'inputClass' => '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['inputClass'], $annot->getInputClass()); } 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->getInputClass()); } /** @@ -158,7 +158,7 @@ class ApiDocTest extends TestCase $data = array( 'resource' => true, 'description' => 'Heya', - 'formType' => 'My\Form\Type', + 'inputClass' => '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['inputClass'], $annot->getInputClass()); } } diff --git a/Tests/Extractor/ApiDocExtratorTest.php b/Tests/Extractor/ApiDocExtratorTest.php index a71a955..b45bb92 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->getInputClass()); $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->getInputClass()); $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->getInputClass()); $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->getInputClass()); } 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->getInputClass()); $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..5275543 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" + * inputClass="Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType" * ) */ public function postTestAction() @@ -76,7 +76,7 @@ class TestController /** * @ApiDoc( * description="create another test", - * formType="dependency_type" + * inputClass="dependency_type" * ) */ public function anotherPostAction()