Skip to content

Template Expressions

Powerful template expression engine for declarative data transformations - inspired by Twig, but designed specifically for data mapping.

The Template Expression Engine provides a powerful expression syntax that works across all mapping methods:

  • Transform values using filter syntax (e.g., | lower, | trim)
  • Provide defaults for null/missing values (e.g., ?? 'Unknown')
  • Chain multiple filters (e.g., | trim | lower | ucfirst)
  • Reference source fields (e.g., {{ user.name }})
  • Reference target fields using aliases (e.g., {{ @fieldName }})
  • Use static values (e.g., 'admin' without {{ }})
  • Wildcard support - Apply filters to array elements (e.g., {{ users.*.name | upper }})

Key Features:

  • 🎯 Declarative syntax - Define transformations in the template
  • 🔄 Unified across all methods - Same syntax in map(), mapFromFile(), and mapFromTemplate()
  • 🔄 Composable filters - Chain multiple transformations
  • 📦 15+ built-in filters - Common transformations out of the box
  • 🔧 Extensible - Register custom filters
  • Fast - Optimized expression parsing and evaluation
use event4u\DataHelpers\DataMapper;
$sources = [
'user' => [
'firstName' => 'alice',
'email' => ' ALICE@EXAMPLE.COM ',
'age' => null,
],
];
$template = [
'profile' => [
// Simple expression
'name' => '{{ user.firstName | ucfirst }}',
// Expression with default value
'age' => '{{ user.age ?? 18 }}',
// Multiple filters
'email' => '{{ user.email | trim | lower }}',
],
];
$result = DataMapper::mapFromTemplate($template, $sources);
// Result:
// [
// 'profile' => [
// 'name' => 'Alice',
// 'age' => 18,
// 'email' => 'alice@example.com',
// ]
// ]

Access source data using dot-notation paths wrapped in {{ }}:

$template = [
'name' => '{{ user.name }}',
'email' => '{{ user.contact.email }}',
];

Provide fallback values for null/missing data using ??:

$template = [
'name' => '{{ user.name ?? "Unknown" }}',
'age' => '{{ user.age ?? 18 }}',
'role' => '{{ user.role ?? "guest" }}',
];

Transform values using the pipe | operator:

$template = [
'name' => '{{ user.name | upper }}',
'email' => '{{ user.email | lower }}',
'title' => '{{ post.title | trim }}',
];

Chain multiple filters together:

$template = [
'name' => '{{ user.name | trim | lower | ucfirst }}',
'slug' => '{{ post.title | trim | lower | replace:" ":"-" }}',
];

Reference target fields using @ prefix:

$template = [
'firstName' => '{{ user.firstName }}',
'lastName' => '{{ user.lastName }}',
'fullName' => '{{ @firstName }} {{ @lastName }}',
];
// upper - Convert to uppercase
'{{ name | upper }}' // 'john' -> 'JOHN'
// lower - Convert to lowercase
'{{ name | lower }}' // 'JOHN' -> 'john'
// ucfirst - Uppercase first character
'{{ name | ucfirst }}' // 'john' -> 'John'
// trim - Remove whitespace
'{{ name | trim }}' // ' john ' -> 'john'
// replace - Replace text
'{{ name | replace:"a":"b" }}' // 'apple' -> 'bpple'
// first - Get first element
'{{ items | first }}' // [1, 2, 3] -> 1
// last - Get last element
'{{ items | last }}' // [1, 2, 3] -> 3
// count - Count elements
'{{ items | count }}' // [1, 2, 3] -> 3
// join - Join array elements
'{{ items | join:", " }}' // [1, 2, 3] -> '1, 2, 3'
// int - Convert to integer
'{{ value | int }}' // '42' -> 42
// float - Convert to float
'{{ value | float }}' // '3.14' -> 3.14
// bool - Convert to boolean
'{{ value | bool }}' // '1' -> true
// string - Convert to string
'{{ value | string }}' // 42 -> '42'
// date - Format date
'{{ created | date:"Y-m-d" }}' // DateTime -> '2024-01-15'
// timestamp - Convert to timestamp
'{{ created | timestamp }}' // DateTime -> 1705276800
use event4u\DataHelpers\DataMapper\Support\TemplateExpression\FilterRegistry;
FilterRegistry::register('slugify', function($value) {
return strtolower(preg_replace('/[^a-z0-9]+/i', '-', $value));
});
$template = [
'slug' => '{{ title | slugify }}',
];
FilterRegistry::register('truncate', function($value, $length = 50) {
return substr($value, 0, $length);
});
$template = [
'excerpt' => '{{ content | truncate:100 }}',
];

Apply filters to array elements:

$sources = [
'users' => [
['name' => 'john'],
['name' => 'jane'],
],
];
$template = [
'names' => '{{ users.*.name | upper }}',
];
// Result: ['names' => ['JOHN', 'JANE']]

Filter array elements:

$template = [
'result' => [
'WHERE' => [
'{{ items.*.price }}' => ['>', 100],
],
'*' => [
'name' => '{{ items.*.name }}',
'price' => '{{ items.*.price }}',
],
],
];

Sort array elements:

$template = [
'result' => [
'ORDER BY' => [
'{{ items.*.price }}' => 'DESC',
],
'*' => [
'name' => '{{ items.*.name }}',
'price' => '{{ items.*.price }}',
],
],
];
$template = [
'user' => [
'name' => '{{ person.firstName | trim | ucfirst }} {{ person.lastName | trim | ucfirst }}',
'email' => '{{ person.email | trim | lower }}',
'role' => '{{ person.role ?? "guest" | upper }}',
'created' => '{{ person.createdAt | date:"Y-m-d H:i:s" }}',
],
];
$template = [
'order' => [
'customer' => [
'name' => '{{ order.customer.name | ucfirst }}',
'email' => '{{ order.customer.email | lower }}',
],
'items' => [
'*' => [
'name' => '{{ order.items.*.name | trim }}',
'price' => '{{ order.items.*.price | float }}',
],
],
],
];
$template = [
'firstName' => '{{ user.firstName | ucfirst }}',
'lastName' => '{{ user.lastName | ucfirst }}',
'fullName' => '{{ @firstName }} {{ @lastName }}',
'greeting' => 'Hello, {{ @fullName }}!',
];
// ✅ Good
'{{ name | trim | ucfirst }}'
// ❌ Bad - Use callback instead
'{{ name }}' // Then transform in PHP
// ✅ Good
'{{ user.role ?? "guest" }}'
// ❌ Bad
'{{ user.role }}' // May be null
// ✅ Good - Logical order
'{{ name | trim | lower | ucfirst }}'
// ❌ Bad - Illogical order
'{{ name | ucfirst | lower | trim }}'