Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
language: php

php: [5.3,5.4,5.5,5.6]
php: [5.5,5.6,7.0]

env:
- SF_VERSION='>=2.5.5,<2.6.0'
- SF_VERSION='~2.6.0'
- SF_VERSION='~2.7.0'
- SF_VERSION='~3.0.0'

before_script:
- export WEB_FIXTURES_HOST=http://localhost/index.php
Expand All @@ -30,6 +28,7 @@ before_script:
- sudo apt-get update > /dev/null
- sudo apt-get install -y --force-yes apache2 libapache2-mod-fastcgi > /dev/null
# enable php-fpm
- if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf; fi
- sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf
- sudo a2enmod rewrite actions fastcgi alias
- echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
Expand Down
52 changes: 40 additions & 12 deletions Factory/JsFormValidatorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
use Fp\JsFormValidatorBundle\Form\Constraint\UniqueEntity;
use Fp\JsFormValidatorBundle\Model\JsConfig;
use Fp\JsFormValidatorBundle\Model\JsFormElement;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\GetterMetadata;
use Symfony\Component\Validator\Mapping\PropertyMetadata;
use Symfony\Component\Validator\ValidatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;

/**
* This factory uses to parse a form to a tree of JsFormElement's
Expand Down Expand Up @@ -91,7 +93,7 @@ public function __construct(
*/
protected function getMetadataFor($className)
{
return $this->validator->getMetadataFactory()->getMetadataFor($className);
return $this->validator->getMetadataFor($className);
}

