merged branch Herzult/feature/collection_size_validator (PR #4149)

Commits
-------

3a5e84f [Validator] Add CollectionSize constraint

Discussion
----------

[Validator] Add CollectionSize constraint

Bug fix: no
Feature addition: yes
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: -
Todo: -

I will also send a PR to the documentation as soon as this one is accepted.

---------------------------------------------------------------------------

by bschussek at 2012-04-29T08:24:28Z

-1

I dislike the rising amount of very specific constraints in the core. Can't we add this to Size?

---------------------------------------------------------------------------

by vicb at 2012-04-29T09:01:39Z

@bschussek #3918 implements what you propose but then the messages are not valid any more:

```php
<?php
    public $minMessage = 'This value should be {{ limit }} or more';
    public $maxMessage = 'This value should be {{ limit }} or less';
    public $invalidMessage = 'This value should be a valid number';
```

I can imagine 2 solutions:

- adding some more message,
- rename the `Size` constraint to `Range` and create a new `Size` constraint for arrays / countables.

What do you think ?

---------------------------------------------------------------------------

by bschussek at 2012-04-29T09:27:53Z

I'd prefer the second solution and merge `Size` with `SizeLength` as well.

---------------------------------------------------------------------------

by vicb at 2012-04-29T09:34:50Z

@bschussek It would make sense. @makasim @Herzult any one of you would like to contribute this (i.e. rename the current Size to Range and create a new Size supporting arrays / countables / strings) ?

---------------------------------------------------------------------------

by Herzult at 2012-04-29T14:31:12Z

Yep, I'm on it.

---------------------------------------------------------------------------

by stof at 2012-04-29T15:22:44Z

@Herzult could you take the other comment into account and merge SizeLength into you Size ?

---------------------------------------------------------------------------

by vicb at 2012-04-29T15:33:05Z

The guessers should also be modified (it might also affect the ODM which is in an other repo, if so it would be good to sync the changes).

---------------------------------------------------------------------------

by Herzult at 2012-04-29T16:38:19Z

@stof the problem merging SizeLength into Size is that they don't have the same required options & messages.

---------------------------------------------------------------------------

by Herzult at 2012-04-29T16:47:40Z

And what about renaming Range to Interval and SizeLength to IntervalLength?

---------------------------------------------------------------------------

by stof at 2012-04-29T16:54:38Z

Well, SizeLength is about matching the length of a string currently. Nothing related to intervals

---------------------------------------------------------------------------

by Herzult at 2012-04-29T17:29:40Z

Here are the current names:

 * **Size** for collection (countable) size
 * **Range** for numbers
 * **SizeLength** for strings

Merging **SizeLength** into **Size** is maybe not appropriate because collections and strings are different things. It'll be hard to find messages that fit both collections and strings. Maybe we had better to find a better name for both. What do you think?

About the ValidatorTypeGuesser, I'll update it as soon as we know ow to name the constraints.

---------------------------------------------------------------------------

by vicb at 2012-04-29T17:43:01Z

Size is a good name for both strings and "collections", could we have two sets of strings and select according to the type ?

---------------------------------------------------------------------------

by Herzult at 2012-04-29T22:39:55Z

I tried to merge them together, what do you think?

---------------------------------------------------------------------------

by vicb at 2012-04-30T06:52:37Z

I think your changes are great, may be @bschussek has more feedback. The ValidatorTypeGuesser and the translation are yet to be updated.

---------------------------------------------------------------------------

by hhamon at 2012-05-01T12:32:28Z

Am I missing something or `SizeLength` for strings is a duplicate for `MinLength` and `MaxLength` constraints?

---------------------------------------------------------------------------

by Herzult at 2012-05-02T13:29:36Z

Yep, that's true. But the only link between this PR and the SizeLength constraint is that I merged it to the one I introduced.

---------------------------------------------------------------------------

by Herzult at 2012-05-07T07:48:01Z

@bschussek what do you think?

---------------------------------------------------------------------------

by vicb at 2012-05-10T19:51:26Z

@Herzult this PR looks good to me, could you update the changelog and update guides, try to factorize the code and squash the commits ? Thanks.

---------------------------------------------------------------------------

by travisbot at 2012-05-11T15:42:35Z

This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1306112) (merged 8d8e6443 into 4ac3bddb).

