diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php index 41ac909..dfc592b 100644 --- a/Parser/JmsMetadataParser.php +++ b/Parser/JmsMetadataParser.php @@ -61,26 +61,97 @@ class JmsMetadataParser implements ParserInterface if (!is_null($item->type)) { $name = isset($item->serializedName) ? $item->serializedName : $item->name; - //TODO: check for nested type + $dataType = $this->processDataType($item->type); $params[$name] = array( - 'dataType' => $item->type, + 'dataType' => $dataType['normalized'], 'required' => false, //TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this 'description' => $this->getDescription($input, $item->name), 'readonly' => $item->readOnly ); + + //check for nested classes with JMS metadata + if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) { + $params[$name]['children'] = $this->parse($dataType['class']); + } } } return $params; } + /** + * Figure out a normalized data type (for documentation), and get a + * nested class name, if available. + * + * @param string $type + * @return array + */ + protected function processDataType($type) + { + //could be basic type + if ($this->isPrimitive($type)) { + return array( + 'normalized' => $type, + 'class' => null + ); + } + + //check for a type inside something that could be treated as an array + if ($nestedType = $this->getNestedTypeInArray($type)) { + if ($this->isPrimitive($nestedType)) { + return array( + 'normalized' => sprintf("array of %ss", $nestedType), + 'class' => null + ); + } + + $exp = explode("\\", $nestedType); + + return array( + 'normalized' => sprintf("array of objects (%s)", end($exp)), + 'class' => $nestedType + ); + } + + //if we got this far, it's a general class name + $exp = explode("\\", $type); + + return array( + 'normalized' => sprintf("object (%s)", end($exp)), + 'class' => $type + ); + } + + protected function isPrimitive($type) + { + return in_array($type, array('boolean', 'integer', 'string', 'double', 'array', 'DateTime')); + } + + /** + * Check the various ways JMS describes values in arrays, and + * get the value type in the array + * + * @param string $type + * @return string|null + */ + protected function getNestedTypeInArray($type) + { + //could be some type of array with , or + $regEx = "/\<([A-Za-z0-9\\\]*)(\,?\s?(.*))?\>/"; + if (preg_match($regEx, $type, $matches)) { + return (!empty($matches[3])) ? $matches[3] : $matches[1]; + } + + return null; + } + protected function getDescription($className, $propertyName) { $description = "No description."; - //TODO: regex comment to get description - or move doc comment parsing functionality from `ApiDocExtractor` to a new location - //in order to reuse it here + //TODO: abstract docblock parsing utility and implement here + return $description; } diff --git a/Parser/ParserInterface.php b/Parser/ParserInterface.php index 8df98f5..fcdadf2 100644 --- a/Parser/ParserInterface.php +++ b/Parser/ParserInterface.php @@ -31,6 +31,8 @@ interface ParserInterface * - required boolean * - description string * - readonly boolean + * - children (optional) array of nested property names mapped to arrays + * in the format described here * * @param string $item The string type of input to parse. * @return array diff --git a/Tests/Fixtures/Model/JmsNested.php b/Tests/Fixtures/Model/JmsNested.php new file mode 100644 index 0000000..9008446 --- /dev/null +++ b/Tests/Fixtures/Model/JmsNested.php @@ -0,0 +1,27 @@ +") + */ + public $baz; + +} diff --git a/Tests/Fixtures/Model/JmsTest.php b/Tests/Fixtures/Model/JmsTest.php index ff44443..d47741c 100644 --- a/Tests/Fixtures/Model/JmsTest.php +++ b/Tests/Fixtures/Model/JmsTest.php @@ -30,4 +30,14 @@ class JmsTest */ public $arr; + /** + * @JMS\Type("Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested"); + */ + public $nested; + + /** + * @JMS\Type("array"); + */ + public $nestedArray; + } diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index 57edb02..fc25e6b 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -185,6 +185,18 @@ arr: * required: false * description: No description. +nested: + + * type: object (JmsNested) + * required: false + * description: No description. + +nestedArray: + + * type: array of objects (JmsNested) + * required: false + * description: No description. + ### `GET` /jms-return-test ### diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php index 9012ac8..0caddcd 100644 --- a/Tests/Formatter/SimpleFormatterTest.php +++ b/Tests/Formatter/SimpleFormatterTest.php @@ -210,7 +210,59 @@ class SimpleFormatterTest extends WebTestCase 'required' => false, 'description' => 'No description.', 'readonly' => false - ) + ), + 'nested' => array( + 'dataType' => 'object (JmsNested)', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false, + 'children' => array( + 'foo' => array( + 'dataType' => 'DateTime', + 'required' => false, + 'description' => 'No description.', + 'readonly' => true, + ), + 'bar' => array( + 'dataType' => 'string', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false, + ), + 'baz' => array( + 'dataType' => 'array of integers', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false, + ) + ) + ), + 'nestedArray' => array( + 'dataType' => 'array of objects (JmsNested)', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false, + 'children' => array( + 'foo' => array( + 'dataType' => 'DateTime', + 'required' => false, + 'description' => 'No description.', + 'readonly' => true, + ), + 'bar' => array( + 'dataType' => 'string', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false, + ), + 'baz' => array( + 'dataType' => 'array of integers', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false, + ) + ) + ), ), 'description' => 'Testing JMS' ),