Skip to content

Validate scalars with PropertyGate and Gate::assert()

Verja\PropertyGate validates a single value through the full four-stage pipeline: converter → null policy → filters → validators. It is used internally by Gate's typed methods, but can also be used directly and is the basis for Gate::assert().

Optional by default

Unlike Gate and ArrayGate, a PropertyGate has no required prefix validator by default. When used as a property inside a Gate, absent or empty values are skipped automatically by the parent gate. Pass 'required' explicitly to enforce presence:

$gate = new PropertyGate('required', 'strLen:3:20');

Fluent required / nullable

Use the builder methods to set the null policy after construction:

$gate = (new PropertyGate('trim', 'strLen:3:20'))->required();

// Conditional — required only when 'type' is present in context:
$gate = (new PropertyGate('notEmpty'))->required('type');

// Nullable with fallback when condition is not met:
$gate = (new PropertyGate())->required('type', 'nullable');

// Always nullable:
$gate = (new PropertyGate('trim'))->nullable();

required($condition, $fallback, $default) and nullable($condition) mirror the NullPolicy\Required and NullPolicy\Nullable constructors exactly. Each call replaces any previously set null policy (last wins).

Mutating a gate after construction

These methods are available on all gate types (PropertyGate, Gate, ArrayGate). All methods accept a string definition or an object instance. Strings are resolved the same way as in the constructor. Callables are wrapped in Validator\Callback. Converter and null policy setters accept null to remove them.

// Add any rule — routes by type (converter, null policy, filter, validator)
$gate->add('slug');
$gate->prepend('notEmpty');

// Filters
$gate->addFilter('stripTags');
$gate->prependFilter(new Filter\Replace('foo', 'bar'));

// Validators
$gate->addValidator('emailAddress');
$gate->prependValidator(new Validator\StrLen(3, 100));

// Converter and null policy (replace; null removes)
$gate->setConverter('boolean');
$gate->setNullPolicy(new NullPolicy\DefaultValue(0));

// Remove — returns number of removed entries
$gate->remove('slug');                   // searches converter, null policy, filters, validators
$gate->removeFilter(new Filter\Trim());  // filters only
$gate->removeValidator('emailAddress'); // validators only

// Deprecated
$gate->appendFilter('stripTags');                      // use addFilter()
$gate->appendValidator('emailAddress');                // use addValidator()
$gate->addFilter('stripTags', true);                   // use prependFilter()
$gate->addValidator(new Validator\StrLen(3), true);    // use prependValidator()

Using PropertyGate directly

use Verja\PropertyGate;

$gate = new PropertyGate('trim', 'notEmpty', 'strLen:3:100');
$result = $gate->validate($input);

if ($result->valid) {
    $value = $result->data; // trimmed, validated string
} else {
    foreach ($result->errors as $error) {
        echo $error->message . "\n";
    }
}

Filters and validators are passed in order — filters run first, validators after:

// Execution: trim → cast to int → between check
$gate = new PropertyGate('trim', 'integer', 'between:1:100');

The ! prefix negates a validator:

$gate = new PropertyGate('!notEmpty'); // value must be empty

Gates as type options

A PropertyGate can hold one or more gate arguments in addition to filters and validators. Gates are detected automatically when a GateInterface instance or a plain array is passed. They always run after all validators.

Single gate — delegate validation

Pass one gate to hand validation off entirely after the validators pass:

use Verja\Gate;
use Verja\PropertyGate;

// isStructured must pass first; then the nested Gate handles the properties
$gate = new PropertyGate('isStructured', (new Gate())->string('city', 'required'));

This is how ->object() works internally.

Multiple gates — implicit union (OR)

Pass two or more gates to require the value to satisfy at least one of them. The first gate that passes wins; its validated and filtered data is returned.

Arrays are converted to a PropertyGate automatically, so each array option is a compact shorthand for a set of validators:

// Value must be a string (min 3 chars) OR an array of exactly 2 strings
$gate = new PropertyGate(
    ['isString', 'strLen:3'],
    new ArrayGate('exactly:2', ['isString'])
);

This also works directly in typed Gate methods. Validators run first; then the gate options are tried:

// isString is validated first; then the value must match 'dateTime' OR 'date'
$gate->string('start', ['dateTime'], ['date']);

When all gates fail, the error key is NO_OPTION_MATCHED. The per-option error maps are available as $error->parameters['options'] — an array indexed by gate position.

Union gate nullable

To make a union-typed property optional, pass 'nullable' alongside the gates. The nullable prefix runs before the gates and short-circuits on null:

$gate->any('field', 'nullable', new PropertyGate('isString'), $someGate);

Gate::assert()

Gate::assert() is a static shortcut that validates a single value and either returns the filtered result or throws Verja\Exception\InvalidValue. Useful for assertions in controllers or service methods where throwing is the right behaviour.

use Verja\Gate;
use Verja\Exception\InvalidValue;

// Returns filtered value on success
$username = Gate::assert($input, 'required', 'trim', 'strLen:3:20');

// With filters that convert types
$age = Gate::assert($input, 'integer', 'between:0:150');

// Catching errors
try {
    $email = Gate::assert($input, 'required', 'emailAddress');
} catch (InvalidValue $e) {
    foreach ($e->errors as $error) {
        echo $error->key . ': ' . $error->message . "\n";
    }
}

InvalidValue carries both $e->errors (flat Error[]) and $e->errorMap (same flat array, keyed by '__scalar__' for single-value results).

Passing context

To supply a context array alongside the value (needed for cross-field validators like Equals), wrap the first argument:

Gate::assert(
    ['value' => $confirmation, 'context' => ['password' => $password]],
    'required',
    'equals:password'
);