---------------------------------------------------------------------------

by vicb at 2012-05-11T21:42:21Z

* could #4259 be helpful ?
* please squash the commits.
* please create a PR / issue on [symfony-docs](https://github.com/symfony/symfony-docs)

thanks for the updates.

---------------------------------------------------------------------------

by travisbot at 2012-05-13T18:38:18Z

This pull request [fails](http://travis-ci.org/symfony/symfony/builds/1321123) (merged eeda9044 into 4ac3bddb).

---------------------------------------------------------------------------

by travisbot at 2012-05-13T18:45:12Z

This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1321146) (merged 491ca19a into 8b54eb56).

---------------------------------------------------------------------------

by travisbot at 2012-05-14T11:29:39Z

This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1326110) (merged 44865024 into 8b54eb56).

---------------------------------------------------------------------------

by vicb at 2012-05-14T11:49:37Z

@Herzult what about plural translations ?

---------------------------------------------------------------------------

by travisbot at 2012-05-14T16:52:37Z

This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1328677) (merged 93480f95 into 46ffbd52).

---------------------------------------------------------------------------

by travisbot at 2012-05-14T17:03:13Z

This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1328705) (merged 326c3b81 into 46ffbd52).

---------------------------------------------------------------------------

by vicb at 2012-05-14T20:19:18Z

thanks for the updates, this PR looks fine to me. @bschussek ?

---------------------------------------------------------------------------

by vicb at 2012-05-16T06:45:51Z

@Herzult can you squash your commits ?

---------------------------------------------------------------------------

by travisbot at 2012-05-16T11:20:44Z

