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