diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md index dfc370370648c..e9e7aa3aaea67 100644 --- a/CHANGELOG-2.1.md +++ b/CHANGELOG-2.1.md @@ -277,6 +277,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * [BC BREAK] renamed "field_*" theme blocks to "form_*" and "field_widget" to "input" * ValidatorTypeGuesser now guesses "collection" for array type constraint + * added method `guessPattern` to FormTypeGuesserInterface to guess which pattern to use in the HTML5 attribute "pattern" ### HttpFoundation diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index e03a8e2128799..5267bca5112ce 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -115,7 +115,7 @@ public function guessMaxLength($class, $property) /** * {@inheritDoc} */ - public function guessMinLength($class, $property) + public function guessPattern($class, $property) { $ret = $this->getMetadata($class); if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { diff --git a/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php b/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php index 840f1400505bc..7ee64cba57403 100644 --- a/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php +++ b/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php @@ -124,7 +124,7 @@ public function guessMaxLength($class, $property) /** * {@inheritDoc} */ - public function guessMinLength($class, $property) + public function guessPattern($class, $property) { if ($column = $this->getColumn($class, $property)) { switch ($column->getType()) { diff --git a/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php b/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php index 7fcbd4f711eaa..f60fc8f989ae3 100644 --- a/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php +++ b/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php @@ -46,14 +46,14 @@ public function testGuessMaxLengthWithFloat() public function testGuessMinLengthWithText() { - $value = $this->guesser->guessMinLength(self::CLASS_NAME, 'value'); + $value = $this->guesser->guessPattern(self::CLASS_NAME, 'value'); $this->assertNull($value); } public function testGuessMinLengthWithFloat() { - $value = $this->guesser->guessMinLength(self::CLASS_NAME, 'price'); + $value = $this->guesser->guessPattern(self::CLASS_NAME, 'price'); $this->assertNotNull($value); $this->assertNull($value->getValue()); diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php old mode 100644 new mode 100755 index 1931f7fa9d496..58c5e23e1e066 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -68,12 +68,12 @@ public function guessMaxLength($class, $property) /** * {@inheritDoc} */ - public function guessMinLength($class, $property) + public function guessPattern($class, $property) { $guesser = $this; return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) { - return $guesser->guessMinLengthForConstraint($constraint); + return $guesser->guessPatternForConstraint($constraint); }); } @@ -205,32 +205,35 @@ public function guessMaxLengthForConstraint(Constraint $constraint) } /** - * Guesses a field's minimum length based on the given constraint - * + * Guesses a field's pattern based on the given constraint + * * @param Constraint $constraint The constraint to guess for * - * @return Guess The guess for the minimum length + * @return Guess The guess for the pattern */ - public function guessMinLengthForConstraint(Constraint $constraint) + public function guessPatternForConstraint(Constraint $constraint) { switch (get_class($constraint)) { case 'Symfony\Component\Validator\Constraints\MinLength': - return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE); + return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE); case 'Symfony\Component\Validator\Constraints\SizeLength': - return new ValueGuess($constraint->min, Guess::HIGH_CONFIDENCE); + return new ValueGuess(sprintf('.{%s,%s}', (string) $constraint->min, (string) $constraint->max), Guess::LOW_CONFIDENCE); + + case 'Symfony\Component\Validator\Constraints\Regex': + return new ValueGuess($constraint->pattern, Guess::HIGH_CONFIDENCE ); + + case 'Symfony\Component\Validator\Constraints\Min': + return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE); + case 'Symfony\Component\Validator\Constraints\Size': + return new ValueGuess(sprintf('.{%s,%s}', strlen((string) $constraint->min), strlen((string) $constraint->max)), Guess::LOW_CONFIDENCE); + case 'Symfony\Component\Validator\Constraints\Type': if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } break; - - case 'Symfony\Component\Validator\Constraints\Min': - return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE); - - case 'Symfony\Component\Validator\Constraints\Size': - return new ValueGuess(strlen((string) $constraint->min), Guess::LOW_CONFIDENCE); } } diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php index 7d72f88488c0e..448116716da80 100644 --- a/src/Symfony/Component/Form/FormFactory.php +++ b/src/Symfony/Component/Form/FormFactory.php @@ -306,7 +306,7 @@ public function createNamedBuilder($type, $name, $data = null, array $options = /** * Returns a form builder for a property of a class. * - * If any of the 'max_length', 'required' and type options can be guessed, + * If any of the 'max_length', 'required', 'pattern' and type options can be guessed, * and are not provided in the options argument, the guessed value is used. * * @param string $class The fully qualified class name @@ -327,27 +327,26 @@ public function createBuilderForProperty($class, $property, $data = null, array $typeGuess = $this->guesser->guessType($class, $property); $maxLengthGuess = $this->guesser->guessMaxLength($class, $property); - $minLengthGuess = $this->guesser->guessMinLength($class, $property); $requiredGuess = $this->guesser->guessRequired($class, $property); + $patternGuess = $this->guesser->guessPattern($class, $property); $type = $typeGuess ? $typeGuess->getType() : 'text'; $maxLength = $maxLengthGuess ? $maxLengthGuess->getValue() : null; - $minLength = $minLengthGuess ? $minLengthGuess->getValue() : null; - $minLength = $minLength ?: 0; + $pattern = $patternGuess ? $patternGuess->getValue() : null; if (null !== $maxLength) { $options = array_merge(array('max_length' => $maxLength), $options); } - if ($minLength > 0) { - $options = array_merge(array('pattern' => '.{'.$minLength.','.$maxLength.'}'), $options); - } - if ($requiredGuess) { $options = array_merge(array('required' => $requiredGuess->getValue()), $options); } + if (null !== $pattern) { + $options = array_merge(array('pattern' => $pattern), $options); + } + // user options may override guessed options if ($typeGuess) { $options = array_merge($typeGuess->getOptions(), $options); diff --git a/src/Symfony/Component/Form/FormTypeGuesserChain.php b/src/Symfony/Component/Form/FormTypeGuesserChain.php index 9d915698ca134..ec40ce1e12d7d 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserChain.php +++ b/src/Symfony/Component/Form/FormTypeGuesserChain.php @@ -40,6 +40,9 @@ public function __construct(array $guessers) } } + /** + * {@inheritDoc} + */ public function guessType($class, $property) { return $this->guess(function ($guesser) use ($class, $property) { @@ -47,6 +50,9 @@ public function guessType($class, $property) }); } + /** + * {@inheritDoc} + */ public function guessRequired($class, $property) { return $this->guess(function ($guesser) use ($class, $property) { @@ -54,6 +60,9 @@ public function guessRequired($class, $property) }); } + /** + * {@inheritDoc} + */ public function guessMaxLength($class, $property) { return $this->guess(function ($guesser) use ($class, $property) { @@ -61,10 +70,13 @@ public function guessMaxLength($class, $property) }); } - public function guessMinLength($class, $property) + /** + * {@inheritDoc} + */ + public function guessPattern($class, $property) { return $this->guess(function ($guesser) use ($class, $property) { - return $guesser->guessMinLength($class, $property); + return $guesser->guessPattern($class, $property); }); } diff --git a/src/Symfony/Component/Form/FormTypeGuesserInterface.php b/src/Symfony/Component/Form/FormTypeGuesserInterface.php index 292dc11e54268..a029526a4269a 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserInterface.php +++ b/src/Symfony/Component/Form/FormTypeGuesserInterface.php @@ -44,13 +44,18 @@ function guessRequired($class, $property); function guessMaxLength($class, $property); /** - * Returns a guess about the field's minimum length + * Returns a guess about the field's pattern + * + * - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE) , lines below + * - If this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess. + * Example: + * You want a float greater than 5, 4.512313 is not valid but length(4.512314) > length(5) + * @link https://github.com/symfony/symfony/pull/3927 * * @param string $class The fully qualified class name * @param string $property The name of the property to guess for * - * @return Guess A guess for the field's minimum length + * @return Guess A guess for the field's required pattern */ - function guessMinLength($class, $property); - + function guessPattern($class, $property); } diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 49e53e833785c..3336ad1faa6d1 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -491,6 +491,39 @@ public function testCreateBuilderUsesRequiredSettingWithHighestConfidence() $this->assertEquals('builderInstance', $builder); } + public function testCreateBuilderUsesPatternIfFound() + { + $this->guesser1->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '/[a-z]/', + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '/[a-zA-Z]/', + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->createMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('text', 'firstName', null, array('pattern' => '/[a-zA-Z]/')) + ->will($this->returnValue('builderInstance')); + + $builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $builder); + } + public function testCreateNamedBuilderFromParentBuilder() { $type = new FooType(); @@ -541,18 +574,18 @@ public function testUnknownOption() $factory->createNamedBuilder($type, "text", "value", array("unknown" => "opt")); } - public function testFormTypeCreatesDefaultValueForEmptyDataOption() - { - $factory = new FormFactory(array(new \Symfony\Component\Form\Extension\Core\CoreExtension())); - - $form = $factory->createNamedBuilder(new AuthorType(), 'author')->getForm(); - $form->bind(array('firstName' => 'John', 'lastName' => 'Smith')); - - $author = new Author(); - $author->firstName = 'John'; - $author->setLastName('Smith'); - - $this->assertEquals($author, $form->getData()); + public function testFieldTypeCreatesDefaultValueForEmptyDataOption() + { + $factory = new FormFactory(array(new \Symfony\Component\Form\Extension\Core\CoreExtension())); + + $form = $factory->createNamedBuilder(new AuthorType(), 'author')->getForm(); + $form->bind(array('firstName' => 'John', 'lastName' => 'Smith')); + + $author = new Author(); + $author->firstName = 'John'; + $author->setLastName('Smith'); + + $this->assertEquals($author, $form->getData()); } private function createMockFactory(array $methods = array())