Skip to content

Validators

Validators are rules describing what data a column accepts. They are declared in schema.xml with <validator> tags and checked in PHP, before an object is persisted.

A <validator> binds a set of <rule> tags to a column. Each <rule> names a validator, an optional value, and the message shown on failure. For example, requiring username to be at least 4 characters:

<table name="user">
<column name="id" type="integer" primaryKey="true" autoIncrement="true" />
<column name="username" type="varchar" size="34" required="true" />
<validator column="username">
<rule name="minLength" value="4" message="Username must be at least ${value} characters." />
</validator>
</table>

${value} in a message is replaced with the rule’s value. You can apply several rules to one column by nesting multiple <rule> tags in a single <validator> (or by declaring multiple <validator> tags for the same column):

<column name="security_level" type="integer" required="true" default="10" />
<validator column="security_level">
<rule name="minValue" value="0" message="Security level must be between 0 and 10." />
<rule name="maxValue" value="10" message="Security level must be between 0 and 10." />
</validator>

After rebuilding your model, each ActiveRecord object gains a validate() method (returning a bool) and a getValidationFailures() method (returning an array of Propulsion\Validator\ValidationFailed objects):

$user = new User();
$user->setUsername('foo'); // 3 characters — too short
if ($user->validate()) {
$user->save();
} else {
foreach ($user->getValidationFailures() as $failure) {
echo $failure->getMessage() . "\n";
}
}

Each ValidationFailed exposes getColumn(), getMessage(), and getValidator(). validate() optionally takes a column name or array of column names to validate only a subset (validate(array|string|null $columns = null)).

Propulsion bundles validators for the most common cases. The name attribute of a <rule> selects one:

Rule nameChecksNotes
requiredThe value is present.A cleaner, PHP-level counterpart to required="true" on the column. No value.
minLengthString length ≥ value.Uses mb_strlen() when available.
maxLengthString length ≤ value.If the column has a size, value may be omitted and defaults to it.
minValueNumber ≥ value (non-strict).
maxValueNumber ≤ value (non-strict).
matchValue matches a preg pattern in value.Pattern is given without delimiters, or delimited with /. Other delimiters are not supported.
notMatchValue does not match the preg pattern in value.Same delimiter rule as match.
validValuesValue is one of a `-delimited list in value`.
typeValue is of the PHP type named in value.e.g. value="string".
uniqueThe value does not already exist in the table.No value.

A few worked examples:

<!-- email address by regular expression -->
<validator column="email">
<rule name="match"
value="/^([a-zA-Z0-9])+([\.a-zA-Z0-9_-])*@([a-zA-Z0-9])+(\.[a-zA-Z0-9_-]+)+$/"
message="Please enter a valid email address." />
</validator>
<!-- restrict to a fixed set of values -->
<validator column="address_type">
<rule name="validValues" value="account|delivery" message="Please select a valid address type." />
</validator>
<!-- reject anything that isn't a digit or a dash -->
<validator column="isbn">
<rule name="notMatch" value="/[^\d-]+/" message="Please enter a valid ISBN." />
</validator>

A custom validator is a class implementing Propulsion\Validator\BasicValidator, which requires a single isValid(ValidatorMap $map, $str): bool method. The ValidatorMap gives access to the rule’s attributes — $map->getValue() returns the rule’s value:

use Propulsion\Validator\BasicValidator;
use Propulsion\Map\ValidatorMap;
class EmailValidator implements BasicValidator
{
public function isValid(ValidatorMap $map, $str): bool
{
return preg_match('/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i', $str ?? '') !== 0;
}
}

Enable it with a <rule> whose name is class and whose class attribute is the validator’s dot-path — the class name with namespace separators replaced by dots, which Propulsion resolves via Propulsion::importClass():

<validator column="email">
<rule name="class" class="app.validator.EmailValidator" message="Invalid e-mail address." />
</validator>