/**
Expand Down Expand Up @@ -242,13 +244,15 @@ public function createJsModel(Form $form)
$model = new JsFormElement;
$model->id = $this->getElementId($form);
$model->name = $form->getName();
$model->type = $conf->getType()->getInnerType()->getName();
$model->type = get_class($conf->getType()->getInnerType());
$model->invalidMessage = $this->translateMessage(
$conf->getOption('invalid_message'),
$conf->getOption('invalid_message_parameters')
);
$model->transformers = $this->parseTransformers($form->getConfig()->getViewTransformers());
$model->cascade = $conf->getOption('cascade_validation');
$model->transformers = $this->normalizeViewTransformers(
$form,
$this->parseTransformers($conf->getViewTransformers())
);
$model->bubbling = $conf->getOption('error_bubbling');
$model->data = $this->getValidationData($form);
$model->children = $this->processChildren($form);
Expand Down Expand Up @@ -315,8 +319,8 @@ protected function getValidationData(Form $form)
$parent = $form->getParent();
if ($parent && null !== $parent->getConfig()->getDataClass()) {
$classMetadata = $metadata = $this->getMetadataFor($parent->getConfig()->getDataClass());
if ($classMetadata->hasMemberMetadatas($form->getName())) {
$metadata = $classMetadata->getMemberMetadatas($form->getName());
if ($classMetadata->hasPropertyMetadata($form->getName())) {
$metadata = $classMetadata->getPropertyMetadata($form->getName());
/** @var PropertyMetadata $item */
foreach ($metadata as $item) {
$this->composeValidationData(
Expand Down Expand Up @@ -444,8 +448,33 @@ protected function getValidationGroups(Form $form)
*/
protected function isProcessableElement($element)
{
return ($element instanceof Form)
&& ('hidden' !== $element->getConfig()->getType()->getName());
return ($element instanceof Form) && (!is_a($element->getConfig()->getType(), HiddenType::class, true));
}

/**
* Gets view transformers from the given form.
* Merges in an extra Choice(s)ToBooleanArrayTransformer transformer in case of expanded choice.
*
* @param FormInterface $form
* @param array $viewTransformers
*
* @return array
*/
protected function normalizeViewTransformers(FormInterface $form, array $viewTransformers)
{
$config = $form->getConfig();

// Choice(s)ToBooleanArrayTransformer was deprecated in SF2.7 in favor of CheckboxListMapper and RadioListMapper
if ($config->getType()->getInnerType() instanceof ChoiceType && $config->getOption('expanded')) {
$namespace = 'Symfony\Component\Form\Extension\Core\DataTransformer\\';
$transformer = $config->getOption('multiple')
? array('name' => $namespace . 'ChoicesToBooleanArrayTransformer')
: array('name' => $namespace . 'ChoiceToBooleanArrayTransformer');
$transformer['choiceList'] = array_values($config->getOption('choices'));
array_unshift($viewTransformers, $transformer);
}

return $viewTransformers;
}

/**
Expand All @@ -471,7 +500,6 @@ protected function parseTransformers(array $transformers)

$result[] = $item;
}

return $result;
}

Expand All @@ -495,7 +523,7 @@ protected function getTransformerParam(DataTransformerInterface $transformer, $p
} elseif (is_scalar($value) || is_array($value)) {
$result = $value;
} elseif ($value instanceof ChoiceListInterface) {
$result = $value->getChoices();
$result = array_values($value->getChoices());
}

return $result;
Expand Down
5 changes: 3 additions & 2 deletions Form/Extension/FormExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Fp\JsFormValidatorBundle\Factory\JsFormValidatorFactory;
use Fp\JsFormValidatorBundle\Form\Subscriber\SubscriberToQueue;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

Expand Down Expand Up @@ -37,7 +38,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
}

/**
* @param OptionsResolverInterface $resolver
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
Expand All @@ -51,6 +52,6 @@ public function configureOptions(OptionsResolver $resolver)
*/
public function getExtendedType()
{
return 'form';
return FormType::class;
}
}
5 changes: 0 additions & 5 deletions Model/JsFormElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ class JsFormElement extends JsModelAbstract
*/
public $invalidMessage;

/**
* @var bool
*/
public $cascade = false;

/**
* @var bool
*/
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
[![Build Status](https://travis-ci.org/formapro/JsFormValidatorBundle.svg?branch=master)](https://travis-ci.org/formapro/JsFormValidatorBundle)
[![Total Downloads](https://poser.pugx.org/fp/jsformvalidator-bundle/downloads.png)](https://packagist.org/packages/fp/jsformvalidator-bundle)

This module enables validation of the Symfony 2.5.5+ forms on the JavaScript side.
This module enables validation of the 3.0+ forms on the JavaScript side.
It converts form type constraints into JavaScript validation rules.

If you have Symfony 2.5.4* or less* - you need to use [Version 1.2.*](https://github.com/formapro/JsFormValidatorBundle/tree/1.2)

* More details here: [Symfony2](https://github.com/symfony/symfony/commit/97243bcd024bbfa458d66a4263a50ee7f16bbe74), [PR #83](https://github.com/formapro/JsFormValidatorBundle/pull/83)

If you have Symfony 2.8.* or 2.7.* - you need to use [Version 1.3.*](https://github.com/formapro/JsFormValidatorBundle/tree/1.3)
If you have Symfony 2.6.* or less - you need to use [Version 1.2.*](https://github.com/formapro/JsFormValidatorBundle/tree/1.2)

## 1 Installation<a name="p_1"></a>

Expand Down
2 changes: 1 addition & 1 deletion Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<service id="fp_js_form_validator.extension" class="%fp_js_form_validator.extension.class%">
<argument type="service" id="fp_js_form_validator.factory" />
<tag name="form.type_extension" alias="form" />
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
</service>

<service id="fp_js_form_validator.factory" class="%fp_js_form_validator.factory.class%">
Expand Down
45 changes: 30 additions & 15 deletions Resources/public/js/FpJsFormValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ function FpJsFormElement() {
this.name = '';
this.type = '';
this.invalidMessage = '';
this.cascade = false;
this.bubbling = false;
this.disabled = false;
this.transformers = [];
Expand Down Expand Up @@ -46,6 +45,7 @@ function FpJsFormElement() {

this.validateRecursively = function () {
this.validate();

for (var childName in this.children) {
this.children[childName].validateRecursively();
}
Expand Down Expand Up @@ -470,17 +470,16 @@ var FpJsFormValidator = new function () {
this.validateElement = function (element) {
var errors = [];
var value = this.getElementValue(element);
for (var type in element.data) {

if (!this.checkParentCascadeOption(element) && 'entity' == type) {
for (var type in element.data) {
if ('entity' == type && element.parent && !this.shouldValidEmbedded(element)) {
continue;
}

if (element.parent && !this.checkParentCascadeOption(element.parent) && 'parent' == type) {
if ('parent' == type && element.parent && element.parent.parent && !this.shouldValidEmbedded(element.parent)) {
continue;
}


// Evaluate groups
var groupsValue = element.data[type]['groups'];
if (typeof groupsValue == "string") {
Expand All @@ -505,19 +504,32 @@ var FpJsFormValidator = new function () {
}
}
}

return errors;
};

this.checkParentCascadeOption = function (element) {
var result = true;
if (element.parent && !element.parent.cascade && 'collection' != element.parent.type) {
result = false;
} else if (element.parent) {
result = this.checkParentCascadeOption(element.parent);
this.shouldValidEmbedded = function (element) {
if (this.getElementValidConstraint(element)) {
return true;
} else if (
element.parent
&& 'Symfony\\Component\\Form\\Extension\\Core\\Type\\CollectionType' == element.parent.type
) {
var validConstraint = this.getElementValidConstraint(element);

return !validConstraint || validConstraint.traverse;
}

return result;
return false;
};

this.getElementValidConstraint = function (element) {
if (element.data && element.data.form) {
for (var i in element.data.form.constraints) {
if (element.data.form.constraints[i] instanceof SymfonyComponentValidatorConstraintsValid) {
return element.data.form.constraints[i];
}
}
}
};

/**
Expand Down Expand Up @@ -569,7 +581,7 @@ var FpJsFormValidator = new function () {

if (i && undefined === value) {
value = this.getMappedValue(element);
} else if ('collection' == element.type) {
} else if ('Symfony\\Component\\Form\\Extension\\Core\\Type\\CollectionType' == element.type) {
value = {};
for (var childName in element.children) {
value[childName] = this.getMappedValue(element.children[childName]);
Expand Down Expand Up @@ -609,7 +621,10 @@ var FpJsFormValidator = new function () {
}

var value;
if ('checkbox' == element.type || 'radio' == element.type) {
if (
'Symfony\\Component\\Form\\Extension\\Core\\Type\\CheckboxType' == element.type
|| 'Symfony\\Component\\Form\\Extension\\Core\\Type\\RadioType' == element.type
) {
value = element.domNode.checked;
} else if ('select' === element.domNode.tagName.toLowerCase()) {
value = [];
Expand Down
2 changes: 1 addition & 1 deletion Resources/public/js/constraints/Callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function SymfonyComponentValidatorConstraintsCallback () {
if (!this.callback) {
this.callback = [];
}
if (!this.methods) {
if (!this.methods.length) {
this.methods = [this.callback];
}

Expand Down
10 changes: 4 additions & 6 deletions Resources/public/js/constraints/Choice.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ function SymfonyComponentValidatorConstraintsChoice() {

if (this.multiple) {
if (invalidCnt) {
while (invalidCnt--) {
errors.push(this.multipleMessage.replace(
'{{ value }}',
FpJsBaseConstraint.formatValue(invalidList[invalidCnt])
));
}
errors.push(this.multipleMessage.replace(
'{{ value }}',
FpJsBaseConstraint.formatValue(invalidList[0])
));
}
if (!isNaN(this.min) && value.length < this.min) {
errors.push(this.minMessage);
Expand Down
13 changes: 1 addition & 12 deletions Resources/public/js/constraints/False.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,4 @@
* @constructor
* @author dev.ymalcev@gmail.com
*/
function SymfonyComponentValidatorConstraintsFalse() {
this.message = '';

this.validate = function (value) {
var errors = [];
if ('' !== value && false !== value) {
errors.push(this.message.replace('{{ value }}', FpJsBaseConstraint.formatValue(value)));
}

return errors;
}
}
var SymfonyComponentValidatorConstraintsFalse = SymfonyComponentValidatorConstraintsIsFalse;
18 changes: 18 additions & 0 deletions Resources/public/js/constraints/IsFalse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//noinspection JSUnusedGlobalSymbols
/**
* Checks if value is (bool) false
* @constructor
* @author dev.ymalcev@gmail.com
*/
function SymfonyComponentValidatorConstraintsIsFalse() {
this.message = '';

this.validate = function (value) {
var errors = [];
if ('' !== value && false !== value) {
errors.push(this.message.replace('{{ value }}', FpJsBaseConstraint.formatValue(value)));
}

return errors;
}
}
18 changes: 18 additions & 0 deletions Resources/public/js/constraints/IsNull.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//noinspection JSUnusedGlobalSymbols
/**
* Checks if value is null
* @constructor
* @author dev.ymalcev@gmail.com
*/
function SymfonyComponentValidatorConstraintsIsNull() {
this.message = '';

this.validate = function(value) {
var errors = [];
if (null !== value) {
errors.push(this.message.replace('{{ value }}', FpJsBaseConstraint.formatValue(value)));
}

return errors;
}
}
Loading