From 96f9b0a308444ff74bc76a33154e6ec6ab6b03d7 Mon Sep 17 00:00:00 2001 From: badura Date: Fri, 16 Dec 2011 17:07:30 +0100 Subject: [PATCH 01/29] javascript validation prototype --- DependencyInjection/Configuration.php | 9 +++++++++ DependencyInjection/SimpleThingsFormExtraExtension.php | 6 +++++- Resources/config/form_extra.xml | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) mode change 100644 => 100755 DependencyInjection/Configuration.php mode change 100644 => 100755 Resources/config/form_extra.xml diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php old mode 100644 new mode 100755 index c61d95b..6000dcf --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -13,6 +13,15 @@ public function getConfigTreeBuilder() return $builder->root('simple_things_form_extra') ->children() + ->arrayNode('client_validation') + ->canBeUnset() + ->children() + ->arrayNode('objects') + ->prototype('scalar')->end() + ->defaultValue(array()) + ->end() + ->end() + ->end() ->arrayNode('recaptcha') ->canBeUnset() ->children() diff --git a/DependencyInjection/SimpleThingsFormExtraExtension.php b/DependencyInjection/SimpleThingsFormExtraExtension.php index b458e5f..4f70e5c 100755 --- a/DependencyInjection/SimpleThingsFormExtraExtension.php +++ b/DependencyInjection/SimpleThingsFormExtraExtension.php @@ -17,12 +17,16 @@ public function load(array $configs, ContainerBuilder $container) $processor = new Processor(); $config = $processor->processConfiguration(new Configuration(), $configs); - + if (isset($config['recaptcha'])) { $loader->load('form_extra_recaptcha.xml'); $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'])) { + $container->setParameter('simple_things_form_extra.client_validation.objects', $config['client_validation']['objects']); + } } public function getAlias() diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml old mode 100644 new mode 100755 index 8735c42..2e8c47d --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -9,6 +9,8 @@ SimpleThings\FormExtraBundle\Form\Type\PlainType SimpleThings\FormExtraBundle\Form\Type\FileSetType SimpleThings\FormExtraBundle\Form\Extension\FieldTypeExtension + SimpleThings\FormExtraBundle\Form\Extension\ValidationTypeExtension + @@ -27,5 +29,12 @@ + + + + + %simple_things_form_extra.client_validation.objects% + + From 0fd424ac30f13de646c27bb0ca24c69b73074ba8 Mon Sep 17 00:00:00 2001 From: David Badura Date: Fri, 16 Dec 2011 18:08:24 +0100 Subject: [PATCH 02/29] validation extension --- Extension/ValidationExtension.php | 71 ++++++++ Form/Extension/ValidationTypeExtension.php | 56 ++++++ Resources/config/form_extra.xml | 6 +- Resources/public/js/jquery.plugin.js | 30 ++++ Resources/public/js/test.html | 21 +++ Resources/public/js/validation.js | 188 +++++++++++++++++++++ 6 files changed, 369 insertions(+), 3 deletions(-) create mode 100755 Extension/ValidationExtension.php create mode 100755 Form/Extension/ValidationTypeExtension.php create mode 100755 Resources/public/js/jquery.plugin.js create mode 100755 Resources/public/js/test.html create mode 100755 Resources/public/js/validation.js diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php new file mode 100755 index 0000000..13515a8 --- /dev/null +++ b/Extension/ValidationExtension.php @@ -0,0 +1,71 @@ + + */ +class ValidationExtension extends \Twig_Extension +{ + + /** + * + * @var ValidatorInterface + */ + private $validation; + + private $objects; + + public function __construct(ValidatorInterface $validation, array $objects) + { + $this->validation = $validation; + $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'), + ); + } + + public function getValidationConstraints() + { + + $metadataFactory = $this->validation->getMetadataFactory(); + + foreach($objects as $object) { + $metadata = $metadataFactory->getClassMetadata($object); + $properties = $metadata->getConstrainedProperties(); + + var_dump($properties); + + } + + + + + + + + } + + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'simplethings_formextra_validation'; + } +} diff --git a/Form/Extension/ValidationTypeExtension.php b/Form/Extension/ValidationTypeExtension.php new file mode 100755 index 0000000..e6c5ac1 --- /dev/null +++ b/Form/Extension/ValidationTypeExtension.php @@ -0,0 +1,56 @@ + + */ +class ValidationTypeExtension extends AbstractTypeExtension +{ + private $validatedObjects = array(); + + public function __construct($validatedObjects) + { + $this->validatedObjects = array_keys($validatedObjects); + } + + /** + * @return string + */ + public function getExtendedType() + { + return 'field'; + } + + /** + * @param FormBuilder $builder + * @param array $options + */ + public function buildForm(FormBuilder $builder, array $options) + { + if (isset($this->validatedObjects[$options['data_class']])) { + $builder->setAttribute('data_class', $options['data_class']); + } + } + + /** + * @param FormView $view + * @param FormInterface $form + */ + public function buildView(FormView $view, FormInterface $form) + { + if (isset($this->validatedObjects[$options['data_class']])) { + $attr = $view->get('attr', array()); + $attr['data-simplethings-validation-class'] = $form->getAttribute('data_class'); + $view->set('attr', $attr); + } + } +} diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml index 2e8c47d..169188e 100755 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -31,10 +31,10 @@ - - + %simple_things_form_extra.client_validation.objects% - + + diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js new file mode 100755 index 0000000..278a98d --- /dev/null +++ b/Resources/public/js/jquery.plugin.js @@ -0,0 +1,30 @@ +(function($, jQuery) { + jQuery.fn.simplethingsFormextraValidation = function (options) { + options = jQuery.extend({}, jQuery.fn.SimpleThingsFormExtraValidation.defaults, options); + + return $(this).each(function() { + + var self = $(this); + + self.change(function() { + + if(options.validator.isValid(self.val(), self.data('simplethings-validation-contraints'))) { + + self.removeClass('error'); + self.addClass('success'); + + } else { + + self.removeClass('success'); + self.addClass('error'); + + } + + }); + + }); + } + jQuery.fn.SimpleThingsFormExtraValidation.defaults = { + validator: simpleThingsFormExtraValidator + } +})(jQuery, jQuery); \ No newline at end of file diff --git a/Resources/public/js/test.html b/Resources/public/js/test.html new file mode 100755 index 0000000..b964cf2 --- /dev/null +++ b/Resources/public/js/test.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js new file mode 100755 index 0000000..9fe781d --- /dev/null +++ b/Resources/public/js/validation.js @@ -0,0 +1,188 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +var simpleThingsFormExtraValidator = { + + violations: [], + + isValid: function(value, constraints) { + this.violations = []; + for(var constraint in constraints){ + if(typeof simpleThingsFormExtraValidationContraints[constraint] == 'function') { + var re = simpleThingsFormExtraValidationContraints[constraint](value, constraints[constraint], this); + if(!re) { + return false; + } + } + } + return true; + }, + + addViolation: function(message, value, constraint) { + this.violations.push(new SimpleThingsFormExtraViolation(message, value, constraint)); + } + +}; + +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +function SimpleThingsFormExtraViolation (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; + } + +} + +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +var simpleThingsFormExtraValidationContraints = { + max: function (value, constraint) { + + if (!isNaN(value)) { + return false; + } + + if (value > constraint.limit) { + return false; + } + + return true; + }, + min: function (value, constraint) { + if (!isNaN(value)) { + return false; + } + + if (value < constraint.limit) { + return false; + } + + return true; + }, + blank: function (value, constraint) { + if ('' != value && null != value) { + return false; + } + + return true; + }, + email: function (value, constraint) { + var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; + if (!reg.test(value)) { + return false; + } + + return true; + }, + falseValidator: function (value, constraint) { + if (null == value) { + return true; + } + + if (false == value || 0 == value) { + return true; + } + + return false; + }, + ip: function (value, constraint) { + 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)) { + return false; + } + + return true; + }, + maxLength: function (value, constraint) { + if(value.length > constraint.limit) { + return false; + } + return true; + }, + minLength: function (value, constraint, validator) { + if(value.length < constraint.limit) { + validator.addMessage(constraint.message, value, {'{{ limit }}': constraint.limit}); + return false; + } + return true; + }, + notBlank: function (value, constraint) { + if ('' == value || null == value) { + return false; + } + + return true; + }, + regex: function (value, constraint) { + if (null == value || '' == value) { + return true; + } + + var reg = new RegExp(constraint.pattern); + if(!reg.test(value)) { + return false; + } + + return true; + }, + size: function (value, constraint) { + if( value < constraint.min) { + return false; + } + + if( value > constraint.max) { + return false; + } + + return true; + }, + sizeLength: function (value, constraint) { + if( value.length < constraint.min) { + return false; + } + + if( value.length > constraint.max) { + return false; + } + + return true; + }, + time: function (value, constraint) { + var reg = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/; + if(!reg.test(value)) { + return false; + } + + return true; + }, + trueValidation: function (value, constraint) { + if(!value) { + return false; + } + + return true; + }, + url: function (value, constraint) { + 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)) { + return false; + } + + return true; + } + +}; \ No newline at end of file From c68f99cd8a69ea7dace1de2e5b75f78b8e4fa832 Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 21 Dec 2011 16:19:20 +0100 Subject: [PATCH 03/29] javascript validator, jquery plugin, form and twig extension update --- Extension/ValidationExtension.php | 41 ++++--- Form/Extension/ValidationTypeExtension.php | 12 +- Resources/config/form_extra.xml | 10 +- Resources/public/js/jquery.plugin.js | 54 +++++---- Resources/public/js/validation.js | 125 ++++++++++++++------- 5 files changed, 155 insertions(+), 87 deletions(-) diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php index 13515a8..9cb4b74 100755 --- a/Extension/ValidationExtension.php +++ b/Extension/ValidationExtension.php @@ -1,8 +1,8 @@ validation = $validation; + $this->validator = $validator; $this->objects = $objects; } @@ -33,30 +33,41 @@ public function __construct(ValidatorInterface $validation, array $objects) public function getFunctions() { return array( - 'simplethings_formextra_validation' => new \Twig_Function_Method($this, 'getValidationConstraints'), + 'simplethings_formextra_validation' => new \Twig_Function_Method($this, 'getValidationConstraints', array('is_safe' => array('html'))), ); } public function getValidationConstraints() { - $metadataFactory = $this->validation->getMetadataFactory(); + $metadataFactory = $this->validator->getMetadataFactory(); + $data = array(); - foreach($objects as $object) { + foreach($this->objects as $object) { + $data[$object] = array(); + $metadata = $metadataFactory->getClassMetadata($object); $properties = $metadata->getConstrainedProperties(); - var_dump($properties); - + foreach($properties as $propertie) { + $data[$object][$propertie] = array(); + $constraints = $metadata->getMemberMetadatas($propertie); + foreach($constraints[0]->constraints as $constraint) { + $data[$object][$propertie][$this->getConstraintName($constraint)] = $constraint; + } + } } - - - - - + return \json_encode($data); } + + protected function getConstraintName($constraint) { + $class = \get_class($constraint); + $parts = \explode('\\', $class); + return \lcfirst(\array_pop($parts)); + } + /** diff --git a/Form/Extension/ValidationTypeExtension.php b/Form/Extension/ValidationTypeExtension.php index e6c5ac1..b337a52 100755 --- a/Form/Extension/ValidationTypeExtension.php +++ b/Form/Extension/ValidationTypeExtension.php @@ -8,9 +8,7 @@ use Symfony\Component\Form\FormBuilder; /** - * Generic extension for fields that allows any attr to be set when building - * the form. - * + * * @author David Badura */ class ValidationTypeExtension extends AbstractTypeExtension @@ -19,7 +17,7 @@ class ValidationTypeExtension extends AbstractTypeExtension public function __construct($validatedObjects) { - $this->validatedObjects = array_keys($validatedObjects); + $this->validatedObjects = array_flip($validatedObjects); } /** @@ -27,7 +25,7 @@ public function __construct($validatedObjects) */ public function getExtendedType() { - return 'field'; + return 'form'; } /** @@ -47,8 +45,8 @@ public function buildForm(FormBuilder $builder, array $options) */ public function buildView(FormView $view, FormInterface $form) { - if (isset($this->validatedObjects[$options['data_class']])) { - $attr = $view->get('attr', array()); + if ($form->hasAttribute('data_class')) { + $attr = $form->getAttribute('attr'); $attr['data-simplethings-validation-class'] = $form->getAttribute('data_class'); $view->set('attr', $attr); } diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml index 169188e..0df82d7 100755 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -10,6 +10,7 @@ SimpleThings\FormExtraBundle\Form\Type\FileSetType SimpleThings\FormExtraBundle\Form\Extension\FieldTypeExtension SimpleThings\FormExtraBundle\Form\Extension\ValidationTypeExtension + SimpleThings\FormExtraBundle\Extension\ValidationExtension @@ -31,10 +32,15 @@ - + %simple_things_form_extra.client_validation.objects% - + + + + + %simple_things_form_extra.client_validation.objects% + diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js index 278a98d..b6a52e8 100755 --- a/Resources/public/js/jquery.plugin.js +++ b/Resources/public/js/jquery.plugin.js @@ -1,30 +1,44 @@ +/** + * @author David Badura + */ (function($, jQuery) { - jQuery.fn.simplethingsFormextraValidation = function (options) { - options = jQuery.extend({}, jQuery.fn.SimpleThingsFormExtraValidation.defaults, options); - + jQuery.fn.simpleThingsFormExtraValidation = function (options) { + options = jQuery.extend({}, jQuery.fn.simpleThingsFormExtraValidation.defaults, options); + return $(this).each(function() { + + var objectName = $(this).data('simplethings-validation-class'); - var self = $(this); - - self.change(function() { + $(this).find('input').each(function() { - if(options.validator.isValid(self.val(), self.data('simplethings-validation-contraints'))) { - - self.removeClass('error'); - self.addClass('success'); - - } else { + var $this = $(this); + + var name = $this.attr('name'); + name = name.substr(name.indexOf("[") + 1, name.indexOf("]") - name.indexOf("[") - 1); - self.removeClass('success'); - self.addClass('error'); + $this.change(function() { - } + if(options.validator.isValid($this.val(), options.constraints[objectName][name])) { + options.onSuccess($this); + } else { + options.onError($this, options.validator.violations); + } + + }); }); + + + }); - } - jQuery.fn.SimpleThingsFormExtraValidation.defaults = { - validator: simpleThingsFormExtraValidator - } -})(jQuery, jQuery); \ No newline at end of file + }; + + jQuery.fn.simpleThingsFormExtraValidation.defaults = { + validator: null, + constraints: null, + onSuccess: function(object) {}, + onError: function(object, violations) {} + }; + +})(jQuery, jQuery); diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index 9fe781d..b51956a 100755 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -1,6 +1,5 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. +/** + * @author David Badura */ var simpleThingsFormExtraValidator = { @@ -25,10 +24,7 @@ var simpleThingsFormExtraValidator = { }; -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ + function SimpleThingsFormExtraViolation (message, value, params) { this.message = message; @@ -45,144 +41,187 @@ function SimpleThingsFormExtraViolation (message, value, params) { } -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ + var simpleThingsFormExtraValidationContraints = { - max: function (value, constraint) { - - if (!isNaN(value)) { + max: function (value, constraint, validator) { + if (isNaN(value)) { + validator.addViolation(constraint.invalidMessage, value); return false; } if (value > constraint.limit) { + validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); return false; } return true; }, - min: function (value, constraint) { - if (!isNaN(value)) { + min: function (value, constraint, validator) { + if (isNaN(value)) { + validator.addViolation(constraint.invalidMessage, value); return false; } if (value < constraint.limit) { + validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); return false; } return true; }, - blank: function (value, constraint) { + blank: function (value, constraint, validator) { if ('' != value && null != value) { + validator.addViolation(constraint.message, value); return false; } return true; }, - email: function (value, constraint) { + email: function (value, constraint, validator) { var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; if (!reg.test(value)) { + validator.addViolation(constraint.message, value); return false; } return true; }, - falseValidator: function (value, constraint) { - if (null == value) { - return true; - } - - if (false == value || 0 == value) { - return true; - } - - return false; - }, - ip: function (value, constraint) { + ip: function (value, constraint, validator) { 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); return false; } return true; }, - maxLength: function (value, constraint) { + maxLength: function (value, constraint, validator) { + if (null == value || '' == value) { + return true; + } + if(value.length > constraint.limit) { return false; } return true; }, minLength: function (value, constraint, validator) { + if (null == value || '' == value) { + return true; + } + if(value.length < constraint.limit) { - validator.addMessage(constraint.message, value, {'{{ limit }}': constraint.limit}); + validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); return false; } return true; }, - notBlank: function (value, constraint) { + notBlank: function (value, constraint, validator) { if ('' == value || null == value) { + validator.addViolation(constraint.message, value); return false; } return true; }, - regex: function (value, constraint) { + 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); return false; } return true; }, - size: function (value, constraint) { + size: function (value, constraint, validator) { + if (isNaN(value)) { + validator.addViolation(constraint.invalidMessage, value); + return false; + } + if( value < constraint.min) { + validator.addViolation(constraint.minMessage, value, {'{{ limit }}': constraint.limit}); return false; } if( value > constraint.max) { + validator.addViolation(constraint.maxMessage, value, {'{{ limit }}': constraint.limit}); return false; } return true; }, - sizeLength: function (value, constraint) { + sizeLength: function (value, constraint, validator) { if( value.length < constraint.min) { + validator.addViolation(constraint.minMessage, value, {'{{ limit }}': constraint.limit}); return false; } if( value.length > constraint.max) { + validator.addViolation(constraint.maxMessage, value, {'{{ limit }}': constraint.limit}); return false; } return true; }, - time: function (value, constraint) { + time: function (value, constraint, validator) { 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); return false; } return true; }, - trueValidation: function (value, constraint) { - if(!value) { + url: function (value, constraint, validator) { + 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); return false; } return true; }, - url: function (value, constraint) { - 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)) { + type: function (value, constraint, validator) { + switch(constraint) { + case 'numeric': + if(isNaN(value)) { + validator.addViolation(constraint.message, value); + return false; + } + break; + case 'string': + if(typeof(value) != 'string') { + validator.addViolation(constraint.message, value); + return false; + } + break; + } + + return true; + }, + '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 From c69d093a2e9ad3004d7139763db95e2f3868eedf Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 21 Dec 2011 20:45:25 +0100 Subject: [PATCH 04/29] validation twig extension update --- Extension/ValidationExtension.php | 50 +++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php index 9cb4b74..0b26db2 100755 --- a/Extension/ValidationExtension.php +++ b/Extension/ValidationExtension.php @@ -3,6 +3,7 @@ namespace SimpleThings\FormExtraBundle\Extension; use Symfony\Component\Validator\ValidatorInterface; +use Symfony\Component\Validator\Constraint; /** * @@ -17,8 +18,17 @@ class ValidationExtension extends \Twig_Extension */ private $validator; + /** + * + * @var array + */ private $objects; - + + /** + * + * @param ValidatorInterface $validator + * @param array $objects + */ public function __construct(ValidatorInterface $validator, array $objects) { $this->validator = $validator; @@ -37,38 +47,48 @@ public function getFunctions() ); } + /** + * + * @return string + */ public function getValidationConstraints() { $metadataFactory = $this->validator->getMetadataFactory(); $data = array(); - foreach($this->objects as $object) { + foreach ($this->objects as $object) { $data[$object] = array(); $metadata = $metadataFactory->getClassMetadata($object); $properties = $metadata->getConstrainedProperties(); - foreach($properties as $propertie) { - $data[$object][$propertie] = array(); - $constraints = $metadata->getMemberMetadatas($propertie); - foreach($constraints[0]->constraints as $constraint) { - $data[$object][$propertie][$this->getConstraintName($constraint)] = $constraint; + foreach ($properties as $property) { + $data[$object][$property] = array(); + $constraintsList = $metadata->getMemberMetadatas($property); + foreach ($constraintsList as $constraints) { + foreach ($constraints->constraints as $constraint) { + $data[$object][$property][$this->getConstraintName($constraint)] = $constraint; + } } } } - return \json_encode($data); + return json_encode($data); } - protected function getConstraintName($constraint) { - $class = \get_class($constraint); - $parts = \explode('\\', $class); - return \lcfirst(\array_pop($parts)); + /** + * + * @param Constraint $constraint + * @return string + */ + protected function getConstraintName(Constraint $constraint) + { + $class = get_class($constraint); + $parts = explode('\\', $class); + return lcfirst(array_pop($parts)); } - - /** * Returns the name of the extension. @@ -79,4 +99,4 @@ public function getName() { return 'simplethings_formextra_validation'; } -} +} \ No newline at end of file From a6c8b3565d1af57a0c89efcb73bc32e980b64097 Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 21 Dec 2011 20:52:55 +0100 Subject: [PATCH 05/29] form type extension update --- Form/Extension/ValidationTypeExtension.php | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Form/Extension/ValidationTypeExtension.php b/Form/Extension/ValidationTypeExtension.php index b337a52..ff546f5 100755 --- a/Form/Extension/ValidationTypeExtension.php +++ b/Form/Extension/ValidationTypeExtension.php @@ -35,20 +35,12 @@ public function getExtendedType() public function buildForm(FormBuilder $builder, array $options) { if (isset($this->validatedObjects[$options['data_class']])) { - $builder->setAttribute('data_class', $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); + } } } - - /** - * @param FormView $view - * @param FormInterface $form - */ - public function buildView(FormView $view, FormInterface $form) - { - if ($form->hasAttribute('data_class')) { - $attr = $form->getAttribute('attr'); - $attr['data-simplethings-validation-class'] = $form->getAttribute('data_class'); - $view->set('attr', $attr); - } - } -} + +} \ No newline at end of file From f5566734c68401a8af5afb04985fb86e76f95d0a Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 21 Dec 2011 20:58:03 +0100 Subject: [PATCH 06/29] remove useless defaultValue in prototyped node --- DependencyInjection/Configuration.php | 1 - 1 file changed, 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 6000dcf..605ddca 100755 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -18,7 +18,6 @@ public function getConfigTreeBuilder() ->children() ->arrayNode('objects') ->prototype('scalar')->end() - ->defaultValue(array()) ->end() ->end() ->end() From d76e0c95ba0bb7a3350f4feb446ccf2b46d0e1e8 Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 21 Dec 2011 21:08:17 +0100 Subject: [PATCH 07/29] maxLength constraint fix --- Resources/public/js/validation.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index b51956a..5e694e6 100755 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -101,6 +101,7 @@ var simpleThingsFormExtraValidationContraints = { } if(value.length > constraint.limit) { + validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); return false; } return true; From a8e16cce168d2e45c3d5e89b5bb7f0aa7eb3dc34 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Wed, 4 Jan 2012 11:32:32 +0100 Subject: [PATCH 08/29] remove test file --- Resources/public/js/test.html | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100755 Resources/public/js/test.html diff --git a/Resources/public/js/test.html b/Resources/public/js/test.html deleted file mode 100755 index b964cf2..0000000 --- a/Resources/public/js/test.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - From 35713e418eaa0d3071a623d85c45650803fc00eb Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Wed, 4 Jan 2012 15:26:09 +0100 Subject: [PATCH 09/29] validation constraint update, validation componentsmoved in the same namespace --- Resources/public/js/validation.js | 425 +++++++++++++++++------------- 1 file changed, 235 insertions(+), 190 deletions(-) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index 5e694e6..4cc31b0 100755 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -8,8 +8,8 @@ var simpleThingsFormExtraValidator = { isValid: function(value, constraints) { this.violations = []; for(var constraint in constraints){ - if(typeof simpleThingsFormExtraValidationContraints[constraint] == 'function') { - var re = simpleThingsFormExtraValidationContraints[constraint](value, constraints[constraint], this); + if(typeof this.constraints[constraint] == 'function') { + var re = this.constraints[constraint](value, constraints[constraint], this); if(!re) { return false; } @@ -19,210 +19,255 @@ var simpleThingsFormExtraValidator = { }, addViolation: function(message, value, constraint) { - this.violations.push(new SimpleThingsFormExtraViolation(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; -function SimpleThingsFormExtraViolation (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]); + this.getMessage = function() { + var message = this.message; + for(var key in this.params) { + message = message.replace(key, this.params[key]); + } + return message; } - return message; - } + + }, -} + constraints: { + max: function (value, constraint, validator) { + if (isNaN(value)) { + 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; + } -var simpleThingsFormExtraValidationContraints = { - max: function (value, constraint, validator) { - if (isNaN(value)) { - validator.addViolation(constraint.invalidMessage, value); - return false; - } - - if (value > constraint.limit) { - validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); - return false; - } - - return true; - }, - min: function (value, constraint, validator) { - if (isNaN(value)) { - validator.addViolation(constraint.invalidMessage, value); - return false; - } - - if (value < constraint.limit) { - validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); - return false; - } - - return true; - }, - blank: function (value, constraint, validator) { - if ('' != value && null != value) { - validator.addViolation(constraint.message, value); - return false; - } + return true; + }, + min: function (value, constraint, validator) { + if (isNaN(value)) { + 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; - }, - email: function (value, constraint, validator) { - var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; - if (!reg.test(value)) { - validator.addViolation(constraint.message, value); - return false; - } - - return true; - }, - ip: function (value, constraint, validator) { - 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); - return false; - } - - return true; - }, - maxLength: function (value, constraint, validator) { - if (null == value || '' == value) { return true; - } - - if(value.length > constraint.limit) { - validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); - return false; - } - return true; - }, - minLength: function (value, constraint, validator) { - if (null == value || '' == value) { + }, + 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) { + 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; - } - - if(value.length < constraint.limit) { - validator.addViolation(constraint.message, value, {'{{ limit }}': constraint.limit}); - return false; - } - return true; - }, - notBlank: function (value, constraint, validator) { - if ('' == value || null == value) { - validator.addViolation(constraint.message, value); - return false; - } + }, + ip: function (value, constraint, validator) { + 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; - }, - 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); - return false; - } + }, + maxLength: function (value, constraint, validator) { + if (null == value || '' == value) { + return true; + } - return true; - }, - size: function (value, constraint, validator) { - if (isNaN(value)) { - validator.addViolation(constraint.invalidMessage, value); - return false; - } - - if( value < constraint.min) { - validator.addViolation(constraint.minMessage, value, {'{{ limit }}': constraint.limit}); - return false; - } - - if( value > constraint.max) { - validator.addViolation(constraint.maxMessage, value, {'{{ limit }}': constraint.limit}); - return false; - } - - return true; - }, - sizeLength: function (value, constraint, validator) { - if( value.length < constraint.min) { - validator.addViolation(constraint.minMessage, value, {'{{ limit }}': constraint.limit}); - return false; - } - - if( value.length > constraint.max) { - validator.addViolation(constraint.maxMessage, value, {'{{ limit }}': constraint.limit}); - return false; - } - - return true; - }, - time: function (value, constraint, validator) { - 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); - return false; - } - - return true; - }, - url: function (value, constraint, validator) { - 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); - return false; - } - - return true; - }, - type: function (value, constraint, validator) { - switch(constraint) { - case 'numeric': - if(isNaN(value)) { - validator.addViolation(constraint.message, value); - return false; - } - break; - case 'string': - if(typeof(value) != 'string') { - validator.addViolation(constraint.message, value); - return false; - } - break; - } - - return true; - }, - 'true': function (value, constraint, validator) { - if(!value) { - validator.addViolation(constraint.message, value); - return false; - } - - return true; - }, - 'false': function (value, constraint, validator) { - if (null == value) { + 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; + }, + 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)) { + 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) { + 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) { + 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; + } - if (false == value || 0 == value) { return true; + }, + type: function (value, constraint, validator) { + switch(constraint.type) { + case 'numeric': + if(isNaN(value) == 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; } - - validator.addViolation(constraint.message, value); - return false; } }; \ No newline at end of file From 5c6a3320e9d9dd55258dc937364a46c47470bce4 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Wed, 4 Jan 2012 15:26:39 +0100 Subject: [PATCH 10/29] jquery validation plugin update --- Resources/public/js/jquery.plugin.js | 42 +++++++++++++++------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js index b6a52e8..4fcd640 100755 --- a/Resources/public/js/jquery.plugin.js +++ b/Resources/public/js/jquery.plugin.js @@ -9,27 +9,28 @@ var objectName = $(this).data('simplethings-validation-class'); - $(this).find('input').each(function() { - - var $this = $(this); - - var name = $this.attr('name'); - name = name.substr(name.indexOf("[") + 1, name.indexOf("]") - name.indexOf("[") - 1); - - $this.change(function() { + if(typeof options.constraints[objectName] != 'undefined') { + $(this).find('input').each(function() { - if(options.validator.isValid($this.val(), options.constraints[objectName][name])) { - options.onSuccess($this); - } else { - options.onError($this, options.validator.violations); - } + var $this = $(this); - }); - - }); - - - + var name = $this.attr('name'); + name = name.substr(name.indexOf("[") + 1, name.indexOf("]") - name.indexOf("[") - 1); + + $this.bind(options.event, function() { + + if(typeof options.constraints[objectName][name] != 'undefined') { + if(options.validator.isValid($this.val(), options.constraints[objectName][name])) { + options.onSuccess($this); + } else { + options.onError($this, options.validator.violations); + } + } + + }); + + }); + } }); }; @@ -38,7 +39,8 @@ validator: null, constraints: null, onSuccess: function(object) {}, - onError: function(object, violations) {} + onError: function(object, violations) {}, + event: 'blur' }; })(jQuery, jQuery); From dbceb7fc02718db8c5ffd2320dfd42f2f558ca55 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Thu, 5 Jan 2012 16:41:47 +0100 Subject: [PATCH 11/29] move generator logic in a service, add a command to create a javascript file with validation constraints --- ...GenerateJsValidationConstraintsCommand.php | 44 ++++++++++++ Service/JsValidationConstraintsGenerator.php | 70 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100755 Command/GenerateJsValidationConstraintsCommand.php create mode 100755 Service/JsValidationConstraintsGenerator.php diff --git a/Command/GenerateJsValidationConstraintsCommand.php b/Command/GenerateJsValidationConstraintsCommand.php new file mode 100755 index 0000000..20a6f08 --- /dev/null +++ b/Command/GenerateJsValidationConstraintsCommand.php @@ -0,0 +1,44 @@ + + */ +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') + ->addOption('name', null, InputOption::VALUE_OPTIONAL, 'The file name', 'javascript-validation-constraints.js') + ->addOption('variable', null, InputOption::VALUE_OPTIONAL, 'The javscript variable name', 'simpleThingsFormExtraValidationConstraints') + ; + } + + 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)); + } + + $generator = $this->getContainer()->get('simple_things_form_extra.js_validation_constraints_generator'); + $constraints = $generator->generate(); + + $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)); + } +} \ No newline at end of file diff --git a/Service/JsValidationConstraintsGenerator.php b/Service/JsValidationConstraintsGenerator.php new file mode 100755 index 0000000..eac1e8f --- /dev/null +++ b/Service/JsValidationConstraintsGenerator.php @@ -0,0 +1,70 @@ + + */ +class JsValidationConstraintsGenerator +{ + + protected $objects; + protected $validator; + + /** + * + * @param ValidatorInterface $validator + * @param array $objects + */ + public function __construct(ValidatorInterface $validator, array $objects = array()) + { + $this->validator = $validator; + $this->objects = $objects; + } + + /** + * + * @return string + */ + public function generate() + { + + $metadataFactory = $this->validator->getMetadataFactory(); + $data = array(); + + foreach ($this->objects as $object) { + $data[$object] = array(); + + $metadata = $metadataFactory->getClassMetadata($object); + $properties = $metadata->getConstrainedProperties(); + + foreach ($properties as $property) { + $data[$object][$property] = array(); + $constraintsList = $metadata->getMemberMetadatas($property); + foreach ($constraintsList as $constraints) { + foreach ($constraints->constraints as $constraint) { + $data[$object][$property][$this->getConstraintName($constraint)] = $constraint; + } + } + } + } + + return json_encode($data); + } + + /** + * + * @param Constraint $constraint + * @return string + */ + protected function getConstraintName(Constraint $constraint) + { + $class = get_class($constraint); + $parts = explode('\\', $class); + return lcfirst(array_pop($parts)); + } + +} From 1420a9d8d7b5e1a0c3d56bbd99c99ba49a95028c Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Thu, 5 Jan 2012 16:51:15 +0100 Subject: [PATCH 12/29] Added missing update to the previous commit --- Extension/ValidationExtension.php | 57 +++++-------------------------- Resources/config/form_extra.xml | 7 +++- 2 files changed, 14 insertions(+), 50 deletions(-) diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php index 0b26db2..ba20036 100755 --- a/Extension/ValidationExtension.php +++ b/Extension/ValidationExtension.php @@ -2,8 +2,8 @@ namespace SimpleThings\FormExtraBundle\Extension; -use Symfony\Component\Validator\ValidatorInterface; -use Symfony\Component\Validator\Constraint; +use SimpleThings\FormExtraBundle\Service\JsValidationConstraintsGenerator; + /** * @@ -14,25 +14,18 @@ class ValidationExtension extends \Twig_Extension /** * - * @var ValidatorInterface + * @var JsValidationConstraintsGenerator */ - private $validator; - - /** - * - * @var array - */ - private $objects; - + private $generator; + /** * * @param ValidatorInterface $validator * @param array $objects */ - public function __construct(ValidatorInterface $validator, array $objects) + public function __construct(JsValidationConstraintsGenerator $generator) { - $this->validator = $validator; - $this->objects = $objects; + $this->generator = $generator; } /** @@ -53,41 +46,7 @@ public function getFunctions() */ public function getValidationConstraints() { - - $metadataFactory = $this->validator->getMetadataFactory(); - $data = array(); - - foreach ($this->objects as $object) { - $data[$object] = array(); - - $metadata = $metadataFactory->getClassMetadata($object); - $properties = $metadata->getConstrainedProperties(); - - foreach ($properties as $property) { - $data[$object][$property] = array(); - $constraintsList = $metadata->getMemberMetadatas($property); - foreach ($constraintsList as $constraints) { - foreach ($constraints->constraints as $constraint) { - $data[$object][$property][$this->getConstraintName($constraint)] = $constraint; - } - } - } - } - - return json_encode($data); - - } - - /** - * - * @param Constraint $constraint - * @return string - */ - protected function getConstraintName(Constraint $constraint) - { - $class = get_class($constraint); - $parts = explode('\\', $class); - return lcfirst(array_pop($parts)); + return $this->generator->generate(); } /** diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml index 0df82d7..27b6a15 100755 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -9,6 +9,7 @@ SimpleThings\FormExtraBundle\Form\Type\PlainType SimpleThings\FormExtraBundle\Form\Type\FileSetType SimpleThings\FormExtraBundle\Form\Extension\FieldTypeExtension + SimpleThings\FormExtraBundle\Service\JsValidationConstraintsGenerator SimpleThings\FormExtraBundle\Form\Extension\ValidationTypeExtension SimpleThings\FormExtraBundle\Extension\ValidationExtension @@ -38,9 +39,13 @@ + + + + %simple_things_form_extra.client_validation.objects% - + From 6a8642b427a5fc015e7887ded6f37a6b99ee0f80 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Thu, 5 Jan 2012 17:25:42 +0100 Subject: [PATCH 13/29] refactor js generator service --- .../GenerateJsValidationConstraintsCommand.php | 3 ++- Extension/ValidationExtension.php | 11 +++++++++-- Resources/config/form_extra.xml | 2 +- Service/JsValidationConstraintsGenerator.php | 16 +++++++++------- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Command/GenerateJsValidationConstraintsCommand.php b/Command/GenerateJsValidationConstraintsCommand.php index 20a6f08..159d11e 100755 --- a/Command/GenerateJsValidationConstraintsCommand.php +++ b/Command/GenerateJsValidationConstraintsCommand.php @@ -31,8 +31,9 @@ protected function execute(InputInterface $input, OutputInterface $output) 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(); + $constraints = $generator->generate($objects); $file = $target . '/' . $input->getOption('name'); $variable = $input->getOption('variable'); diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php index ba20036..107dcb6 100755 --- a/Extension/ValidationExtension.php +++ b/Extension/ValidationExtension.php @@ -18,14 +18,21 @@ class ValidationExtension extends \Twig_Extension */ private $generator; + /** + * + * @var array + */ + private $objects; + /** * * @param ValidatorInterface $validator * @param array $objects */ - public function __construct(JsValidationConstraintsGenerator $generator) + public function __construct(JsValidationConstraintsGenerator $generator, array $objects) { $this->generator = $generator; + $this->objects = $objects; } /** @@ -46,7 +53,7 @@ public function getFunctions() */ public function getValidationConstraints() { - return $this->generator->generate(); + return $this->generator->generate($this->objects); } /** diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml index 27b6a15..50a0156 100755 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -40,11 +40,11 @@ + %simple_things_form_extra.client_validation.objects% - %simple_things_form_extra.client_validation.objects% diff --git a/Service/JsValidationConstraintsGenerator.php b/Service/JsValidationConstraintsGenerator.php index eac1e8f..5a1338f 100755 --- a/Service/JsValidationConstraintsGenerator.php +++ b/Service/JsValidationConstraintsGenerator.php @@ -11,31 +11,33 @@ class JsValidationConstraintsGenerator { - protected $objects; + /** + * + * @var ValidatorInterface + */ protected $validator; /** * * @param ValidatorInterface $validator - * @param array $objects */ - public function __construct(ValidatorInterface $validator, array $objects = array()) + public function __construct(ValidatorInterface $validator) { $this->validator = $validator; - $this->objects = $objects; } /** - * + * + * @param array $objects * @return string */ - public function generate() + public function generate(array $objects) { $metadataFactory = $this->validator->getMetadataFactory(); $data = array(); - foreach ($this->objects as $object) { + foreach ($objects as $object) { $data[$object] = array(); $metadata = $metadataFactory->getClassMetadata($object); From a483cad92c2d3f5f9577dc1d1a4dec2f1122488e Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Thu, 12 Jan 2012 16:24:48 +0100 Subject: [PATCH 14/29] form erros passed as attr --- Form/Extension/ErrorAttrTypeExtension.php | 41 ++++++++++++++++++++++ Form/Extension/ValidationTypeExtension.php | 10 +++--- Resources/config/form_extra.xml | 15 +++++--- Resources/public/js/jquery.plugin.js | 15 ++++---- 4 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 Form/Extension/ErrorAttrTypeExtension.php mode change 100755 => 100644 Form/Extension/ValidationTypeExtension.php mode change 100755 => 100644 Resources/config/form_extra.xml mode change 100755 => 100644 Resources/public/js/jquery.plugin.js diff --git a/Form/Extension/ErrorAttrTypeExtension.php b/Form/Extension/ErrorAttrTypeExtension.php new file mode 100644 index 0000000..3f17e61 --- /dev/null +++ b/Form/Extension/ErrorAttrTypeExtension.php @@ -0,0 +1,41 @@ + + */ +class ErrorAttrTypeExtension extends AbstractTypeExtension +{ + + /** + * @return string + */ + public function getExtendedType() + { + return 'field'; + } + + public function buildView(FormView $view, FormInterface $form) + { + $errors = array(); + $fieldErrors = $form->getErrors(); + foreach ($fieldErrors as $fieldError) { + $errors[] = $fieldError->getMessage(); + } + + if($errors) { + $attr = $view->get('attr'); + if(!isset($attr['data-error'])) { + $attr['data-error'] = implode('\r\n', $errors); + $view->set('attr', $attr); + } + } + } + +} \ No newline at end of file diff --git a/Form/Extension/ValidationTypeExtension.php b/Form/Extension/ValidationTypeExtension.php old mode 100755 new mode 100644 index ff546f5..120bdc1 --- a/Form/Extension/ValidationTypeExtension.php +++ b/Form/Extension/ValidationTypeExtension.php @@ -8,18 +8,18 @@ use Symfony\Component\Form\FormBuilder; /** - * + * * @author David Badura */ class ValidationTypeExtension extends AbstractTypeExtension { private $validatedObjects = array(); - + public function __construct($validatedObjects) { $this->validatedObjects = array_flip($validatedObjects); } - + /** * @return string */ @@ -27,7 +27,7 @@ public function getExtendedType() { return 'form'; } - + /** * @param FormBuilder $builder * @param array $options @@ -42,5 +42,5 @@ public function buildForm(FormBuilder $builder, array $options) } } } - + } \ No newline at end of file diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml old mode 100755 new mode 100644 index 50a0156..b720ee1 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -11,6 +11,7 @@ SimpleThings\FormExtraBundle\Form\Extension\FieldTypeExtension SimpleThings\FormExtraBundle\Service\JsValidationConstraintsGenerator SimpleThings\FormExtraBundle\Form\Extension\ValidationTypeExtension + SimpleThings\FormExtraBundle\Form\Extension\ErrorAttrTypeExtension SimpleThings\FormExtraBundle\Extension\ValidationExtension @@ -31,21 +32,25 @@ - + %simple_things_form_extra.client_validation.objects% + + + + %simple_things_form_extra.client_validation.objects% - - + + - - + + diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js old mode 100755 new mode 100644 index 4fcd640..e0200c1 --- a/Resources/public/js/jquery.plugin.js +++ b/Resources/public/js/jquery.plugin.js @@ -4,11 +4,11 @@ (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').each(function() { @@ -17,6 +17,8 @@ var name = $this.attr('name'); name = name.substr(name.indexOf("[") + 1, name.indexOf("]") - name.indexOf("[") - 1); + options.onCreate($this); + $this.bind(options.event, function() { if(typeof options.constraints[objectName][name] != 'undefined') { @@ -27,20 +29,21 @@ } } - }); + }); }); } - + }); }; - + jQuery.fn.simpleThingsFormExtraValidation.defaults = { validator: null, constraints: null, + onCreate: function(object) {}, onSuccess: function(object) {}, onError: function(object, violations) {}, event: 'blur' }; - + })(jQuery, jQuery); From e505c13d5c6c9bdc116bd9d44508872d6e3ff766 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Wed, 18 Jan 2012 14:03:21 +0100 Subject: [PATCH 15/29] update js validation constraints --- Resources/public/js/validation.js | 44 ++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) mode change 100755 => 100644 Resources/public/js/validation.js diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js old mode 100755 new mode 100644 index 4cc31b0..3a79c6e --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -2,10 +2,10 @@ * @author David Badura */ var simpleThingsFormExtraValidator = { - + violations: [], - - isValid: function(value, constraints) { + + isValid: function(value, constraints) { this.violations = []; for(var constraint in constraints){ if(typeof this.constraints[constraint] == 'function') { @@ -14,14 +14,14 @@ var simpleThingsFormExtraValidator = { 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; @@ -35,9 +35,9 @@ var simpleThingsFormExtraValidator = { } return message; } - + }, - + constraints: { max: function (value, constraint, validator) { if (isNaN(value)) { @@ -85,7 +85,7 @@ var simpleThingsFormExtraValidator = { return false; } - return true; + return true; }, email: function (value, constraint, validator) { var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; @@ -112,7 +112,7 @@ var simpleThingsFormExtraValidator = { maxLength: function (value, constraint, validator) { if (null == value || '' == value) { return true; - } + } if(value.length > parseFloat(constraint.limit)) { validator.addViolation(constraint.message, value, { @@ -126,7 +126,7 @@ var simpleThingsFormExtraValidator = { minLength: function (value, constraint, validator) { if (null == value || '' == value) { return true; - } + } if(value.length < parseFloat(constraint.limit)) { validator.addViolation(constraint.message, value, { @@ -143,7 +143,7 @@ var simpleThingsFormExtraValidator = { return false; } - return true; + return true; }, regex: function (value, constraint, validator) { if (null == value || '' == value) { @@ -158,7 +158,7 @@ var simpleThingsFormExtraValidator = { return false; } - return true; + return true; }, size: function (value, constraint, validator) { if (isNaN(value)) { @@ -166,7 +166,7 @@ var simpleThingsFormExtraValidator = { '{{ value }}': value }); return false; - } + } if( parseFloat(value) < parseFloat(constraint.min)) { validator.addViolation(constraint.minMessage, value, { @@ -228,7 +228,19 @@ var simpleThingsFormExtraValidator = { return true; }, type: function (value, constraint, validator) { + + if (null == value || '' == value) { + return true; + } + switch(constraint.type) { + case 'int': + case 'integer': + case 'digit': + if(isNaN(value) == false && parseInt(value) == value) { + return true; + } + break; case 'numeric': if(isNaN(value) == false) { return true; @@ -242,7 +254,7 @@ var simpleThingsFormExtraValidator = { default: return true; } - + validator.addViolation(constraint.message, value, { '{{ value }}': value, '{{ type }}': constraint.type @@ -256,7 +268,7 @@ var simpleThingsFormExtraValidator = { } return true; - }, + }, 'false': function (value, constraint, validator) { if (null == value) { return true; From 77ef8bdc7d7f3337d41651188c8e27c9795e20de Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Fri, 27 Jan 2012 13:16:56 +0100 Subject: [PATCH 16/29] change event callbacks to jquery events --- Resources/public/js/jquery.plugin.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js index e0200c1..8d001a3 100644 --- a/Resources/public/js/jquery.plugin.js +++ b/Resources/public/js/jquery.plugin.js @@ -23,9 +23,14 @@ if(typeof options.constraints[objectName][name] != 'undefined') { if(options.validator.isValid($this.val(), options.constraints[objectName][name])) { - options.onSuccess($this); + $this.trigger({ + type: 'validationSuccess' + }); } else { - options.onError($this, options.validator.violations); + $this.trigger({ + type: 'validationError', + violations: options.validator.violations + }); } } @@ -41,8 +46,6 @@ validator: null, constraints: null, onCreate: function(object) {}, - onSuccess: function(object) {}, - onError: function(object, violations) {}, event: 'blur' }; From 99d15da38a14f285445783da1d7527f44323149b Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Fri, 27 Jan 2012 16:32:35 +0100 Subject: [PATCH 17/29] add not null constraint --- Resources/public/js/validation.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index 3a79c6e..8773627 100644 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -145,6 +145,14 @@ var simpleThingsFormExtraValidator = { 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; @@ -228,7 +236,7 @@ var simpleThingsFormExtraValidator = { return true; }, type: function (value, constraint, validator) { - + if (null == value || '' == value) { return true; } From c185eaff3af1b14a7a739c754bd664accb07a5d5 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Fri, 27 Jan 2012 16:33:00 +0100 Subject: [PATCH 18/29] add select support --- Resources/public/js/jquery.plugin.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Resources/public/js/jquery.plugin.js b/Resources/public/js/jquery.plugin.js index 8d001a3..1d54cf3 100644 --- a/Resources/public/js/jquery.plugin.js +++ b/Resources/public/js/jquery.plugin.js @@ -10,7 +10,7 @@ var objectName = $(this).data('simplethings-validation-class'); if(typeof options.constraints[objectName] != 'undefined') { - $(this).find('input').each(function() { + $(this).find('input, select').each(function() { var $this = $(this); @@ -19,8 +19,7 @@ options.onCreate($this); - $this.bind(options.event, function() { - + $this.bind('change', function() { if(typeof options.constraints[objectName][name] != 'undefined') { if(options.validator.isValid($this.val(), options.constraints[objectName][name])) { $this.trigger({ @@ -33,7 +32,10 @@ }); } } + }); + $this.bind('blur', function() { + $(this).trigger('change'); }); }); @@ -45,8 +47,7 @@ jQuery.fn.simpleThingsFormExtraValidation.defaults = { validator: null, constraints: null, - onCreate: function(object) {}, - event: 'blur' + onCreate: function(object) {} }; -})(jQuery, jQuery); +})(jQuery, jQuery); \ No newline at end of file From 7573694fc6887ade50e873a28e080a974e25dc92 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Tue, 31 Jan 2012 15:13:08 +0100 Subject: [PATCH 19/29] fix breaks in ErrorAttrTypeExtension --- Form/Extension/ErrorAttrTypeExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Form/Extension/ErrorAttrTypeExtension.php b/Form/Extension/ErrorAttrTypeExtension.php index 3f17e61..9be27ba 100644 --- a/Form/Extension/ErrorAttrTypeExtension.php +++ b/Form/Extension/ErrorAttrTypeExtension.php @@ -32,7 +32,7 @@ public function buildView(FormView $view, FormInterface $form) if($errors) { $attr = $view->get('attr'); if(!isset($attr['data-error'])) { - $attr['data-error'] = implode('\r\n', $errors); + $attr['data-error'] = implode("
", $errors); $view->set('attr', $attr); } } From f55317f1e0e03f31ccfc9919423dab02c19d2950 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Tue, 31 Jan 2012 16:02:58 +0100 Subject: [PATCH 20/29] js validation make seconds optional by time constrain --- Resources/public/js/validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index 8773627..bf5a6d5 100644 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -214,7 +214,7 @@ var simpleThingsFormExtraValidator = { return true; }, time: function (value, constraint, validator) { - var reg = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/; + 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 From 7a029cf4a13b9268666ee95cd8333c5eecdb11c1 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Tue, 31 Jan 2012 17:03:10 +0100 Subject: [PATCH 21/29] fix validation constraints --- Resources/public/js/validation.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index bf5a6d5..c8294af 100644 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -88,6 +88,10 @@ var simpleThingsFormExtraValidator = { 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, { @@ -99,6 +103,10 @@ var simpleThingsFormExtraValidator = { 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, { @@ -214,6 +222,10 @@ var simpleThingsFormExtraValidator = { 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, { @@ -225,6 +237,10 @@ var simpleThingsFormExtraValidator = { 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, { @@ -236,7 +252,6 @@ var simpleThingsFormExtraValidator = { return true; }, type: function (value, constraint, validator) { - if (null == value || '' == value) { return true; } From 2aca8c270182cedd0e93aaa67aac92b07a0d0fd8 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Tue, 31 Jan 2012 17:59:12 +0100 Subject: [PATCH 22/29] add support of comma as decimal point --- Resources/public/js/validation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index c8294af..19ce95f 100644 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -40,7 +40,7 @@ var simpleThingsFormExtraValidator = { constraints: { max: function (value, constraint, validator) { - if (isNaN(value)) { + if (isNaN(value.replace(/,/, "."))) { validator.addViolation(constraint.invalidMessage, value, { '{{ limit }}': constraint.limit, '{{ value }}': value @@ -59,7 +59,7 @@ var simpleThingsFormExtraValidator = { return true; }, min: function (value, constraint, validator) { - if (isNaN(value)) { + if (isNaN(value.replace(/,/, "."))) { validator.addViolation(constraint.invalidMessage, value, { '{{ limit }}': constraint.limit, '{{ value }}': value @@ -177,7 +177,7 @@ var simpleThingsFormExtraValidator = { return true; }, size: function (value, constraint, validator) { - if (isNaN(value)) { + if (isNaN(value.replace(/,/, "."))) { validator.addViolation(constraint.invalidMessage, value, { '{{ value }}': value }); @@ -265,7 +265,7 @@ var simpleThingsFormExtraValidator = { } break; case 'numeric': - if(isNaN(value) == false) { + if(isNaN(value.replace(/,/, ".")) == false) { return true; } break; From fa0759be7f0061125e16d66715cf1868747b6613 Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Thu, 2 Feb 2012 12:05:04 +0100 Subject: [PATCH 23/29] translate validations --- Form/Extension/ErrorAttrTypeExtension.php | 10 ++++++- Resources/config/form_extra.xml | 3 ++ Service/JsValidationConstraintsGenerator.php | 29 ++++++++++++++++---- 3 files changed, 35 insertions(+), 7 deletions(-) mode change 100755 => 100644 Service/JsValidationConstraintsGenerator.php diff --git a/Form/Extension/ErrorAttrTypeExtension.php b/Form/Extension/ErrorAttrTypeExtension.php index 9be27ba..ce19cb1 100644 --- a/Form/Extension/ErrorAttrTypeExtension.php +++ b/Form/Extension/ErrorAttrTypeExtension.php @@ -5,6 +5,7 @@ use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; +use Symfony\Component\Translation\Translator; /** * @@ -13,6 +14,13 @@ class ErrorAttrTypeExtension extends AbstractTypeExtension { + protected $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + /** * @return string */ @@ -26,7 +34,7 @@ public function buildView(FormView $view, FormInterface $form) $errors = array(); $fieldErrors = $form->getErrors(); foreach ($fieldErrors as $fieldError) { - $errors[] = $fieldError->getMessage(); + $errors[] = $this->translator->trans($fieldError->getMessageTemplate(), $fieldError->getMessageParameters(), 'validators'); } if($errors) { diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml index b720ee1..037d703 100644 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -40,6 +40,7 @@ + @@ -50,6 +51,8 @@ + + %kernel.default_locale% diff --git a/Service/JsValidationConstraintsGenerator.php b/Service/JsValidationConstraintsGenerator.php old mode 100755 new mode 100644 index 5a1338f..8254541 --- a/Service/JsValidationConstraintsGenerator.php +++ b/Service/JsValidationConstraintsGenerator.php @@ -4,6 +4,7 @@ use Symfony\Component\Validator\ValidatorInterface; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Translation\Translator; /** * @author david badura @@ -13,22 +14,36 @@ class JsValidationConstraintsGenerator /** * - * @var ValidatorInterface + * @var ValidatorInterface */ protected $validator; + /** + * + * @var Translator + */ + protected $translator; + + /** + * + * @var string + */ + protected $defaultLocale; + /** * * @param ValidatorInterface $validator */ - public function __construct(ValidatorInterface $validator) + public function __construct(ValidatorInterface $validator, Translator $translator, $defaultLocale) { $this->validator = $validator; + $this->translator = $translator; + $this->defaultLocale = $defaultLocale; } /** - * - * @param array $objects + * + * @param array $objects * @return string */ public function generate(array $objects) @@ -48,7 +63,9 @@ public function generate(array $objects) $constraintsList = $metadata->getMemberMetadatas($property); foreach ($constraintsList as $constraints) { foreach ($constraints->constraints as $constraint) { - $data[$object][$property][$this->getConstraintName($constraint)] = $constraint; + $const = clone $constraint; + $const->message = $this->translator->trans($const->message, array(), 'validators', $this->defaultLocale); + $data[$object][$property][$this->getConstraintName($const)] = $const; } } } @@ -60,7 +77,7 @@ public function generate(array $objects) /** * * @param Constraint $constraint - * @return string + * @return string */ protected function getConstraintName(Constraint $constraint) { From 0d01a48459216456ea94e814948de932f448f197 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Feb 2012 21:08:48 +0100 Subject: [PATCH 24/29] Add test for ClientValidation Configuration --- .../SimpleThingsFormExtraExtensionTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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(); From ef4722d29eb207f1496ff5d688841d8fbf2d2cc5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Feb 2012 21:21:51 +0100 Subject: [PATCH 25/29] Cleanups and renamings --- ...GenerateJsValidationConstraintsCommand.php | 24 ++++++++++++----- Extension/ValidationExtension.php | 26 +++++++++++++------ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/Command/GenerateJsValidationConstraintsCommand.php b/Command/GenerateJsValidationConstraintsCommand.php index 159d11e..4ff7c7e 100755 --- a/Command/GenerateJsValidationConstraintsCommand.php +++ b/Command/GenerateJsValidationConstraintsCommand.php @@ -1,4 +1,15 @@ setName('formextra:jsvalidation:generate') ->setDescription('Generates javascript validation constraints') - ->addOption('target', null, InputOption::VALUE_OPTIONAL, 'The target directory', 'web') + ->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', 'simpleThingsFormExtraValidationConstraints') + ->addOption('variable', null, InputOption::VALUE_OPTIONAL, 'The javscript variable name', 'jsFormExtraValidationConstraints') ; } @@ -30,16 +41,17 @@ protected function execute(InputInterface $input, OutputInterface $output) 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)); } -} \ No newline at end of file +} + diff --git a/Extension/ValidationExtension.php b/Extension/ValidationExtension.php index 107dcb6..7aa2c11 100755 --- a/Extension/ValidationExtension.php +++ b/Extension/ValidationExtension.php @@ -1,20 +1,28 @@ */ class ValidationExtension extends \Twig_Extension { - /** * - * @var JsValidationConstraintsGenerator + * @var JsValidationConstraintsGenerator */ private $generator; @@ -23,11 +31,11 @@ class ValidationExtension extends \Twig_Extension * @var array */ private $objects; - + /** * * @param ValidatorInterface $validator - * @param array $objects + * @param array $objects */ public function __construct(JsValidationConstraintsGenerator $generator, array $objects) { @@ -48,12 +56,14 @@ public function getFunctions() } /** + * 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); + return $this->generator->generate($this->objects); } /** @@ -65,4 +75,4 @@ public function getName() { return 'simplethings_formextra_validation'; } -} \ No newline at end of file +} From 1ec95ad2c0a83a6370c1418f8549f6254454c3cd Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 8 Feb 2012 21:22:05 +0100 Subject: [PATCH 26/29] Add LICENSE --- LICENSE | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 LICENSE 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. + From a9e17cff8caafae883a64baa1a69e0d58236bbc7 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 Feb 2012 00:49:04 +0100 Subject: [PATCH 27/29] Add Test for Generator --- DependencyInjection/Configuration.php | 11 +++ Resources/config/client_validation.xml | 4 +- Service/JsValidationConstraintsGenerator.php | 81 ++++++++++++------- .../JsValidationConstraintsGeneratorTest.php | 68 ++++++++++++++++ 4 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 Tests/Services/JsValidationConstraintsGeneratorTest.php diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 01bd3c9..3a4b7cb 100755 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1,4 +1,15 @@ - - + + %kernel.default_locale% diff --git a/Service/JsValidationConstraintsGenerator.php b/Service/JsValidationConstraintsGenerator.php index 8254541..e565c4b 100644 --- a/Service/JsValidationConstraintsGenerator.php +++ b/Service/JsValidationConstraintsGenerator.php @@ -1,8 +1,19 @@ validator = $validator; + $this->metadataFactory = $metadataFactory; $this->translator = $translator; $this->defaultLocale = $defaultLocale; } /** - * - * @param array $objects + * @param array $classNames * @return string */ - public function generate(array $objects) + public function generate(array $classNames) { - - $metadataFactory = $this->validator->getMetadataFactory(); $data = array(); + foreach ($classNames as $className) { + $data[$className] = $this->generateClass($className); + } + + return json_encode($data); + } - foreach ($objects as $object) { - $data[$object] = array(); + /** + * 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 = $metadataFactory->getClassMetadata($object); - $properties = $metadata->getConstrainedProperties(); + $metadata = $this->metadataFactory->getClassMetadata($className); + $properties = $metadata->getConstrainedProperties(); - foreach ($properties as $property) { - $data[$object][$property] = array(); - $constraintsList = $metadata->getMemberMetadatas($property); - foreach ($constraintsList as $constraints) { - foreach ($constraints->constraints as $constraint) { - $const = clone $constraint; - $const->message = $this->translator->trans($const->message, array(), 'validators', $this->defaultLocale); - $data[$object][$property][$this->getConstraintName($const)] = $const; + 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 json_encode($data); + return $data; } /** - * + * @todo Only shorten symfony ones and underscore/camlize others? * @param Constraint $constraint * @return string */ @@ -85,5 +104,5 @@ protected function getConstraintName(Constraint $constraint) $parts = explode('\\', $class); return lcfirst(array_pop($parts)); } - } + 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; +} + From 3f9220caa571e0e77cb30ea9c5a78429101a763d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 23 Feb 2012 13:22:40 +0100 Subject: [PATCH 28/29] Remove FieldTypeExtension reference in form_extra.xml --- Resources/config/form_extra.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Resources/config/form_extra.xml b/Resources/config/form_extra.xml index 8735c42..e1c52de 100644 --- a/Resources/config/form_extra.xml +++ b/Resources/config/form_extra.xml @@ -8,7 +8,6 @@ SimpleThings\FormExtraBundle\Form\Type\ImageType SimpleThings\FormExtraBundle\Form\Type\PlainType SimpleThings\FormExtraBundle\Form\Type\FileSetType - SimpleThings\FormExtraBundle\Form\Extension\FieldTypeExtension @@ -23,9 +22,5 @@ - - - - From d8ac294734e26d3a99772288cbaac84ae636b34a Mon Sep 17 00:00:00 2001 From: DavidBadura Date: Tue, 6 Mar 2012 12:00:01 +0100 Subject: [PATCH 29/29] fix js validation constraint (type integer) --- Resources/public/js/validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/public/js/validation.js b/Resources/public/js/validation.js index 19ce95f..c12e383 100644 --- a/Resources/public/js/validation.js +++ b/Resources/public/js/validation.js @@ -260,7 +260,7 @@ var simpleThingsFormExtraValidator = { case 'int': case 'integer': case 'digit': - if(isNaN(value) == false && parseInt(value) == value) { + if(value.indexOf('.') == -1 && isNaN(value) == false && parseInt(value) == value) { return true; } break;