diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php index 34535bf..ced2ac0 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 - $params[$name] = array( - 'dataType' => $item->type, + 'dataType' => $this->getNormalizedType($item->type), '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 w/ JMS metadata + if ($nestedInputClass = $this->getNestedClass($item->type)) { + $params[$name]['children'] = $this->parse($nestedInputClass); + } } } return $params; } + + /** + * There are various ways via JMS to declare arrays of objects, but that's an internal + * implementation detail. + * + * @param string $type + * @return string + */ + protected function getNormalizedType($type) + { + if (in_array($type, array('boolean', 'integer', 'string', 'double', 'array', 'DateTime'))) { + return $type; + } + + if(false !== strpos($type, "array") || false !== strpos($type, "ArrayCollection")) { + if ($nested = $this->getNestedClassInArray($type)) { + $exp = explode("\\", $nested); + return sprintf("array of objects (%s)", end($exp)); + } else { + return "array"; + } + } + + $exp = explode("\\", $type); + return sprintf("object (%s)", end($exp)); + } + + /** + * Check the various ways JMS describes custom classes in arrays, and + * get the name of the class in the array, if available. + * + * @param string $type + * @return string|false + */ + protected function getNestedClassInArray($type) + { + + //could be some type of array with , or + $regEx = "/\<([A-Za-z0-9\\\]*)(\,?\s?(.*))?\>/"; + if (preg_match($regEx, $type, $matches)) { + $matched = (!empty($matches[3])) ? $matches[3] : $matches[1]; + return in_array($matched, array('boolean', 'integer', 'string', 'double', 'array', 'DateTime')) ? false : $matched; + } + + return false; + } + + /** + * Scan the JMS Serializer types for reference to a class. + * + * http://jmsyst.com/bundles/JMSSerializerBundle/master/reference/annotations#type + * + * @param string $type + * @return string|false + */ + protected function getNestedClass($type) + { + if (in_array($type, array('boolean', 'integer', 'string', 'double', 'array', 'DateTime'))) { + return false; + } + + //could be a nested object of some sort + if ($nested = $this->getNestedClassInArray($type)) { + return $nested; + } + + //or could just be a class name (potentially) + return (null === $this->factory->getMetadataForClass($type)) ? false : $type; + } 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..86ba05c --- /dev/null +++ b/Tests/Fixtures/Model/JmsNested.php @@ -0,0 +1,20 @@ +"); + */ + public $nestedArray; } diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index d058ae5..779f1a9 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. + ### `ANY` /my-commented/{id}/{page} ### diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php index 6868d28..e02f130 100644 --- a/Tests/Formatter/SimpleFormatterTest.php +++ b/Tests/Formatter/SimpleFormatterTest.php @@ -210,7 +210,47 @@ 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, + ) + ) + ), + '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, + ) + ) + ), ), 'description' => 'Testing JMS' ),