NelmioApiDocBundle/Parser/ValidationParser.php

206 lines
6.5 KiB
PHP
Raw Normal View History

<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Parser;
2013-11-06 15:25:10 +01:00
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Constraint;
/**
* Uses the Symfony Validation component to extract information about API objects.
*/
class ValidationParser implements ParserInterface, PostParserInterface
{
/**
* @var \Symfony\Component\Validator\MetadataFactoryInterface
*/
protected $factory;
/**
* Requires a validation MetadataFactory.
*
* @param MetadataFactoryInterface $factory
*/
public function __construct(MetadataFactoryInterface $factory)
{
$this->factory = $factory;
}
/**
* {@inheritdoc}
*/
public function supports(array $input)
{
$className = $input['class'];
return $this->factory->hasMetadataFor($className);
}
/**
* {@inheritdoc}
*/
public function parse(array $input)
{
$params = array();
$className = $input['class'];
$classdata = $this->factory->getMetadataFor($className);
$properties = $classdata->getConstrainedProperties();
2013-11-14 10:28:42 +01:00
foreach ($properties as $property) {
$vparams = array();
$pds = $classdata->getPropertyMetadata($property);
2013-11-14 10:28:42 +01:00
foreach ($pds as $propdata) {
$constraints = $propdata->getConstraints();
2013-11-14 10:28:42 +01:00
foreach ($constraints as $constraint) {
2013-11-06 15:25:10 +01:00
$vparams = $this->parseConstraint($constraint, $vparams, $className);
}
}
2013-11-14 10:28:42 +01:00
if (isset($vparams['format'])) {
$vparams['format'] = join(', ', $vparams['format']);
}
2013-11-14 10:28:42 +01:00
foreach (array('dataType', 'readonly', 'required') as $reqprop) {
if (!isset($vparams[$reqprop])) {
$vparams[$reqprop] = null;
}
}
$params[$property] = $vparams;
}
return $params;
}
/**
* {@inheritDoc}
*/
public function postParse(array $input, array $parameters)
{
2013-11-14 10:28:42 +01:00
foreach ($parameters as $param => $data) {
if (isset($data['class']) && isset($data['children'])) {
$input = array('class' => $data['class']);
$parameters[$param]['children'] = array_merge(
$parameters[$param]['children'], $this->postParse($input, $parameters[$param]['children'])
);
$parameters[$param]['children'] = array_merge(
$parameters[$param]['children'], $this->parse($input, $parameters[$param]['children'])
);
}
}
return $parameters;
}
/**
* Create a valid documentation parameter based on an individual validation Constraint.
* Currently supports:
* - NotBlank/NotNull
* - Type
* - Email
* - Url
* - Ip
* - Length (min and max)
* - Choice (single and multiple, min and max)
* - Regex (match and non-match)
*
2013-11-14 10:28:42 +01:00
* @param Constraint $constraint The constraint metadata object.
* @param array $vparams The existing validation parameters.
* @return mixed The parsed list of validation parameters.
*/
2013-11-06 15:25:10 +01:00
protected function parseConstraint(Constraint $constraint, $vparams, $className)
{
$class = substr(get_class($constraint), strlen('Symfony\\Component\\Validator\\Constraints\\'));
2013-11-14 10:28:42 +01:00
switch ($class) {
case 'NotBlank':
case 'NotNull':
$vparams['required'] = true;
break;
case 'Type':
$vparams['dataType'] = $constraint->type;
break;
case 'Email':
$vparams['format'][] = '{email address}';
break;
case 'Url':
$vparams['format'][] = '{url}';
break;
case 'Ip':
$vparams['format'][] = '{ip address}';
break;
case 'Length':
$messages = array();
2013-11-14 10:28:42 +01:00
if (isset($constraint->min)) {
$messages[] = "min: {$constraint->min}";
}
2013-11-14 10:28:42 +01:00
if (isset($constraint->max)) {
$messages[] = "max: {$constraint->max}";
}
$vparams['format'][] = '{length: ' . join(', ', $messages) . '}';
break;
case 'Choice':
2013-11-06 15:25:10 +01:00
$choices = $this->getChoices($constraint, $className);
$format = '[' . join('|', $choices) . ']';
2013-11-14 10:28:42 +01:00
if ($constraint->multiple) {
$messages = array();
2013-11-14 10:28:42 +01:00
if (isset($constraint->min)) {
$messages[] = "min: {$constraint->min} ";
}
2013-11-14 10:28:42 +01:00
if (isset($constraint->max)) {
$messages[] = "max: {$constraint->max} ";
}
$vparams['format'][] = '{' . join ('', $messages) . 'choice of ' . $format . '}';
} else {
$vparams['format'][] = $format;
}
break;
case 'Regex':
2013-11-14 10:28:42 +01:00
if ($constraint->match) {
$vparams['format'][] = '{match: ' . $constraint->pattern . '}';
} else {
$vparams['format'][] = '{not match: ' . $constraint->pattern . '}';
}
break;
}
return $vparams;
}
2013-11-06 15:25:10 +01:00
/**
* Return Choice constraint choices.
*
* @param Constraint $constraint
* @param $className
* @return array
* @throws \Symfony\Component\Validator\Exception\ConstraintDefinitionException
*/
2013-11-14 10:28:42 +01:00
protected function getChoices(Constraint $constraint, $className)
2013-11-06 15:25:10 +01:00
{
if ($constraint->callback) {
if (is_callable(array($className, $constraint->callback))) {
$choices = call_user_func(array($className, $constraint->callback));
} elseif (is_callable($constraint->callback)) {
$choices = call_user_func($constraint->callback);
} else {
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback');
}
} else {
$choices = $constraint->choices;
}
return $choices;
}
}