Skip to content

Commit cf49830

Browse files
committed
minor #15586 [Validator] Describe how to create a custom constraint with options (W0rma)
This PR was submitted for the 5.3 branch but it was merged into the 5.4 branch instead. Discussion ---------- [Validator] Describe how to create a custom constraint with options Closes #15501 Commits ------- 3f8c7fc Describe how to create a custom constraint with options
2 parents 6a123d8 + 3f8c7fc commit cf49830

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed

validation/custom_constraint.rst

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

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

0 commit comments

Comments
 (0)