Skip to content

Commit 3e03efd

Browse files
committed
Merge branch '5.4' into 6.3
* 5.4: Describe how to create a custom constraint with options
2 parents 2219fd2 + f07d861 commit 3e03efd

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

validation/custom_constraint.rst

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,231 @@ then your validator is already registered as a service and :doc:`tagged </servic
217217
with the necessary ``validator.constraint_validator``. This means you can
218218
:ref:`inject services or configuration <services-constructor-injection>` like any other service.
219219

220+
Constraint Validators with Custom Options
221+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222+
223+
If you want to add some configuration options to your custom constraint, first
224+
define those options as public properties on the constraint class:
225+
226+
.. configuration-block::
227+
228+
.. code-block:: php-annotations
229+
230+
// src/Validator/Foo.php
231+
namespace App\Validator;
232+
233+
use Symfony\Component\Validator\Constraint;
234+
235+
/**
236+
* @Annotation
237+
*/
238+
class Foo extends Constraint
239+
{
240+
public $mandatoryFooOption;
241+
public $message = 'This value is invalid';
242+
public $optionalBarOption = false;
243+
244+
public function __construct(
245+
$mandatoryFooOption,
246+
string $message = null,
247+
bool $optionalBarOption = null,
248+
array $groups = null,
249+
$payload = null,
250+
array $options = []
251+
) {
252+
if (\is_array($mandatoryFooOption)) {
253+
$options = array_merge($mandatoryFooOption, $options);
254+
} elseif (null !== $mandatoryFooOption) {
255+
$options['value'] = $mandatoryFooOption;
256+
}
257+
258+
parent::__construct($options, $groups, $payload);
259+
260+
$this->message = $message ?? $this->message;
261+
$this->optionalBarOption = $optionalBarOption ?? $this->optionalBarOption;
262+
}
263+
264+
public function getDefaultOption()
265+
{
266+
// If no associative array is passed to the constructor this
267+
// property is set instead.
268+
269+
return 'mandatoryFooOption';
270+
}
271+
272+
public function getRequiredOptions()
273+
{
274+
// return names of options which must be set.
275+
276+
return ['mandatoryFooOption'];
277+
}
278+
}
279+
280+
.. code-block:: php-attributes
281+
282+
// src/Validator/Foo.php
283+
namespace App\Validator;
284+
285+
use Symfony\Component\Validator\Constraint;
286+
287+
#[\Attribute]
288+
class Foo extends Constraint
289+
{
290+
public $mandatoryFooOption;
291+
public $message = 'This value is invalid';
292+
public $optionalBarOption = false;
293+
294+
public function __construct(
295+
$mandatoryFooOption,
296+
string $message = null,
297+
bool $optionalBarOption = null,
298+
array $groups = null,
299+
$payload = null,
300+
array $options = []
301+
) {
302+
if (\is_array($mandatoryFooOption)) {
303+
$options = array_merge($mandatoryFooOption, $options);
304+
} elseif (null !== $mandatoryFooOption) {
305+
$options['value'] = $mandatoryFooOption;
306+
}
307+
308+
parent::__construct($options, $groups, $payload);
309+
310+
$this->message = $message ?? $this->message;
311+
$this->optionalBarOption = $optionalBarOption ?? $this->optionalBarOption;
312+
}
313+
314+
public function getDefaultOption()
315+
{
316+
return 'mandatoryFooOption';
317+
}
318+
319+
public function getRequiredOptions()
320+
{
321+
return ['mandatoryFooOption'];
322+
}
323+
}
324+
325+
Then, inside the validator class you can access these options directly via the
326+
constraint class passes to the ``validate()`` method::
327+
328+
class FooValidator extends ConstraintValidator
329+
{
330+
public function validate($value, Constraint $constraint)
331+
{
332+
// access any option of the constraint
333+
if ($constraint->optionalBarOption) {
334+
// ...
335+
}
336+
337+
// ...
338+
}
339+
}
340+
341+
When using this constraint in your own application, you can pass the value of
342+
the custom options like you pass any other option in built-in constraints:
343+
344+
.. configuration-block::
345+
346+
.. code-block:: php-annotations
347+
348+
// src/Entity/AcmeEntity.php
349+
namespace App\Entity;
350+
351+
use App\Validator as AcmeAssert;
352+
use Symfony\Component\Validator\Constraints as Assert;
353+
354+
class AcmeEntity
355+
{
356+
// ...
357+
358+
/**
359+
* @Assert\NotBlank
360+
* @AcmeAssert\Foo(
361+
* mandatoryFooOption="bar",
362+
* optionalBarOption=true
363+
* )
364+
*/
365+
protected $name;
366+
367+
// ...
368+
}
369+
370+
.. code-block:: php-attributes
371+
372+
// src/Entity/AcmeEntity.php
373+
namespace App\Entity;
374+
375+
use App\Validator as AcmeAssert;
376+
use Symfony\Component\Validator\Constraints as Assert;
377+
378+
class AcmeEntity
379+
{
380+
// ...
381+
382+
#[Assert\NotBlank]
383+
#[AcmeAssert\Foo(
384+
mandatoryFooOption: 'bar',
385+
optionalBarOption: true
386+
)]
387+
protected $name;
388+
389+
// ...
390+
}
391+
392+
.. code-block:: yaml
393+
394+
# config/validator/validation.yaml
395+
App\Entity\AcmeEntity:
396+
properties:
397+
name:
398+
- NotBlank: ~
399+
- App\Validator\Foo:
400+
mandatoryFooOption: bar
401+
optionalBarOption: true
402+
403+
.. code-block:: xml
404+
405+
<!-- config/validator/validation.xml -->
406+
<?xml version="1.0" encoding="UTF-8" ?>
407+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
408+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
409+
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
410+
411+
<class name="App\Entity\AcmeEntity">
412+
<property name="name">
413+
<constraint name="NotBlank"/>
414+
<constraint name="App\Validator\Foo">
415+
<option name="mandatoryFooOption">bar</option>
416+
<option name="optionalBarOption">true</option>
417+
</constraint>
418+
</property>
419+
</class>
420+
</constraint-mapping>
421+
422+
.. code-block:: php
423+
424+
// src/Entity/AcmeEntity.php
425+
namespace App\Entity;
426+
427+
use App\Validator\ContainsAlphanumeric;
428+
use Symfony\Component\Validator\Constraints\NotBlank;
429+
use Symfony\Component\Validator\Mapping\ClassMetadata;
430+
431+
class AcmeEntity
432+
{
433+
public $name;
434+
435+
public static function loadValidatorMetadata(ClassMetadata $metadata)
436+
{
437+
$metadata->addPropertyConstraint('name', new NotBlank());
438+
$metadata->addPropertyConstraint('name', new Foo([
439+
'mandatoryFooOption' => 'bar',
440+
'optionalBarOption' => true,
441+
]));
442+
}
443+
}
444+
220445
Create a Reusable Set of Constraints
221446
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222447

0 commit comments

Comments
 (0)