This pull request [passes](http://travis-ci.org/symfony/symfony/builds/1344811) (merged 3a5e84f4 into 58b6ef23).
This commit is contained in:
Fabien Potencier 2012-05-16 14:22:22 +02:00
commit 5314836d3c
12 changed files with 636 additions and 304 deletions

View File

@ -214,6 +214,18 @@
<source>A PHP extension caused the upload to fail.</source>
<target>A PHP extension caused the upload to fail.</target>
</trans-unit>
<trans-unit id="54">
<source>This collection should contain {{ limit }} elements or more.</source>
<target>This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.</target>
</trans-unit>
<trans-unit id="55">
<source>This collection should contain {{ limit }} elements or less.</source>
<target>This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.</target>
</trans-unit>
<trans-unit id="56">
<source>This collection should contain exactly {{ limit }} elements.</source>
<target>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -76,7 +76,7 @@
</trans-unit>
<trans-unit id="19">
<source>This value is too long. It should have {{ limit }} characters or less.</source>
<target>Cette chaine est trop longue. Elle doit avoir au maximum {{ limit }} caractères.</target>
<target>Cette chaine est trop longue. Elle doit avoir au maximum {{ limit }} caractère.|Cette chaine est trop longue. Elle doit avoir au maximum {{ limit }} caractères.</target>
</trans-unit>
<trans-unit id="20">
<source>This value should be {{ limit }} or more.</source>
@ -84,7 +84,7 @@
</trans-unit>
<trans-unit id="21">
<source>This value is too short. It should have {{ limit }} characters or more.</source>
<target>Cette chaine est trop courte. Elle doit avoir au minimum {{ limit }} caractères.</target>
<target>Cette chaine est trop courte. Elle doit avoir au minimum {{ limit }} caractère.|Cette chaine est trop courte. Elle doit avoir au minimum {{ limit }} caractères.</target>
</trans-unit>
<trans-unit id="22">
<source>This value should not be blank.</source>
@ -192,7 +192,7 @@
</trans-unit>
<trans-unit id="48">
<source>This value should have exactly {{ limit }} characters.</source>
<target>Cette chaine doit avoir exactement {{ limit }} caractères.</target>
<target>Cette chaine doit avoir exactement {{ limit }} caractère.|Cette chaine doit avoir exactement {{ limit }} caractères.</target>
</trans-unit>
<trans-unit id="49">
<source>The file was only partially uploaded.</source>
@ -214,6 +214,18 @@
<source>A PHP extension caused the upload to fail.</source>
<target>Une extension PHP a empêché le transfert du fichier.</target>
</trans-unit>
<trans-unit id="54">
<source>This collection should contain {{ limit }} elements or more.</source>
<target>Cette collection doit contenir {{ limit }} élément ou plus.|Cette collection doit contenir {{ limit }} éléments ou plus.</target>
</trans-unit>
<trans-unit id="55">
<source>This collection should contain {{ limit }} elements or less.</source>
<target>Cette collection doit contenir {{ limit }} élément ou moins.|Cette collection doit contenir {{ limit }} éléments ou moins.</target>
</trans-unit>
<trans-unit id="56">
<source>This collection should contain exactly {{ limit }} elements.</source>
<target>Cette collection doit contenir exactement {{ limit }} élément.|Cette collection doit contenir exactement {{ limit }} éléments.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -155,11 +155,19 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\MaxLength':
case 'Symfony\Component\Validator\Constraints\MinLength':
case 'Symfony\Component\Validator\Constraints\Regex':
case 'Symfony\Component\Validator\Constraints\SizeLength':
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Min':
case 'Symfony\Component\Validator\Constraints\Size':
switch ($constraint->type) {
case 'string':
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
case 'collection':
return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE);
}
break;
case 'Symfony\Component\Validator\Constraints\Min':
case 'Symfony\Component\Validator\Constraints\Range':
case 'Symfony\Component\Validator\Constraints\Max':
return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE);
}
@ -194,8 +202,11 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\MaxLength':
return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\SizeLength':
return new ValueGuess($constraint->max, Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Size':
if ('string' === $constraint->type && null !== $constraint->max) {
return new ValueGuess($constraint->max, Guess::HIGH_CONFIDENCE);
}
break;
case 'Symfony\Component\Validator\Constraints\Type':
if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) {
@ -206,7 +217,7 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\Max':
return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Size':
case 'Symfony\Component\Validator\Constraints\Range':
return new ValueGuess(strlen((string) $constraint->max), Guess::LOW_CONFIDENCE);
}
}
@ -224,7 +235,23 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\MinLength':
return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\SizeLength':
case 'Symfony\Component\Validator\Constraints\Size':
if ('string' !== $constraint->type) {
return;
}
if ($constraint->min === $constraint->max) {
return new ValueGuess(sprintf('.{%s}', (string) $constraint->min), Guess::LOW_CONFIDENCE);
}
if (null === $constraint->min) {
return new ValueGuess(sprintf('.{0,%s}', (string) $constraint->max), Guess::LOW_CONFIDENCE);
}
if (null === $constraint->max) {
return new ValueGuess(sprintf('.{%s,}', (string) $constraint->min), Guess::LOW_CONFIDENCE);
}
return new ValueGuess(sprintf('.{%s,%s}', (string) $constraint->min, (string) $constraint->max), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Regex':
@ -233,7 +260,7 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\Min':
return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Size':
case 'Symfony\Component\Validator\Constraints\Range':
return new ValueGuess(sprintf('.{%s,%s}', strlen((string) $constraint->min), strlen((string) $constraint->max)), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Type':

View File

@ -5,8 +5,8 @@ CHANGELOG
-----
* added support for `ctype_*` assertions in `TypeValidator`
* added a Size validator
* added a SizeLength validator
* added a Range validator for numeric values
* added a Size validator for string & collections
* improved the ImageValidator with min width, max width, min height, and max height constraints
* added support for MIME with wildcard in FileValidator
* changed Collection validator to add "missing" and "extra" errors to

View File

@ -18,14 +18,13 @@ use Symfony\Component\Validator\Constraint;
*
* @api
*/
class SizeLength extends Constraint
class Range extends Constraint
{
public $minMessage = 'This value is too short. It should have {{ limit }} characters or more.';
public $maxMessage = 'This value is too long. It should have {{ limit }} characters or less.';
public $exactMessage = 'This value should have exactly {{ limit }} characters.';
public $minMessage = 'This value should be {{ limit }} or more.';
public $maxMessage = 'This value should be {{ limit }} or less.';
public $invalidMessage = 'This value should be a valid number.';
public $min;
public $max;
public $charset = 'UTF-8';
/**
* {@inheritDoc}

View File

@ -13,12 +13,13 @@ namespace Symfony\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class SizeLengthValidator extends ConstraintValidator
class RangeValidator extends ConstraintValidator
{
/**
* Checks if the passed value is valid.
@ -26,51 +27,38 @@ class SizeLengthValidator extends ConstraintValidator
* @param mixed $value The value that should be validated
* @param Constraint $constraint The constraint for the validation
*
* @return Boolean Whether or not the value is valid
*
* @api
*/
public function validate($value, Constraint $constraint)
{
if (null === $value || '' === $value) {
if (null === $value) {
return;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string) $value;
if (function_exists('grapheme_strlen') && 'UTF-8' === $constraint->charset) {
$length = grapheme_strlen($value);
} elseif (function_exists('mb_strlen')) {
$length = mb_strlen($value, $constraint->charset);
} else {
$length = strlen($value);
}
if ($constraint->min == $constraint->max && $length != $constraint->max) {
$this->context->addViolation($constraint->exactMessage, array(
if (!is_numeric($value)) {
$this->context->addViolation($constraint->invalidMessage, array(
'{{ value }}' => $value,
'{{ limit }}' => $constraint->max,
), null, (int) $constraint->max);
));
return;
}
if ($length > $constraint->max) {
if ($value > $constraint->max) {
$this->context->addViolation($constraint->maxMessage, array(
'{{ value }}' => $value,
'{{ limit }}' => $constraint->max,
), null, (int) $constraint->max);
));
return;
}
if ($length < $constraint->min) {
if ($value < $constraint->min) {
$this->context->addViolation($constraint->minMessage, array(
'{{ value }}' => $value,
'{{ limit }}' => $constraint->min,
), null, (int) $constraint->min);
));
}
}
}

View File

@ -20,17 +20,70 @@ use Symfony\Component\Validator\Constraint;
*/
class Size extends Constraint
{
public $minMessage = 'This value should be {{ limit }} or more.';
public $maxMessage = 'This value should be {{ limit }} or less.';
public $invalidMessage = 'This value should be a valid number.';
const TYPE_STRING = 'string';
const TYPE_COLLECTION = 'collection';
public $minMessage;
public $maxMessage;
public $exactMessage;
public $type;
public $min;
public $max;
public $charset = 'UTF-8';
/**
* {@inheritDoc}
*/
public function getRequiredOptions()
private $stringMinMessage = 'This value is too short. It should have {{ limit }} characters or more.';
private $stringMaxMessage = 'This value is too long. It should have {{ limit }} characters or less.';
private $stringExactMessage = 'This value should have exactly {{ limit }} characters.';
private $collectionMinMessage = 'This collection should contain {{ limit }} elements or more.';
private $collectionMaxMessage = 'This collection should contain {{ limit }} elements or less.';
private $collectionExactMessage = 'This collection should contain exactly {{ limit }} elements.';
public function getMinMessage($type)
{
return array('min', 'max');
if (null !== $this->minMessage) {
return $this->minMessage;
}
switch ($type) {
case static::TYPE_STRING:
return $this->stringMinMessage;
case static::TYPE_COLLECTION:
return $this->collectionMinMessage;
default:
throw new \InvalidArgumentException('Invalid type specified.');
}
}
public function getMaxMessage($type)
{
if (null !== $this->maxMessage) {
return $this->maxMessage;
}
switch ($type) {
case static::TYPE_STRING:
return $this->stringMaxMessage;
case static::TYPE_COLLECTION:
return $this->collectionMaxMessage;
default:
throw new \InvalidArgumentException('Invalid type specified.');
}
}
public function getExactMessage($type)
{
if (null !== $this->exactMessage) {
return $this->exactMessage;
}
switch ($type) {
case static::TYPE_STRING:
return $this->stringExactMessage;
case static::TYPE_COLLECTION:
return $this->collectionExactMessage;
default:
throw new \InvalidArgumentException('Invalid type specified.');
}
}
}

View File

@ -13,52 +13,151 @@ namespace Symfony\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class SizeValidator extends ConstraintValidator
{
/**
* Checks if the passed value is valid.
*
* @param mixed $value The value that should be validated
* @param Constraint $constraint The constraint for the validation
*
* @return Boolean Whether or not the value is valid
*
* @api
* {@inheritDoc}
*/
public function validate($value, Constraint $constraint)
{
if (null === $constraint->min && null === $constraint->max) {
throw new ConstraintDefinitionException(
'Either "min" or "max" must be specified on constraint Size'
);
}
if (null === $constraint->type) {
$type = $this->guessType($value);
} else {
$type = $constraint->type;
}
switch ($type) {
case Size::TYPE_STRING:
return $this->validateString($value, $constraint);
case Size::TYPE_COLLECTION:
return $this->validateCollection($value, $constraint);
default:
throw new ConstraintDefinitionException(sprintf(
'The "type" on constraint Size must be either "%s" or "%s", "%s" given.',
Size::TYPE_STRING,
Size::TYPE_COLLECTION,
$type
));
}
}
private function validateString($value, Constraint $constraint)
{
if (null === $value || '' === $value) {
return;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
throw new UnexpectedTypeException($value, 'string, scalar or object with __toString()');
}
$value = (string) $value;
if (function_exists('grapheme_strlen') && 'UTF-8' === $constraint->charset) {
$length = grapheme_strlen($value);
} elseif (function_exists('mb_strlen')) {
$length = mb_strlen($value, $constraint->charset);
} else {
$length = strlen($value);
}
$this->validateSize(
$constraint,
$length,
Size::TYPE_STRING,
array('{{ value }}' => $value)
);
}
private function validateCollection($value, Constraint $constraint)
{
if (null === $value) {
return;
}
if (!is_numeric($value)) {
$this->context->addViolation($constraint->invalidMessage, array(
'{{ value }}' => $value,
));
if (!is_array($value) && !$value instanceof \Countable) {
throw new UnexpectedTypeException($value, 'array or Countable');
}
$count = count($value);
$this->validateSize(
$constraint,
$count,
Size::TYPE_COLLECTION,
array('{{ count }}' => $count)
);
}
private function validateSize(Constraint $constraint, $size, $type, array $parameters)
{
if ($constraint->min == $constraint->max && $size != $constraint->max) {
$this->context->addViolation(
$constraint->getExactMessage($type),
array_merge(array('{{ limit }}' => $constraint->max), $parameters),
null,
(int) $constraint->max
);
return;
}
if ($value > $constraint->max) {
$this->context->addViolation($constraint->maxMessage, array(
'{{ value }}' => $value,
'{{ limit }}' => $constraint->max,
));
if (null !== $constraint->max && $size > $constraint->max) {
$this->context->addViolation(
$constraint->getMaxMessage($type),
array_merge(array('{{ limit }}' => $constraint->max), $parameters),
null,
(int) $constraint->max
);
return;
}
if ($value < $constraint->min) {
$this->context->addViolation($constraint->minMessage, array(
'{{ value }}' => $value,
'{{ limit }}' => $constraint->min,
));
if (null !== $constraint->min && $size < $constraint->min) {
$this->context->addViolation(
$constraint->getMinMessage($type),
array_merge(array('{{ limit }}' => $constraint->min), $parameters),
null,
(int) $constraint->min
);
}
}
private function guessType($value)
{
if (null === $value || is_scalar($value)) {
return Size::TYPE_STRING;
}
if (is_object($value) && method_exists($value, '__toString')) {
if ($value instanceof \Countable) {
throw new \RuntimeException(
'The "type" must be specified on constraint Size because the '.
'validator is not able to guess it since the value is an object '.
'implementing both the __toString() method and the Countable '.
'interface.'
);
}
return Size::TYPE_STRING;
}
if (is_array($value) || $value instanceof \Countable) {
return Size::TYPE_COLLECTION;
}
throw new UnexpectedTypeException(
$value, 'scalar, string, array, Countable or object with __toString()'
);
}
}

View File

@ -0,0 +1,121 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Validator\Tests\Constraints;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Constraints\RangeValidator;
class RangeValidatorTest extends \PHPUnit_Framework_TestCase
{
protected $context;
protected $validator;
protected function setUp()
{
$this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false);
$this->validator = new RangeValidator();
$this->validator->initialize($this->context);
}
public function testNullIsValid()
{
$this->context->expects($this->never())
->method('addViolation');
$this->validator->validate(null, new Range(array('min' => 10, 'max' => 20)));
}
/**
* @dataProvider getValidValues
*/
public function testValidValues($value)
{
$this->context->expects($this->never())
->method('addViolation');
$constraint = new Range(array('min' => 10, 'max' => 20));
$this->validator->validate($value, $constraint);
}
public function getValidValues()
{
return array(
array(10.00001),
array(19.99999),
array('10.00001'),
array('19.99999'),
array(10),
array(20),
array(10.0),
array(20.0),
);
}
/**
* @dataProvider getInvalidValues
*/
public function testInvalidValues($value)
{
$this->context->expects($this->once())
->method('addViolation');
$constraint = new Range(array('min' => 10, 'max' => 20));
$this->validator->validate($value, $constraint);
}
public function getInvalidValues()
{
return array(
array(9.999999),
array(20.000001),
array('9.999999'),
array('20.000001'),
array(new \stdClass()),
);
}
public function testMinMessageIsSet()
{
$constraint = new Range(array(
'min' => 10,
'max' => 20,
'minMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => 9,
'{{ limit }}' => 10,
));
$this->validator->validate(9, $constraint);
}
public function testMaxMessageIsSet()
{
$constraint = new Range(array(
'min' => 10,
'max' => 20,
'maxMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => 21,
'{{ limit }}' => 20,
));
$this->validator->validate(21, $constraint);
}
}

View File

@ -1,172 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Validator\Tests\Constraints;
use Symfony\Component\Validator\Constraints\SizeLength;
use Symfony\Component\Validator\Constraints\SizeLengthValidator;
class SizeLengthValidatorTest extends \PHPUnit_Framework_TestCase
{
protected $context;
protected $validator;
protected function setUp()
{
$this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false);
$this->validator = new SizeLengthValidator();
$this->validator->initialize($this->context);
}
protected function tearDown()
{
$this->context = null;
$this->validator = null;
}
public function testNullIsValid()
{
$this->context->expects($this->never())
->method('addViolation');
$this->validator->validate(null, new SizeLength(array('min' => 6, 'max' => 10)));
}
public function testEmptyStringIsValid()
{
$this->context->expects($this->never())
->method('addViolation');
$this->validator->validate('', new SizeLength(array('min' => 6, 'max' => 10)));
}
/**
* @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException
*/
public function testExpectsStringCompatibleType()
{
$this->validator->validate(new \stdClass(), new SizeLength(array('min' => 6, 'max' => 10)));
}
/**
* @dataProvider getValidValues
*/
public function testValidValues($value, $mbOnly = false)
{
if ($mbOnly && !function_exists('mb_strlen')) {
return $this->markTestSkipped('mb_strlen does not exist');
}
$this->context->expects($this->never())
->method('addViolation');
$constraint = new SizeLength(array('min' => 6, 'max' => 10));
$this->validator->validate($value, $constraint);
}
public function getValidValues()
{
return array(
array(123456),
array(1234567890),
array('123456'),
array('1234567890'),
array('üüüüüü', true),
array('üüüüüüüüüü', true),
array('éééééé', true),
array('éééééééééé', true),
);
}
/**
* @dataProvider getInvalidValues
*/
public function testInvalidValues($value, $mbOnly = false)
{
if ($mbOnly && !function_exists('mb_strlen')) {
return $this->markTestSkipped('mb_strlen does not exist');
}
$this->context->expects($this->once())
->method('addViolation');
$constraint = new SizeLength(array('min' => 6, 'max' => 10));
$this->validator->validate($value, $constraint);
}
public function getInvalidValues()
{
return array(
array(12345),
array(12345678901),
array('12345'),
array('12345678901'),
array('üüüüü', true),
array('üüüüüüüüüüü', true),
array('ééééé', true),
array('ééééééééééé', true),
);
}
public function testMinMessageIsSet()
{
$constraint = new SizeLength(array(
'min' => 5,
'max' => 10,
'minMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => '1234',
'{{ limit }}' => 5,
), null, 5);
$this->validator->validate('1234', $constraint);
}
public function testMaxMessageIsSet()
{
$constraint = new SizeLength(array(
'min' => 5,
'max' => 10,
'maxMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => '12345678901',
'{{ limit }}' => 10,
), null, 10);
$this->validator->validate('12345678901', $constraint);
}
public function testExactMessageIsSet()
{
$constraint = new SizeLength(array(
'min' => 5,
'max' => 5,
'exactMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => '1234',
'{{ limit }}' => 5,
), null, 5);
$this->validator->validate('1234', $constraint);
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace Symfony\Component\Validator\Tests\Constraints;
use Symfony\Component\Validator\Constraints\Size;
class SizeTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getMinMessageData
*/
public function testGetMinMessage($options, $type, $expected)
{
$size = new Size($options);
$this->assertEquals($expected, $size->getMinMessage($type));
}
public function getMinMessageData()
{
$size = new Size();
return array(
array(array(), Size::TYPE_STRING, $this->readAttribute($size, 'stringMinMessage')),
array(array(), Size::TYPE_COLLECTION, $this->readAttribute($size, 'collectionMinMessage')),
array(array('minMessage' => 'Custom min message'), Size::TYPE_STRING, 'Custom min message'),
array(array('minMessage' => 'Custom min message'), Size::TYPE_COLLECTION, 'Custom min message'),
);
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetMinMessageWithInvalidType()
{
$size = new Size();
$size->getMinMessage('foo');
}
/**
* @dataProvider getMaxMessageData
*/
public function testGetMaxMessage($options, $type, $expected)
{
$size = new Size($options);
$this->assertEquals($expected, $size->getMaxMessage($type));
}
public function getMaxMessageData()
{
$size = new Size();
return array(
array(array(), Size::TYPE_STRING, $this->readAttribute($size, 'stringMaxMessage')),
array(array(), Size::TYPE_COLLECTION, $this->readAttribute($size, 'collectionMaxMessage')),
array(array('maxMessage' => 'Custom max message'), Size::TYPE_STRING, 'Custom max message'),
array(array('maxMessage' => 'Custom max message'), Size::TYPE_COLLECTION, 'Custom max message'),
);
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetMaxMessageWithInvalidType()
{
$size = new Size();
$size->getMaxMessage('foo');
}
/**
* @dataProvider getExactMessageData
*/
public function testGetExactMessage($options, $type, $expected)
{
$size = new Size($options);
$this->assertEquals($expected, $size->getExactMessage($type));
}
public function getExactMessageData()
{
$size = new Size();
return array(
array(array(), Size::TYPE_STRING, $this->readAttribute($size, 'stringExactMessage')),
array(array(), Size::TYPE_COLLECTION, $this->readAttribute($size, 'collectionExactMessage')),
array(array('exactMessage' => 'Custom exact message'), Size::TYPE_STRING, 'Custom exact message'),
array(array('exactMessage' => 'Custom exact message'), Size::TYPE_COLLECTION, 'Custom exact message'),
);
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetExactMessageWithInvalidType()
{
$size = new Size();
$size->getExactMessage('foo');
}
}

View File

@ -14,6 +14,19 @@ namespace Symfony\Component\Validator\Tests\Constraints;
use Symfony\Component\Validator\Constraints\Size;
use Symfony\Component\Validator\Constraints\SizeValidator;
class SizeAmbiguous implements \Countable
{
public function __toString()
{
return '';
}
public function count()
{
return 0;
}
}
class SizeValidatorTest extends \PHPUnit_Framework_TestCase
{
protected $context;
@ -28,94 +41,176 @@ class SizeValidatorTest extends \PHPUnit_Framework_TestCase
public function testNullIsValid()
{
$this->context->expects($this->never())
->method('addViolation');
$this->context->expects($this->never())->method('addViolation');
$this->validator->validate(null, new Size(array('min' => 10, 'max' => 20)));
}
/**
* @dataProvider getValidValues
*/
public function testValidValues($value)
public function testNullIsValidAsAString()
{
$this->context->expects($this->never())->method('addViolation');
$this->validator->validate(null, new Size(array('type' => 'string', 'min' => 10, 'max' => 20)));
}
public function testNullIsValidAsACollectionCollection()
{
$this->context->expects($this->never())->method('addViolation');
$this->validator->validate(null, new Size(array('type' => 'collection', 'min' => 10, 'max' => 20)));
}
public function testEmptyStringIsValid()
{
$this->context->expects($this->never())->method('addViolation');
$this->validator->validate('', new Size(array('min' => 10, 'max' => 20)));
}
public function testEmptyStringIsValidAsAString()
{
$this->context->expects($this->never())->method('addViolation');
$this->validator->validate('', new Size(array('type' => 'string', 'min' => 10, 'max' => 20)));
}
/**
* @dataProvider getValidStringValues
*/
public function testValidStringValues($value, $mbOnly = false)
{
if ($mbOnly && !function_exists('mb_strlen')) {
return $this->markTestSkipped('mb_strlen does not exist');
}
$this->context->expects($this->never())
->method('addViolation');
$constraint = new Size(array('min' => 10, 'max' => 20));
$constraint = new Size(array('min' => 6, 'max' => 10));
$this->validator->validate($value, $constraint);
}
public function getValidValues()
public function getValidStringValues()
{
return array(
array(10.00001),
array(19.99999),
array('10.00001'),
array('19.99999'),
array(10),
array(20),
array(10.0),
array(20.0),
array(123456),
array(1234567890),
array('123456'),
array('1234567890'),
array('üüüüüü', true),
array('üüüüüüüüüü', true),
array('éééééé', true),
array('éééééééééé', true),
);
}
/**
* @dataProvider getInvalidValues
* @dataProvider getInvalidStringValues
*/
public function testInvalidValues($value)
public function testInvalidStringValues($value, $mbOnly = false)
{
$this->context->expects($this->once())
->method('addViolation');
if ($mbOnly && !function_exists('mb_strlen')) {
return $this->markTestSkipped('mb_strlen does not exist');
}
$constraint = new Size(array('min' => 10, 'max' => 20));
$this->validator->validate($value, $constraint);
$this->context->expects($this->once())->method('addViolation');
$this->validator->validate($value, new Size(array('min' => 6, 'max' => 10)));
}
public function getInvalidValues()
public function getInvalidStringValues()
{
return array(
array(9.999999),
array(20.000001),
array('9.999999'),
array('20.000001'),
array(new \stdClass()),
array(12345),
array(12345678901),
array('12345'),
array('12345678901'),
array('üüüüü', true),
array('üüüüüüüüüüü', true),
array('ééééé', true),
array('ééééééééééé', true),
);
}
public function testMinMessageIsSet()
/**
* @dataProvider getValidCollectionValues
*/
public function testValidCollectionValue($value)
{
$constraint = new Size(array(
'min' => 10,
'max' => 20,
'minMessage' => 'myMessage',
));
$this->context->expects($this->never())->method('addViolation');
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => 9,
'{{ limit }}' => 10,
));
$this->validator->validate(9, $constraint);
$this->validator->validate($value, new Size(array('min' => 10, 'max' => 20)));
}
public function testMaxMessageIsSet()
public function getValidCollectionValues()
{
$constraint = new Size(array(
'min' => 10,
'max' => 20,
'maxMessage' => 'myMessage',
));
$countable = $this->getMock('Countable');
$countable->expects($this->any())->method('count')->will($this->returnValue(15));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ value }}' => 21,
'{{ limit }}' => 20,
));
return array(
array($countable),
array(range(1, 15)),
);
}
$this->validator->validate(21, $constraint);
/**
* @dataProvider getInvalidCollectionValues
*/
public function testInvalidCollectionValue($value)
{
$this->context->expects($this->once())->method('addViolation');
$this->validator->validate($value, new Size(array('min' => 10, 'max' => 20)));
}
public function getInvalidCollectionValues()
{
$tooSmallCountable = $this->getMock('Countable');
$tooSmallCountable->expects($this->any())->method('count')->will($this->returnValue(5));
$tooBigCountable = $this->getmock('countable');
$tooBigCountable->expects($this->any())->method('count')->will($this->returnValue(25));
return array(
array($tooSmallCountable),
array($tooBigCountable),
array(array()),
array(range(1, 5)),
array(range(1, 25)),
);
}
/**
* @expectedException RuntimeException
*/
public function throwsAnExceptionWhenOnAmbiguousValue()
{
$this->validator->validate(new SizeAmbiguous(), new Size(array('min' => 10, 'max' => 20)));
}
/**
* @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException
*/
public function testExpectsEitherStringOrCollectionCompatible()
{
$this->validator->validate(new \stdCLass(), new Size(array('min' => 10, 'max' => 20)));
}
/**
* @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException
*/
public function testExpectsStringCompatibleType()
{
$countable = $this->getMock('Countable');
$this->validator->validate($countable, new Size(array('type' => 'string', 'min' => 6, 'max' => 10)));
}
/**
* @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException
*/
public function testExpectsCollectionCompatibleType()
{
$this->validator->validate('string', new Size(array('type' => 'collection', 'min' => 6, 'max' => 10)));
}
}