diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php index 4a12b042dc..8deecea079 100755 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -170,6 +170,10 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface case 'Symfony\Component\Validator\Constraints\Max': return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE); + case 'Symfony\Component\Validator\Constraints\MinCount': + case 'Symfony\Component\Validator\Constraints\MaxCount': + return new TypeGuess('collection', array(), Guess::LOW_CONFIDENCE); + case 'Symfony\Component\Validator\Constraints\Time': return new TypeGuess('time', array('input'=>'string'), Guess::HIGH_CONFIDENCE); diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 8f3480ecb0..a444da485d 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -22,3 +22,4 @@ CHANGELOG * [BC BREAK] collections in fields annotated with `Valid` are not traversed recursively anymore by default. `Valid` contains a new property `deep` which enables the BC behavior. + * added MinCount and MaxCount constraint diff --git a/src/Symfony/Component/Validator/Constraints/MaxCount.php b/src/Symfony/Component/Validator/Constraints/MaxCount.php new file mode 100644 index 0000000000..c3286033a0 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/MaxCount.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * @Annotation + * + * @api + */ +class MaxCount extends Constraint +{ + public $message = 'This collection should contain {{ limit }} elements or less.'; + public $limit; + + /** + * {@inheritDoc} + */ + public function getDefaultOption() + { + return 'limit'; + } + + /** + * {@inheritDoc} + */ + public function getRequiredOptions() + { + return array('limit'); + } +} diff --git a/src/Symfony/Component/Validator/Constraints/MaxCountValidator.php b/src/Symfony/Component/Validator/Constraints/MaxCountValidator.php new file mode 100644 index 0000000000..50efbb7f47 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/MaxCountValidator.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Bernhard Schussek + */ +class MaxCountValidator 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 + * + * @api + */ + public function validate($value, Constraint $constraint) + { + if (null === $value) { + return; + } + + if (!is_array($value) && !$value instanceof \Countable) { + throw new UnexpectedTypeException($value, 'array or \Countable'); + } + + $count = count($value); + + if ($count > $constraint->limit) { + $this->context->addViolation($constraint->message, array( + '{{ count }}' => $count, + '{{ limit }}' => $constraint->limit, + ), $value, (int) $constraint->limit); + } + } +} diff --git a/src/Symfony/Component/Validator/Constraints/MinCount.php b/src/Symfony/Component/Validator/Constraints/MinCount.php new file mode 100644 index 0000000000..ebf1ff5d47 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/MinCount.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * @Annotation + * + * @api + */ +class MinCount extends Constraint +{ + public $message = 'This collection should contain {{ limit }} elements or more.'; + public $limit; + + /** + * {@inheritDoc} + */ + public function getDefaultOption() + { + return 'limit'; + } + + /** + * {@inheritDoc} + */ + public function getRequiredOptions() + { + return array('limit'); + } +} diff --git a/src/Symfony/Component/Validator/Constraints/MinCountValidator.php b/src/Symfony/Component/Validator/Constraints/MinCountValidator.php new file mode 100644 index 0000000000..1620caa4b3 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/MinCountValidator.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Bernhard Schussek + */ +class MinCountValidator 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 + * + * @throws UnexpectedTypeException If the given value is no array or \Countable. + */ + public function validate($value, Constraint $constraint) + { + if (null === $value) { + return; + } + + if (!is_array($value) && !$value instanceof \Countable) { + throw new UnexpectedTypeException($value, 'array or \Countable'); + } + + $count = count($value); + + if ($count < $constraint->limit) { + $this->context->addViolation($constraint->message, array( + '{{ count }}' => $count, + '{{ limit }}' => $constraint->limit, + ), $value, (int) $constraint->limit); + } + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorArrayTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorArrayTest.php new file mode 100644 index 0000000000..8453930ae7 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorArrayTest.php @@ -0,0 +1,23 @@ + + * + * 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; + +/** + * @author Bernhard Schussek + */ +class MaxCountValidatorArrayTest extends MaxCountValidatorTest +{ + protected function createCollection(array $content) + { + return $content; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorCountableTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorCountableTest.php new file mode 100644 index 0000000000..c4852e00d3 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorCountableTest.php @@ -0,0 +1,38 @@ + + * + * 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; + +class MaxCountValidatorCountableTest_Countable implements \Countable +{ + private $content; + + public function __construct(array $content) + { + $this->content = $content; + } + + public function count() + { + return count($this->content); + } +} + +/** + * @author Bernhard Schussek + */ +class MaxCountValidatorCountableTest extends MaxCountValidatorTest +{ + protected function createCollection(array $content) + { + return new MaxCountValidatorCountableTest_Countable($content); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorTest.php new file mode 100644 index 0000000000..f6ef305ffe --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/MaxCountValidatorTest.php @@ -0,0 +1,113 @@ + + * + * 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\MaxCount; +use Symfony\Component\Validator\Constraints\MaxCountValidator; + +/** + * @author Bernhard Schussek + */ +abstract class MaxCountValidatorTest 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 MaxCountValidator(); + $this->validator->initialize($this->context); + } + + protected function tearDown() + { + $this->context = null; + $this->validator = null; + } + + abstract protected function createCollection(array $content); + + public function testNullIsValid() + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate(null, new MaxCount(6)); + } + + /** + * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsCountableType() + { + $this->validator->validate(new \stdClass(), new MaxCount(5)); + } + + /** + * @dataProvider getValidValues + */ + public function testValidValues($value) + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $constraint = new MaxCount(3); + $this->validator->validate($value, $constraint); + } + + public function getValidValues() + { + return array( + array($this->createCollection(array(1))), + array($this->createCollection(array(1, 2))), + array($this->createCollection(array(1, 2, 3))), + array($this->createCollection(array('a' => 1, 'b' => 2, 'c' => 3))), + ); + } + + /** + * @dataProvider getInvalidValues + */ + public function testInvalidValues($value) + { + $constraint = new MaxCount(array( + 'limit' => 3, + 'message' => 'myMessage' + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', $this->identicalTo(array( + '{{ count }}' => count($value), + '{{ limit }}' => 3, + )), $value, 3); + + $this->validator->validate($value, $constraint); + } + + public function getInvalidValues() + { + return array( + array($this->createCollection(array(1, 2, 3, 4))), + array($this->createCollection(array(1, 2, 3, 4, 5))), + array($this->createCollection(array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4))), + ); + } + + public function testDefaultOption() + { + $constraint = new MaxCount(5); + + $this->assertEquals(5, $constraint->limit); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorArrayTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorArrayTest.php new file mode 100644 index 0000000000..96eb63a157 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorArrayTest.php @@ -0,0 +1,23 @@ + + * + * 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; + +/** + * @author Bernhard Schussek + */ +class MinCountValidatorArrayTest extends MinCountValidatorTest +{ + protected function createCollection(array $content) + { + return $content; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorCountableTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorCountableTest.php new file mode 100644 index 0000000000..48b175a65d --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorCountableTest.php @@ -0,0 +1,38 @@ + + * + * 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; + +class MinCountValidatorCountableTest_Countable implements \Countable +{ + private $content; + + public function __construct(array $content) + { + $this->content = $content; + } + + public function count() + { + return count($this->content); + } +} + +/** + * @author Bernhard Schussek + */ +class MinCountValidatorCountableTest extends MinCountValidatorTest +{ + protected function createCollection(array $content) + { + return new MinCountValidatorCountableTest_Countable($content); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorTest.php new file mode 100644 index 0000000000..ab0cf86eaa --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/MinCountValidatorTest.php @@ -0,0 +1,114 @@ + + * + * 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\MinCount; +use Symfony\Component\Validator\Constraints\MinCountValidator; + +/** + * @author Bernhard Schussek + */ +abstract class MinCountValidatorTest 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 MinCountValidator(); + $this->validator->initialize($this->context); + } + + protected function tearDown() + { + $this->context = null; + $this->validator = null; + } + + abstract protected function createCollection(array $content); + + public function testNullIsValid() + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate(null, new MinCount(6)); + } + + /** + * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsCountableType() + { + $this->validator->validate(new \stdClass(), new MinCount(5)); + } + + /** + * @dataProvider getValidValues + */ + public function testValidValues($value) + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $constraint = new MinCount(3); + $this->validator->validate($value, $constraint); + } + + public function getValidValues() + { + return array( + array($this->createCollection(array(1, 2, 3))), + array($this->createCollection(array(1, 2, 3, 4))), + array($this->createCollection(array(1, 2, 3, 4, 5))), + array($this->createCollection(array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4))), + ); + } + + /** + * @dataProvider getInvalidValues + */ + public function testInvalidValues($value) + { + $constraint = new MinCount(array( + 'limit' => 4, + 'message' => 'myMessage' + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', $this->identicalTo(array( + '{{ count }}' => count($value), + '{{ limit }}' => 4, + )), $value, 4); + + $this->validator->validate($value, $constraint); + } + + public function getInvalidValues() + { + return array( + array($this->createCollection(array(1))), + array($this->createCollection(array(1, 2))), + array($this->createCollection(array(1, 2, 3))), + array($this->createCollection(array('a' => 1, 'b' => 2, 'c' => 3))), + ); + } + + public function testDefaultOption() + { + $constraint = new MinCount(5); + + $this->assertEquals(5, $constraint->limit); + } +}