This chapter explains the parser layer that sits underneath Jetstack expressions. The old name "validators" is misleading here. The code in validators.php is primarily about:
This is the layer that turns strings like {$this.owner.full_name}, {?sum(this.items, price)}, or {$this.status|upper} into actual runtime values.
Two expressions can look similar and still be handled by different parser mechanisms:
{$this.owner.full_name} is variable traversal{=contains({$this.tags}, "vip")} is code-processor evaluation{?sum(this.items, price)} is utility-shortcode evaluation{%status%} is table-column translation{$this.title|upper} is variable traversal followed by a string filterWhen an expression behaves unexpectedly, the most useful question is often not "is the data wrong?" but "which parser is responsible for this expression?"
The variable parser resolves dot-path access from the current context. It is responsible for expressions such as:
{$this.customer.name}
{$this.parent.status}
{$this.lines[:first].price}
This parser can also apply a small set of path-level helper nodes and final string filters.
The math parser delegates to the code processor. This is the {=...} layer. It exposes the function catalog documented in Code Processor Functions.
It also adds one extra fallback helper:
try(arg1, arg2, arg3, arg4)That helper attempts variable resolution on each argument until one produces a non-null result.
The table-column parser translates a property reference into the SQL-oriented table-column representation used by query logic.
Use this mental model when a feature needs a column-safe expression rather than a resolved runtime value.
The utility parser is the shortcode layer behind {?...} expressions. This is the part of validators.php that exposes the built-in shortcode-style helper functions documented in detail below.
These are the utility shortcodes defined in UtilsParser.
date(...)Purpose:
Formats the current date or a provided date value.
Syntax:
{?date()}
{?date("Y-m-d")}
{?date("d.m.Y", "2026-04-10")}
Parameters:
strtotime()Behavior:
Y-m-dReturn value:
Formatted date string.
Notes:
date(...) helper.{=...} expressions.sum(collectionExpression, itemExpression)Purpose:
Calculates the sum of a value over an iterable collection.
Syntax:
{?sum(this.items, price)}
{?sum(this.positions, total_price)}
Parameters:
Behavior:
VariableParserReturn value:
Numeric sum. Returns 0 when argument count is wrong or the first argument is not iterable.
Use this when:
avg(collectionExpression, itemExpression)Purpose:
Calculates the arithmetic average over an iterable collection.
Syntax:
{?avg(this.lines, amount)}
Parameters:
Behavior:
VariableParserReturn value:
Average value as a number. Returns 0 when there are no usable values or the argument count is wrong.
Notes:
min(collectionExpression, itemExpression)Purpose:
Finds the minimum value over an iterable collection.
Syntax:
{?min(this.lines, amount)}
Parameters:
Behavior:
min() to the non-empty result setReturn value:
Minimum value from the collected results.
Notes:
0.avg, it is safest to use this with clearly numeric values.max(collectionExpression, itemExpression)Purpose:
Finds the maximum value over an iterable collection.
Syntax:
{?max(this.lines, amount)}
Parameters:
Behavior:
max() to the non-empty result setReturn value:
Maximum value from the collected results.
Notes:
0.count(collectionExpression)Purpose:
Counts items in an iterable value.
Syntax:
{?count(this.items)}
Parameters:
Behavior:
VariableParsercount(...) if the result is iterableReturn value:
Item count as a number. Returns 0 when the value is not iterable or the argument count is wrong.
lookup(type, relationPath, property = "id", calculation = "LIST", returnType = "rendered")Purpose:
Performs a lookup across related records and optionally aggregates the target property.
Syntax:
{?lookup("Invoice", "customer", "amount", "SUM", "plain")}
{?lookup("Project", "client.account_manager", "id", "COUNT", "raw")}
{?lookup("Task", "project", "name")}
Parameters:
idSupported calculation modes:
LIST: no aggregate function, return matching values as a list-like resultSUMAVGMINMAXCOUNTThe source sanitizes the aggregation name by uppercasing it and stripping hyphens.
Supported return types:
renderedplainrawBehavior:
Return value:
rendered or plain: concatenated string with <br> separatorsraw: JSON-encoded array of raw IDs or raw valuesnull on invalid setup in most failure cases0 when the calculation is COUNT and the query yields no resultWhen to use it:
When not to use it:
try(arg1, arg2, arg3, ...)Purpose:
Returns the first argument whose variable resolution yields a non-null result.
Syntax:
{?try(this.title, this.name, this.id)}
Parameters:
Behavior:
VariableParsernull resultReturn value:
First non-null resolved value, or null when none resolve.
Notes:
{=...} expressions. The purpose is the same: fallback resolution across multiple candidates.The variable parser supports more than simple field access. These helpers appear as path nodes inside {$...} expressions.
| Helper | What it does | Notes |
|---|---|---|
this |
Refers to the current base object context. | Mostly redundant, but useful for clarity. |
self |
Refers to the current property value in property-aware evaluation. | In some flows it can use a custom provided self value. |
parent |
Moves to the parent object or component. | In strict mode, unresolved parent lookup throws a scope-resolution exception. |
__User |
Resolves the current user object when used as the first path node. | Useful in context-aware display logic. |
getContext(TypeSystemName) |
Looks up a context object by type system name from the merged stack. | Works with context-aware builders and nested scopes. |
lookup(ClassName, conditions...) |
Looks up an object from the context stack by model class name, optionally filtered by conditions such as typeId:Invoice or other properties. |
This is different from the utility shortcode lookup(...). This helper searches the in-memory context stack, not related database rows. |
| Helper | What it does | Notes |
|---|---|---|
toString() |
Converts the current result into string form. | Uses Utils::xToString(...). |
plain() |
On a property node, returns the plain property value. | Only meaningful when the current result is a Property. |
raw() |
On a property node, returns the raw stored value. | Useful when rendered or normalized value would hide the actual storage representation. |
count() |
On an array result, returns count(...). |
Path-level helper, different from the utility shortcode. |
max() |
On an array result, returns max(...) or 0 for an empty array. |
|
min() |
On an array result, returns min(...) or 0 for an empty array. |
|
avg() |
On an array result, returns the average of filtered values. |
| Helper | What it does | Notes |
|---|---|---|
[:first] |
Returns the first item from an array, or the first character from a string/scalar value. | |
[:last] |
Returns the last item from an array, or the last character from a string/scalar value. | |
[n] |
Returns an indexed item from an array or a character at index n from a scalar/string value. |
After variable resolution, the parser can apply pipe-style post-processing filters:
{$this.title|upper}
{$this.code|trim|lower}
{$this.name|replace:a|b}
The parser splits filters by |. For each filter:
Nette\Utils\Strings: and are split further by |Nette\Utils\Strings are supported hereThis parser layer does not define its own custom named filter catalog. Instead, it forwards to Nette\Utils\Strings when a matching method exists.
That means the supported filter surface depends on the string utility methods available in the installed Nette version.
Several parser branches distinguish between strict and non-strict evaluation:
QueryScopeResolutionExceptionnull-like fallback values insteadThis matters most in:
Use these rules of thumb:
{$...} when you need to read from context{=...} when you need boolean logic, code-processor functions, or typed composition{?...} when you need one of the utility shortcodes from this chapter{%...%} when the target feature expects table-column translationlookup(...) shortcode carefully. It is powerful, but it also hides real query complexity inside an expression.try(...) short and intentional so fallback order stays readable.raw() only when the stored form truly matters for the behavior you are implementing.