@@ -233,6 +233,230 @@ then your validator is already registered as a service and :doc:`tagged </servic
233
233
with the necessary ``validator.constraint_validator ``. This means you can
234
234
:ref: `inject services or configuration <services-constructor-injection >` like any other service.
235
235
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
+
236
460
Create a Reusable Set of Constraints
237
461
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238
462
0 commit comments