diff --git a/Command/GenerateJsValidationConstraintsCommand.php b/Command/GenerateJsValidationConstraintsCommand.php
new file mode 100755
index 0000000..4ff7c7e
--- /dev/null
+++ b/Command/GenerateJsValidationConstraintsCommand.php
@@ -0,0 +1,57 @@
+
+ */
+class GenerateJsValidationConstraintsCommand extends ContainerAwareCommand
+{
+ protected function configure()
+ {
+ $this
+ ->setName('formextra:jsvalidation:generate')
+ ->setDescription('Generates javascript validation constraints')
+ ->addOption('target', null, InputOption::VALUE_OPTIONAL, 'The target directory', 'web/js')
+ ->addOption('name', null, InputOption::VALUE_OPTIONAL, 'The file name', 'javascript-validation-constraints.js')
+ ->addOption('variable', null, InputOption::VALUE_OPTIONAL, 'The javscript variable name', 'jsFormExtraValidationConstraints')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $target = rtrim($input->getOption('target'), '/');
+
+ if (!is_dir($target)) {
+ throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $target));
+ }
+
+ $objects = $this->getContainer()->getParameter('simple_things_form_extra.client_validation.objects');
+ $generator = $this->getContainer()->get('simple_things_form_extra.js_validation_constraints_generator');
+ $constraints = $generator->generate($objects);
+
+ $file = $target . '/' . $input->getOption('name');
+ $variable = $input->getOption('variable');
+
+ file_put_contents($file, sprintf('var %s = ', $variable).$constraints);
+ $output->writeln(sprintf('Generate javascript validation constraints in %s', $file));
+ $output->writeln(sprintf('The javascript variable name is %s', $variable));
+ }
+}
+
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
old mode 100644
new mode 100755
index 4c2edfb..3a4b7cb
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -1,4 +1,15 @@
root('simple_things_form_extra')
->children()
+ ->arrayNode('client_validation')
+ ->canBeUnset()
+ ->children()
+ ->arrayNode('objects')
+ ->prototype('scalar')->end()
+ ->end()
+ ->end()
+ ->end()
->booleanNode('translation_domain_forward_compat')->defaultFalse()->end()
->booleanNode('help_extension')->defaultFalse()->end()
->arrayNode('recaptcha')
diff --git a/DependencyInjection/SimpleThingsFormExtraExtension.php b/DependencyInjection/SimpleThingsFormExtraExtension.php
index 25190d9..5856fa7 100755
--- a/DependencyInjection/SimpleThingsFormExtraExtension.php
+++ b/DependencyInjection/SimpleThingsFormExtraExtension.php
@@ -31,6 +31,11 @@ public function load(array $configs, ContainerBuilder $container)
$container->getDefinition('simple_things_form_extra.service.recaptcha')->replaceArgument(1, $config['recaptcha']['private_key']);
$container->getDefinition('simple_things_form_extra.form.type.recaptcha')->replaceArgument(1, $config['recaptcha']['public_key']);
}
+
+ if (isset($config['client_validation'])) {
+ $loader->load('client_validation.xml');
+ $container->setParameter('simple_things_form_extra.client_validation.objects', $config['client_validation']['objects']);
+ }
}
public function getAlias()
diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php
new file mode 100755
index 0000000..7aa2c11
--- /dev/null
+++ b/Extension/ValidationExtension.php
@@ -0,0 +1,78 @@
+
+ */
+class ValidationExtension extends \Twig_Extension
+{
+ /**
+ *
+ * @var JsValidationConstraintsGenerator
+ */
+ private $generator;
+
+ /**
+ *
+ * @var array
+ */
+ private $objects;
+
+ /**
+ *
+ * @param ValidatorInterface $validator
+ * @param array $objects
+ */
+ public function __construct(JsValidationConstraintsGenerator $generator, array $objects)
+ {
+ $this->generator = $generator;
+ $this->objects = $objects;
+ }
+
+ /**
+ * Returns a list of functions to add to the existing list.
+ *
+ * @return array An array of functions
+ */
+ public function getFunctions()
+ {
+ return array(
+ 'simplethings_formextra_validation' => new \Twig_Function_Method($this, 'getValidationConstraints', array('is_safe' => array('html'))),
+ );
+ }
+
+ /**
+ * Generates a JSON representation of the validation constraints that are
+ * exported to the client-side.
+ *
+ * @return string
+ */
+ public function getValidationConstraints()
+ {
+ return $this->generator->generate($this->objects);
+ }
+
+ /**
+ * Returns the name of the extension.
+ *
+ * @return string The extension name
+ */
+ public function getName()
+ {
+ return 'simplethings_formextra_validation';
+ }
+}
diff --git a/Form/Extension/ErrorAttrTypeExtension.php b/Form/Extension/ErrorAttrTypeExtension.php
new file mode 100644
index 0000000..ce19cb1
--- /dev/null
+++ b/Form/Extension/ErrorAttrTypeExtension.php
@@ -0,0 +1,49 @@
+
+ */
+class ErrorAttrTypeExtension extends AbstractTypeExtension
+{
+
+ protected $translator;
+
+ public function __construct(Translator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ /**
+ * @return string
+ */
+ public function getExtendedType()
+ {
+ return 'field';
+ }
+
+ public function buildView(FormView $view, FormInterface $form)
+ {
+ $errors = array();
+ $fieldErrors = $form->getErrors();
+ foreach ($fieldErrors as $fieldError) {
+ $errors[] = $this->translator->trans($fieldError->getMessageTemplate(), $fieldError->getMessageParameters(), 'validators');
+ }
+
+ if($errors) {
+ $attr = $view->get('attr');
+ if(!isset($attr['data-error'])) {
+ $attr['data-error'] = implode("
", $errors);
+ $view->set('attr', $attr);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Form/Extension/ValidationTypeExtension.php b/Form/Extension/ValidationTypeExtension.php
new file mode 100644
index 0000000..120bdc1
--- /dev/null
+++ b/Form/Extension/ValidationTypeExtension.php
@@ -0,0 +1,46 @@
+
+ */
+class ValidationTypeExtension extends AbstractTypeExtension
+{
+ private $validatedObjects = array();
+
+ public function __construct($validatedObjects)
+ {
+ $this->validatedObjects = array_flip($validatedObjects);
+ }
+
+ /**
+ * @return string
+ */
+ public function getExtendedType()
+ {
+ return 'form';
+ }
+
+ /**
+ * @param FormBuilder $builder
+ * @param array $options
+ */
+ public function buildForm(FormBuilder $builder, array $options)
+ {
+ if (isset($this->validatedObjects[$options['data_class']])) {
+ $attr = $builder->getAttribute('attr');
+ if(!isset($attr['data-simplethings-validation-class'])) {
+ $attr['data-simplethings-validation-class'] = $options['data_class'];
+ $builder->setAttribute('attr', $attr);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3e975dc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,10 @@
+Copyright (c) 2011-2012, SimpleThings GmbH
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/Resources/config/client_validation.xml b/Resources/config/client_validation.xml
new file mode 100644
index 0000000..43120c7
--- /dev/null
+++ b/Resources/config/client_validation.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ SimpleThings\FormExtraBundle\Service\JsValidationConstraintsGenerator
+ SimpleThings\FormExtraBundle\Form\Extension\ValidationTypeExtension
+ SimpleThings\FormExtraBundle\Form\Extension\ErrorAttrTypeExtension
+ SimpleThings\FormExtraBundle\Extension\ValidationExtension
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js
new file mode 100644
index 0000000..1d54cf3
--- /dev/null
+++ b/Resources/public/js/jquery.plugin.js
@@ -0,0 +1,53 @@
+/**
+ * @author David Badura
+ */
+(function($, jQuery) {
+ jQuery.fn.simpleThingsFormExtraValidation = function (options) {
+ options = jQuery.extend({}, jQuery.fn.simpleThingsFormExtraValidation.defaults, options);
+
+ return $(this).each(function() {
+
+ var objectName = $(this).data('simplethings-validation-class');
+
+ if(typeof options.constraints[objectName] != 'undefined') {
+ $(this).find('input, select').each(function() {
+
+ var $this = $(this);
+
+ var name = $this.attr('name');
+ name = name.substr(name.indexOf("[") + 1, name.indexOf("]") - name.indexOf("[") - 1);
+
+ options.onCreate($this);
+
+ $this.bind('change', function() {
+ if(typeof options.constraints[objectName][name] != 'undefined') {
+ if(options.validator.isValid($this.val(), options.constraints[objectName][name])) {
+ $this.trigger({
+ type: 'validationSuccess'
+ });
+ } else {
+ $this.trigger({
+ type: 'validationError',
+ violations: options.validator.violations
+ });
+ }
+ }
+ });
+
+ $this.bind('blur', function() {
+ $(this).trigger('change');
+ });
+
+ });
+ }
+
+ });
+ };
+
+ jQuery.fn.simpleThingsFormExtraValidation.defaults = {
+ validator: null,
+ constraints: null,
+ onCreate: function(object) {}
+ };
+
+})(jQuery, jQuery);
\ No newline at end of file
diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js
new file mode 100644
index 0000000..c12e383
--- /dev/null
+++ b/Resources/public/js/validation.js
@@ -0,0 +1,308 @@
+/**
+ * @author David Badura
+ */
+var simpleThingsFormExtraValidator = {
+
+ violations: [],
+
+ isValid: function(value, constraints) {
+ this.violations = [];
+ for(var constraint in constraints){
+ if(typeof this.constraints[constraint] == 'function') {
+ var re = this.constraints[constraint](value, constraints[constraint], this);
+ if(!re) {
+ return false;
+ }
+ }
+ }
+ return true;
+ },
+
+ addViolation: function(message, value, constraint) {
+ this.violations.push(new this.violation(message, value, constraint));
+ },
+
+ violation: function(message, value, params) {
+
+ this.message = message;
+ this.value = value;
+ this.params = params;
+
+ this.getMessage = function() {
+ var message = this.message;
+ for(var key in this.params) {
+ message = message.replace(key, this.params[key]);
+ }
+ return message;
+ }
+
+ },
+
+ constraints: {
+ max: function (value, constraint, validator) {
+ if (isNaN(value.replace(/,/, "."))) {
+ validator.addViolation(constraint.invalidMessage, value, {
+ '{{ limit }}': constraint.limit,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ if (parseFloat(value) > parseFloat(constraint.limit)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ limit }}': constraint.limit,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ min: function (value, constraint, validator) {
+ if (isNaN(value.replace(/,/, "."))) {
+ validator.addViolation(constraint.invalidMessage, value, {
+ '{{ limit }}': constraint.limit,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ if (parseFloat(value) < parseFloat(constraint.limit)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ limit }}': constraint.limit,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ blank: function (value, constraint, validator) {
+ if ('' != value && null != value) {
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ email: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
+ if (!reg.test(value)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ ip: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ var reg = /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
+ if(!reg.test(value)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ maxLength: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ if(value.length > parseFloat(constraint.limit)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ limit }}': constraint.limit,
+ '{{ value }}': value
+ });
+ return false;
+ }
+ return true;
+ },
+ minLength: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ if(value.length < parseFloat(constraint.limit)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ limit }}': constraint.limit,
+ '{{ value }}': value
+ });
+ return false;
+ }
+ return true;
+ },
+ notBlank: function (value, constraint, validator) {
+ if ('' == value || null == value) {
+ validator.addViolation(constraint.message, value);
+ return false;
+ }
+
+ return true;
+ },
+ notNull: function (value, constraint, validator) {
+ if ('' == value || null == value) {
+ validator.addViolation(constraint.message, value);
+ return false;
+ }
+
+ return true;
+ },
+ regex: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ var reg = new RegExp(constraint.pattern);
+ if(!reg.test(value)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ size: function (value, constraint, validator) {
+ if (isNaN(value.replace(/,/, "."))) {
+ validator.addViolation(constraint.invalidMessage, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ if( parseFloat(value) < parseFloat(constraint.min)) {
+ validator.addViolation(constraint.minMessage, value, {
+ '{{ limit }}': constraint.min,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ if( parseFloat(value) > parseFloat(constraint.max)) {
+ validator.addViolation(constraint.maxMessage, value, {
+ '{{ limit }}': constraint.max,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ sizeLength: function (value, constraint, validator) {
+ if( value.length < parseFloat(constraint.min)) {
+ validator.addViolation(constraint.minMessage, value, {
+ '{{ limit }}': constraint.min,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ if( value.length > parseFloat(constraint.max)) {
+ validator.addViolation(constraint.maxMessage, value, {
+ '{{ limit }}': constraint.max,
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ time: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ var reg = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?$/;
+ if(!reg.test(value)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ url: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ var reg = /(?:https?:\/\/(?:(?:(?:(?:(?:[a-zA-Z\d](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?))|(?:(?:\d+)(?:\.(?:\d+)){3}))(?::(?:\d+))?)(?:\/(?:(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{2}))|[;:@&=])*)(?:\/(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{2}))|[;:@&=])*))*)(?:\?(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{2}))|[;:@&=])*))?)?)/;
+ if(!reg.test(value)) {
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value
+ });
+ return false;
+ }
+
+ return true;
+ },
+ type: function (value, constraint, validator) {
+ if (null == value || '' == value) {
+ return true;
+ }
+
+ switch(constraint.type) {
+ case 'int':
+ case 'integer':
+ case 'digit':
+ if(value.indexOf('.') == -1 && isNaN(value) == false && parseInt(value) == value) {
+ return true;
+ }
+ break;
+ case 'numeric':
+ if(isNaN(value.replace(/,/, ".")) == false) {
+ return true;
+ }
+ break;
+ case 'string':
+ if(typeof(value) == 'string') {
+ return true;
+ }
+ break;
+ default:
+ return true;
+ }
+
+ validator.addViolation(constraint.message, value, {
+ '{{ value }}': value,
+ '{{ type }}': constraint.type
+ });
+ return false;
+ },
+ 'true': function (value, constraint, validator) {
+ if(!value) {
+ validator.addViolation(constraint.message, value);
+ return false;
+ }
+
+ return true;
+ },
+ 'false': function (value, constraint, validator) {
+ if (null == value) {
+ return true;
+ }
+
+ if (false == value || 0 == value) {
+ return true;
+ }
+
+ validator.addViolation(constraint.message, value);
+ return false;
+ }
+ }
+};
\ No newline at end of file
diff --git a/Service/JsValidationConstraintsGenerator.php b/Service/JsValidationConstraintsGenerator.php
new file mode 100644
index 0000000..e565c4b
--- /dev/null
+++ b/Service/JsValidationConstraintsGenerator.php
@@ -0,0 +1,108 @@
+
+ */
+class JsValidationConstraintsGenerator
+{
+ /**
+ * @var ClassMetadataFactoryInterface
+ */
+ protected $metadataFactory;
+
+ /**
+ * @var Translator
+ */
+ protected $translator;
+
+ /**
+ * @var string
+ */
+ protected $defaultLocale;
+
+ /**
+ * @param metadataFactoryInterface $metadataFactory
+ */
+ public function __construct(ClassMetadataFactoryInterface $metadataFactory, Translator $translator = null, $defaultLocale = null)
+ {
+ $this->metadataFactory = $metadataFactory;
+ $this->translator = $translator;
+ $this->defaultLocale = $defaultLocale;
+ }
+
+ /**
+ * @param array $classNames
+ * @return string
+ */
+ public function generate(array $classNames)
+ {
+ $data = array();
+ foreach ($classNames as $className) {
+ $data[$className] = $this->generateClass($className);
+ }
+
+ return json_encode($data);
+ }
+
+ /**
+ * Generate array representation of constraints that is exported to JSON.
+ *
+ * @todo export class constraints
+ * @todo filter exported contraints?
+ * @param string $className
+ * @return array
+ */
+ public function generateClass($className)
+ {
+ $data = array();
+
+ $metadata = $this->metadataFactory->getClassMetadata($className);
+ $properties = $metadata->getConstrainedProperties();
+
+ foreach ($properties as $property) {
+ $data[$property] = array();
+ $constraintsList = $metadata->getMemberMetadatas($property);
+ foreach ($constraintsList as $constraints) {
+ foreach ($constraints->constraints as $constraint) {
+ $const = clone $constraint;
+ if ($this->translator) {
+ $const->message = $this->translator->trans($const->message, array(), 'validations', $this->defaultLocale);
+ }
+ $data[$property][$this->getConstraintName($const)] = $const;
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @todo Only shorten symfony ones and underscore/camlize others?
+ * @param Constraint $constraint
+ * @return string
+ */
+ protected function getConstraintName(Constraint $constraint)
+ {
+ $class = get_class($constraint);
+ $parts = explode('\\', $class);
+ return lcfirst(array_pop($parts));
+ }
+}
+
diff --git a/Tests/DependencyInjection/SimpleThingsFormExtraExtensionTest.php b/Tests/DependencyInjection/SimpleThingsFormExtraExtensionTest.php
index 9b7d968..7e4b7f8 100644
--- a/Tests/DependencyInjection/SimpleThingsFormExtraExtensionTest.php
+++ b/Tests/DependencyInjection/SimpleThingsFormExtraExtensionTest.php
@@ -89,6 +89,18 @@ public function testLoadTranslationDomain()
}
}
+ public function testLoadClientValidation()
+ {
+ $container = new ContainerBuilder();
+ $extension = new SimpleThingsFormExtraExtension();
+ $extension->load(array(array(
+ 'client_validation' => array('objects' => array('stdClass')),
+ )), $container);
+
+ $this->assertTrue($container->hasDefinition('simple_things_form_extra.form.extension.validation'));
+ $this->assertEquals(array('stdClass'), $container->getParameter('simple_things_form_extra.client_validation.objects'));
+ }
+
public function testLoadAllowsEmptyConfig()
{
$container = new ContainerBuilder();
diff --git a/Tests/Services/JsValidationConstraintsGeneratorTest.php b/Tests/Services/JsValidationConstraintsGeneratorTest.php
new file mode 100644
index 0000000..55361a8
--- /dev/null
+++ b/Tests/Services/JsValidationConstraintsGeneratorTest.php
@@ -0,0 +1,68 @@
+metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
+ $this->generator = new JsValidationConstraintsGenerator($this->metadataFactory);
+ }
+
+ public function testGenerateEmpty()
+ {
+ $this->assertEquals("[]", $this->generator->generate(array()));
+ }
+
+ public function testGenerate()
+ {
+ $constraint = new Min(array('limit' => 10));
+
+ $cm = new ClassMetadata(__NAMESPACE__ . '\\TestEntity');
+ $cm->addPropertyConstraint('field1', $constraint);
+ $this->metadataFactory->expects($this->once())->method('getClassMetadata')->will($this->returnValue($cm));
+
+ $data = $this->generator->generate(array(__NAMESPACE__ . '\\TestEntity'));
+ $this->assertEquals('{"SimpleThings\\\\FormExtraBundle\\\\Tests\\\\Services\\\\TestEntity":{"field1":{"min":{"message":"This value should be {{ limit }} or more","invalidMessage":"This value should be a valid number","limit":10,"groups":["Default","TestEntity"]}}}}', $data);
+ }
+
+ public function testGenerateClass()
+ {
+ $constraint = new Min(array('limit' => 10));
+
+ $cm = new ClassMetadata(__NAMESPACE__ . '\\TestEntity');
+ $cm->addPropertyConstraint('field1', $constraint);
+ $this->metadataFactory->expects($this->once())->method('getClassMetadata')->will($this->returnValue($cm));
+
+ $data = $this->generator->generateClass(__NAMESPACE__ . '\\TestEntity');
+
+ $this->assertArrayHasKey('field1', $data);
+ $this->assertArrayHasKey('min', $data['field1']);
+ $this->assertEquals($constraint, $data['field1']['min']);
+ }
+}
+
+class TestEntity
+{
+ public $field1;
+}
+