From 29a6194abe82bc4a8fc18dd5fc4ff0b79cffb049 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Fri, 21 Feb 2020 14:52:00 +0100 Subject: [PATCH] [Validator] Add a constraint to sequentially validate a set of constraints --- reference/constraints.rst | 1 + reference/constraints/Sequentially.rst | 142 +++++++++++++++++++++++++ reference/constraints/map.rst.inc | 1 + validation/sequence_provider.rst | 11 ++ 4 files changed, 155 insertions(+) create mode 100644 reference/constraints/Sequentially.rst diff --git a/reference/constraints.rst b/reference/constraints.rst index 4335c9f13ae..760c0199b89 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -63,6 +63,7 @@ Validation Constraints Reference constraints/Isbn constraints/Issn + constraints/Sequentially constraints/Compound constraints/Callback constraints/Expression diff --git a/reference/constraints/Sequentially.rst b/reference/constraints/Sequentially.rst new file mode 100644 index 00000000000..6d4ea2e164c --- /dev/null +++ b/reference/constraints/Sequentially.rst @@ -0,0 +1,142 @@ +Sequentially +============ + +This constraint allows you to apply a set of rules that should be validated +step-by-step, allowing to interrupt the validation once the first violation is raised. + +As an alternative in situations ``Sequentially`` cannot solve, you may consider +using :doc:`GroupSequence` which allows more control. + +.. versionadded:: 5.1 + + The ``Sequentially`` constraint was introduced in Symfony 5.1. + +========== =================================================================== +Applies to :ref:`property or method ` +Options - `constraints`_ + - `groups`_ + - `payload`_ +Class :class:`Symfony\\Component\\Validator\\Constraints\\Sequentially` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\SequentiallyValidator` +========== =================================================================== + +Basic Usage +----------- + +Suppose that you have a ``Place`` object with an ``$address`` property which +must match the following requirements: +- it's a non-blank string +- of at least 10 chars long +- with a specific format +- and geolocalizable using an external service + +In such situations, you may encounter three issues: +- the ``Length`` or ``Regex`` constraints may fail hard with a :class:`Symfony\\Component\\Validator\\Exception\\UnexpectedValueException` +exception if the actual value is not a string, as enforced by ``Type``. +- you may end with multiple error messages for the same property +- you may perform a useless and heavy external call to geolocalize the address, +while the format isn't valid. + +You can validate each of these constraints sequentially to solve these issues: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Localization/Place.php + namespace App\Localization; + + use App\Validator\Constraints as AcmeAssert; + use Symfony\Component\Validator\Constraints as Assert; + + class Place + { + /** + * @var string + * + * @Assert\Sequentially({ + * @Assert\NotBlank(), + * @Assert\Type("string"), + * @Assert\Length(min=10), + * @Assert\Regex(Place::ADDRESS_REGEX), + * @AcmeAssert\Geolocalizable(), + * }) + */ + public $address; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Localization\Place: + properties: + address: + - Sequentially: + - NotBlank: ~ + - Type: string + - Length: { min: 10 } + - Regex: !php/const App\Localization\Place::ADDRESS_REGEX + - App\Validator\Constraints\Geolocalizable: ~ + + .. code-block:: xml + + + + + + + + + + string + + + + + + + + + + + + + .. code-block:: php + + // src/Localization/Place.php + namespace App\Localization; + + use App\Validator\Constraints as AcmeAssert; + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Place + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('address', new Assert\Sequentially([ + new Assert\NotBlank(), + new Assert\Type("string"), + new Assert\Length(['min' => 10]), + new Assert\Regex(self::ADDRESS_REGEX), + new AcmeAssert\Geolocalizable(), + ])); + } + } + +Options +------- + +``constraints`` +~~~~~~~~~~~~~~~ + +**type**: ``array`` [:ref:`default option `] + +This required option is the array of validation constraints that you want +to apply sequentially. + +.. include:: /reference/constraints/_groups-option.rst.inc + +.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 963a2816fa0..dbc6a3b5d74 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -84,6 +84,7 @@ Financial and other Number Constraints Other Constraints ~~~~~~~~~~~~~~~~~ +* :doc:`Sequentially ` * :doc:`Compound ` * :doc:`Callback ` * :doc:`Expression ` diff --git a/validation/sequence_provider.rst b/validation/sequence_provider.rst index 98a9389212f..4bb6302e4a8 100644 --- a/validation/sequence_provider.rst +++ b/validation/sequence_provider.rst @@ -355,3 +355,14 @@ provides a sequence of groups to be validated: // ... } } + +How to Sequentially Apply Constraints on a Single Property +========================================================== + +Sometimes, you may want to apply constraints sequentially on a single +property. The :doc:`Sequentially constraint` +can solve this for you in a more straightforward way than using a ``GroupSequence``. + +.. versionadded:: 5.1 + + The ``Sequentially`` constraint was introduced in Symfony 5.1.