diff --git a/Formatter/AbstractFormatter.php b/Formatter/AbstractFormatter.php
index b5d2fc7..f521a4a 100644
--- a/Formatter/AbstractFormatter.php
+++ b/Formatter/AbstractFormatter.php
@@ -73,6 +73,7 @@ abstract class AbstractFormatter implements FormatterInterface
'dataType' => $info['dataType'],
'readonly' => $info['readonly'],
'required' => $info['required'],
+ '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,
);
diff --git a/NelmioApiDocBundle.php b/NelmioApiDocBundle.php
index c165888..bfce996 100644
--- a/NelmioApiDocBundle.php
+++ b/NelmioApiDocBundle.php
@@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Nelmio\ApiDocBundle\DependencyInjection\RegisterJmsParserPass;
use Nelmio\ApiDocBundle\DependencyInjection\RegisterExtractorParsersPass;
use Nelmio\ApiDocBundle\DependencyInjection\ExtractorHandlerCompilerPass;
+use Nelmio\ApiDocBundle\DependencyInjection\ParserHandlerCompilerPass;
class NelmioApiDocBundle extends Bundle
{
diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php
index f04a2d9..f55f913 100644
--- a/Parser/FormTypeParser.php
+++ b/Parser/FormTypeParser.php
@@ -23,6 +23,11 @@ class FormTypeParser implements ParserInterface
*/
protected $formFactory;
+ /**
+ * @var \Symfony\Component\Form\FormRegistry
+ */
+ protected $formRegistry;
+
/**
* @var array
*/
@@ -82,6 +87,7 @@ class FormTypeParser implements ParserInterface
private function parseForm($form, $prefix = null)
{
+ $className = get_class($form);
$parameters = array();
foreach ($form as $name => $child) {
$config = $child->getConfig();
diff --git a/Parser/SymfonyValidationParser.php b/Parser/SymfonyValidationParser.php
new file mode 100644
index 0000000..f1294cf
--- /dev/null
+++ b/Parser/SymfonyValidationParser.php
@@ -0,0 +1,115 @@
+factory = $factory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports(array $input)
+ {
+ $className = $input['class'];
+
+ return $this->factory->hasMetadataFor($className);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function parse(array $input)
+ {
+ $vparams = array();
+ $className = $input['class'];
+
+ $classdata = $this->factory->getMetadataFor($className);
+
+ if($classdata->hasPropertyMetadata($name)) {
+ $propdata = $classdata->getPropertyMetadata($name);
+ $propdata = reset($propdata);
+ $constraints = $propdata->getConstraints();
+
+ foreach($constraints as $constraint) {
+ $vparams = $this->parseConstraint($constraint, $vparams);
+ }
+
+ if(isset($vparams['format'])) {
+ $vparams['format'] = join(', ', $vparams['format']);
+ }
+ }
+
+ return $vparams;
+ }
+
+ protected function parseConstraint(Constraint $constraint, $vparams)
+ {
+ $class = substr(get_class($constraint), strlen('Symfony\\Component\\Validator\\Constraints\\'));
+
+ 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();
+ if(isset($constraint->min)) {
+ $messages[] = "min: {$constraint->min}";
+ }
+ if(isset($constraint->max)) {
+ $messages[] = "max: {$constraint->max}";
+ }
+ $vparams['format'][] = '{length: ' . join(', ', $messages) . '}';
+ break;
+ case 'Choice':
+ $format = '[' . join('|', $constraint->choices) . ']';
+ if($constraint->multiple) {
+ $messages = array();
+ if(isset($constraint->min)) {
+ $messages[] = "min: {$constraint->min} ";
+ }
+ if(isset($constraint->max)) {
+ $messages[] = "max: {$constraint->max} ";
+ }
+ $vparams['format'][] = '{' . join ('', $messages) . 'choice of ' . $format . '}';
+ } else {
+ $vparams['format'][] = $format;
+ }
+ break;
+ case 'Regex':
+ if($constraint->match) {
+ $vparams['format'][] = '{match: ' . $constraint->pattern . '}';
+ } else {
+ $vparams['format'][] = '{not match: ' . $constraint->pattern . '}';
+ }
+ break;
+ }
+
+ return $vparams;
+ }
+}
\ No newline at end of file
diff --git a/Resources/config/formatters.xml b/Resources/config/formatters.xml
index 53c31be..6e23df1 100644
--- a/Resources/config/formatters.xml
+++ b/Resources/config/formatters.xml
@@ -5,6 +5,7 @@
= - - + -
{% endif %} {% endfor %} diff --git a/Tests/Fixtures/Model/ValidatorTest.php b/Tests/Fixtures/Model/ValidatorTest.php new file mode 100644 index 0000000..46ad6b9 --- /dev/null +++ b/Tests/Fixtures/Model/ValidatorTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Tests\Fixtures\Model; + +use Symfony\Component\Validator\Constraints as Assert; + +class ValidatorTest +{ + /** + * @Assert\Length(min=10); + */ + public $length10; + + /** + * @Assert\Length(min=1, max=10) + */ + public $length1to10; + + /** + * @Assert\NotBlank() + */ + public $notblank; + + /** + * @Assert\NotNull() + */ + public $notnull; + + /** + * @Assert\Type("DateTime"); + */ + public $type; + + /** + * @Assert\Email() + */ + public $email; + + /** + * @Assert\Url() + */ + public $url; + + /** + * @Assert\Ip() + */ + public $ip; + + /** + * @Assert\Choice(choices={"a", "b"}) + */ + public $singlechoice; + + /** + * @Assert\Choice(choices={"x", "y", "z"}, multiple=true) + */ + public $multiplechoice; + + /** + * @Assert\Choice(choices={"foo", "bar", "baz", "qux"}, multiple=true, min=2, max=3) + */ + public $multiplerangechoice; + + /** + * @Assert\Regex(pattern="/^\d{1,4}\w{1,4}$/") + */ + public $regexmatch; + + /** + * @Assert\Regex(pattern="/\d/", match=false) + */ + public $regexnomatch; + + /** + * @Assert\NotNull() + * @Assert\Type("string") + * @Assert\Email() + */ + public $multipleassertions; + + /** + * @Assert\Url() + * @Assert\Length(min=10) + */ + public $multipleformats; +} diff --git a/Tests/Parser/SymfonyValidationHandlerTest.php b/Tests/Parser/SymfonyValidationHandlerTest.php new file mode 100644 index 0000000..4deb76b --- /dev/null +++ b/Tests/Parser/SymfonyValidationHandlerTest.php @@ -0,0 +1,128 @@ +getContainer(); + $factory = $container->get('validator.mapping.class_metadata_factory'); + + $this->handler = new SymfonyValidationHandler($factory); + } + + /** + * @dataProvider dataTestHandler + */ + public function testHandler($property, $expected) + { + $result = $this->handler->handle('Nelmio\ApiDocBundle\Tests\Fixtures\Model\ValidatorTest', $property, array()); + + $this->assertEquals($expected, $result); + } + + + public function dataTestHandler() + { + return array( + array( + 'property' => 'length10', + 'expected' => array( + 'format' => '{length: min: 10}' + ) + ), + array( + 'property' => 'length1to10', + 'expected' => array( + 'format' => '{length: min: 1, max: 10}' + ) + ), + array( + 'property' => 'notblank', + 'expected' => array( + 'required' => true + ) + ), + array( + 'property' => 'notnull', + 'expected' => array( + 'required' => true + ) + ), + array( + 'property' => 'type', + 'expected' => array( + 'dataType' => 'DateTime' + ) + ), + array( + 'property' => 'email', + 'expected' => array( + 'format' => '{email address}' + ) + ), + array( + 'property' => 'url', + 'expected' => array( + 'format' => '{url}' + ) + ), + array( + 'property' => 'ip', + 'expected' => array( + 'format' => '{ip address}' + ) + ), + array( + 'property' => 'singlechoice', + 'expected' => array( + 'format' => '[a|b]' + ) + ), + array( + 'property' => 'multiplechoice', + 'expected' => array( + 'format' => '{choice of [x|y|z]}' + ) + ), + array( + 'property' => 'multiplerangechoice', + 'expected' => array( + 'format' => '{min: 2 max: 3 choice of [foo|bar|baz|qux]}' + ) + ), + array( + 'property' => 'regexmatch', + 'expected' => array( + 'format' => '{match: /^\d{1,4}\w{1,4}$/}' + ) + ), + array( + 'property' => 'regexnomatch', + 'expected' => array( + 'format' => '{not match: /\d/}' + ) + ), + array( + 'property' => 'multipleassertions', + 'expected' => array( + 'required' => true, + 'dataType' => 'string', + 'format' => '{email address}' + ) + ), + array( + 'property' => 'multipleformats', + 'expected' => array( + 'format' => '{url}, {length: min: 10}' + ) + ) + ); + } +}