@@ -217,6 +217,231 @@ then your validator is already registered as a service and :doc:`tagged </servic
217
217
with the necessary ``validator.constraint_validator ``. This means you can
218
218
:ref: `inject services or configuration <services-constructor-injection >` like any other service.
219
219
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
+
220
445
Create a Reusable Set of Constraints
221
446
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222
447
0 commit comments