Conditional Validation with the Decorator Design Pattern

Igor Vorobiov
Mar 6, 2018 · 2 min read
“A set of formal suits on hangers in a wardrobe” by Igor Ovsyannykov on Unsplash

It’s often needed to apply certain validation rules only if certain values are present. For example, let’s say we want the field1 to be required only if the field2 has value1. Or, let’s assume field3 can contain only value3 or value4 if field4 is equal to value5 otherwise, the field3 can contain only value6, value7.

Now imagine, we have a validation library that already ships with validation rules for checking whether a field is present and not empty, and whether the value of a field is in the specified list of values. In other words, we have the following validation rules:

$validator
->addRule('field1', new NotBlank())
->addRule('field2', new NotBlank())
->addRule('field4', new NotBlank())
->addRule('field3', [
new NotBlank(),
new Choices(['value3', 'value4'])
]);

In this example, we apply the validation rules without conditions and not as described above. What should we do to match the requirements?

Sure, we could extend the NotBlank and the Choices classes. Then, we could override the isValid method and add an additional logic to there to check what’s needed. Of course, that would work but there’s more elegant way to achieve this.

What if we create a special validation rule to accept the other validation rules and execute them only if the provided condition passes. Consider the following changes:

$validator
->addRule('field1',
new When('field2 == "value1"', new NotBlank()))

->addRule('field2', new NotBlank())
->addRule('field4', new NotBlank())
->addRule('field3', [
new NotBlank(),
new When('field4 == "value5"',
new Choices(['value3', 'value4'])),

new When('field4 != "value5"',
new Choices(['value6', 'value7']))
]);

As you can see, we didn’t extend already existing rules. Instead, we have created a decorator that adds an additional functionality on top of any existing rules. The implementation of the When class would look like this:

class When implements RuleInterface {

private $rule;

private $condition;

...

public function __construct(
string $condition, RuleInterface $rule)
{
$this->condition = $condition;
$this->rule = $rule;
}

public function isValid($value) {

if ($this->expression->evaluate($this->condition)){
return $this->rule->isValid($value);
}

return true;
}
}

(Note: the above code is not a complete solution. Its intent to show an idea of how the problem can be solved)

All in all

You should also read here about the most common design patterns. Also, if you are curious about how to build your own validator then you should read this.