Built parse-merging into the ApiDocExtractor.

Wired up a "post-parse" pass to allow recursive parsing across multiple parsers.
This commit is contained in:
Josh Hall-Bachner 2013-06-30 18:46:00 -07:00
parent 0913157399
commit 5e1549a29d
6 changed files with 89 additions and 16 deletions

@ -268,10 +268,17 @@ class ApiDocExtractor
$normalizedInput = $this->normalizeClassParameter($input);
$parameters = array();
foreach ($this->parsers as $parser) {
if ($parser->supports($normalizedInput)) {
$parameters = $parser->parse($normalizedInput);
break;
$parameters = $this->mergeParameters($parameters, $parser->parse($normalizedInput));
}
}
foreach($this->parsers as $parser) {
if($parser->supports($normalizedInput) && method_exists($parser, 'postParse')) {
$mp = $parser->postParse($normalizedInput, $parameters);
$parameters = $this->mergeParameters($parameters, $mp);
}
}
@ -294,7 +301,6 @@ class ApiDocExtractor
foreach ($this->parsers as $parser) {
if ($parser->supports($normalizedOutput)) {
$response = $parser->parse($normalizedOutput);
break;
}
}
@ -374,6 +380,45 @@ class ApiDocExtractor
return array_merge($defaults, $input);
}
protected function mergeParameters($p1, $p2)
{
$params = array();
foreach($p2 as $propname => $propvalue) {
if(!isset($p1[$propname])) {
$params[$propname] = $propvalue;
} else {
$v1 = $p1[$propname];
foreach($propvalue as $name => $value) {
if(is_array($value)) {
if(isset($v1[$name]) && is_array($v1[$name])) {
$v1[$name] = $this->mergeParameters($v1[$name], $value);
} else {
$v1[$name] = $value;
}
} elseif(!is_null($value)) {
if(in_array($name, array('required', 'readonly'))) {
$v1[$name] = $v1[$name] || $value;
} elseif($name == 'requirement') {
if(isset($v1[$name])) {
$v1[$name] .= ', ' . $value;
} else {
$v1[$name] = $value;
}
} else {
$v1[$name] = $value;
}
}
}
$params[$propname] = $v1;
}
}
return $params;
}
/**
* Parses annotations for a given method, and adds new information to the given ApiDoc
* annotation. Useful to extract information from the FOSRestBundle annotations.

@ -69,10 +69,10 @@ abstract class AbstractFormatter implements FormatterInterface
$newName = $this->getNewName($name, $info, $parentName);
$newParams[$newName] = array(
'description' => $info['description'],
'dataType' => $info['dataType'],
'readonly' => $info['readonly'],
'required' => $info['required'],
'description' => array_key_exists('description', $info) ? $info['description'] : null,
'format' => array_key_exists('format', $info) ? $info['format'] : null,
'sinceVersion' => array_key_exists('sinceVersion', $info) ? $info['sinceVersion'] : null,
'untilVersion' => array_key_exists('untilVersion', $info) ? $info['untilVersion'] : null,

@ -87,7 +87,6 @@ class FormTypeParser implements ParserInterface
private function parseForm($form, $prefix = null)
{
$className = get_class($form);
$parameters = array();
foreach ($form as $name => $child) {
$config = $child->getConfig();
@ -133,6 +132,8 @@ class FormTypeParser implements ParserInterface
'description' => $config->getAttribute('description'),
'readonly' => $config->getDisabled(),
);
} else {
$parameters[$name]['class'] = $type;
}
continue;

@ -126,6 +126,10 @@ class JmsMetadataParser implements ParserInterface
'untilVersion' => $item->untilVersion,
);
if(!is_null($dataType['class'])) {
$params[$name]['class'] = $dataType['class'];
}
// if class already parsed, continue, to avoid infinite recursion
if (in_array($dataType['class'], $visited)) {
continue;

@ -6,7 +6,7 @@ use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Constraint;
class SymfonyValidationParser implements ParserInterface
class ValidationParser implements ParserInterface
{
/**
* @var \Symfony\Component\Validator\MetadataFactoryInterface
@ -33,26 +33,49 @@ class SymfonyValidationParser implements ParserInterface
*/
public function parse(array $input)
{
$vparams = array();
$params = array();
$className = $input['class'];
$classdata = $this->factory->getMetadataFor($className);
$properties = $classdata->getConstrainedProperties();
if($classdata->hasPropertyMetadata($name)) {
$propdata = $classdata->getPropertyMetadata($name);
$propdata = reset($propdata);
$constraints = $propdata->getConstraints();
foreach($properties as $property) {
$vparams = array();
$pds = $classdata->getPropertyMetadata($property);
foreach($pds as $propdata) {
$constraints = $propdata->getConstraints();
foreach($constraints as $constraint) {
$vparams = $this->parseConstraint($constraint, $vparams);
foreach($constraints as $constraint) {
$vparams = $this->parseConstraint($constraint, $vparams);
}
}
if(isset($vparams['format'])) {
$vparams['format'] = join(', ', $vparams['format']);
}
foreach(array('dataType', 'readonly', 'required') as $reqprop) {
if(!isset($vparams[$reqprop])) {
$vparams[$reqprop] = null;
}
}
$params[$property] = $vparams;
}
return $vparams;
return $params;
}
public function postParse(array $input, $parameters)
{
foreach($parameters as $param => $data) {
if(isset($data['class']) && isset($data['children'])) {
$input = array('class' => $data['class']);
$parameters[$param]['children'] = $this->parse($input, $parameters[$param]['children']);
}
}
return $parameters;
}
protected function parseConstraint(Constraint $constraint, $vparams)

@ -5,7 +5,7 @@
<parameters>
<parameter key="nelmio_api_doc.parser.form_type_parser.class">Nelmio\ApiDocBundle\Parser\FormTypeParser</parameter>
<parameter key="nelmio_api_doc.parser.symfony_validation_parser.class">Nelmio\ApiDocBundle\Parser\SymfonyValidationParser</parameter>
<parameter key="nelmio_api_doc.parser.validation_parser.class">Nelmio\ApiDocBundle\Parser\ValidationParser</parameter>
<parameter key="nelmio_api_doc.formatter.abstract_formatter.class">Nelmio\ApiDocBundle\Formatter\AbstractFormatter</parameter>
<parameter key="nelmio_api_doc.formatter.markdown_formatter.class">Nelmio\ApiDocBundle\Formatter\MarkdownFormatter</parameter>
<parameter key="nelmio_api_doc.formatter.simple_formatter.class">Nelmio\ApiDocBundle\Formatter\SimpleFormatter</parameter>
@ -19,7 +19,7 @@
<argument type="service" id="form.registry" />
<tag name="nelmio_api_doc.extractor.parser" />
</service>
<service id="nelmio_api_doc.parser.symfony_validation_parser" class="%nelmio_api_doc.parser.symfony_validation_parser.class%">
<service id="nelmio_api_doc.parser.validation_parser" class="%nelmio_api_doc.parser.validation_parser.class%">
<argument type="service" id="validator.mapping.class_metadata_factory"/>
<tag name="nelmio_api_doc.extractor.parser" />
</service>