Template Expressions
Powerful template expression engine for declarative data transformations - inspired by Twig, but designed specifically for data mapping.
Introduction
Section titled “Introduction”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(), andmapFromTemplate() - 🔄 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
Quick Start
Section titled “Quick Start”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',// ]// ]Expression Syntax
Section titled “Expression Syntax”Simple Variables
Section titled “Simple Variables”Access source data using dot-notation paths wrapped in {{ }}:
$template = [ 'name' => '{{ user.name }}', 'email' => '{{ user.contact.email }}',];Default Values
Section titled “Default Values”Provide fallback values for null/missing data using ??:
$template = [ 'name' => '{{ user.name ?? "Unknown" }}', 'age' => '{{ user.age ?? 18 }}', 'role' => '{{ user.role ?? "guest" }}',];Filters
Section titled “Filters”Transform values using the pipe | operator:
$template = [ 'name' => '{{ user.name | upper }}', 'email' => '{{ user.email | lower }}', 'title' => '{{ post.title | trim }}',];Chaining Filters
Section titled “Chaining Filters”Chain multiple filters together:
$template = [ 'name' => '{{ user.name | trim | lower | ucfirst }}', 'slug' => '{{ post.title | trim | lower | replace:" ":"-" }}',];Alias References
Section titled “Alias References”Reference target fields using @ prefix:
$template = [ 'firstName' => '{{ user.firstName }}', 'lastName' => '{{ user.lastName }}', 'fullName' => '{{ @firstName }} {{ @lastName }}',];Built-in Filters
Section titled “Built-in Filters”String Filters
Section titled “String Filters”// 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'Array Filters
Section titled “Array Filters”// 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'Type Filters
Section titled “Type Filters”// 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 Filters
Section titled “Date Filters”// date - Format date'{{ created | date:"Y-m-d" }}' // DateTime -> '2024-01-15'
// timestamp - Convert to timestamp'{{ created | timestamp }}' // DateTime -> 1705276800Custom Filters
Section titled “Custom Filters”Register Custom Filter
Section titled “Register Custom Filter”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 }}',];Filter with Parameters
Section titled “Filter with Parameters”FilterRegistry::register('truncate', function($value, $length = 50) { return substr($value, 0, $length);});
$template = [ 'excerpt' => '{{ content | truncate:100 }}',];Wildcard Support
Section titled “Wildcard Support”Apply filters to array elements:
$sources = [ 'users' => [ ['name' => 'john'], ['name' => 'jane'], ],];
$template = [ 'names' => '{{ users.*.name | upper }}',];
// Result: ['names' => ['JOHN', 'JANE']]WHERE and ORDER BY Clauses
Section titled “WHERE and ORDER BY Clauses”WHERE Clauses
Section titled “WHERE Clauses”Filter array elements:
$template = [ 'result' => [ 'WHERE' => [ '{{ items.*.price }}' => ['>', 100], ], '*' => [ 'name' => '{{ items.*.name }}', 'price' => '{{ items.*.price }}', ], ],];ORDER BY Clauses
Section titled “ORDER BY Clauses”Sort array elements:
$template = [ 'result' => [ 'ORDER BY' => [ '{{ items.*.price }}' => 'DESC', ], '*' => [ 'name' => '{{ items.*.name }}', 'price' => '{{ items.*.price }}', ], ],];Advanced Examples
Section titled “Advanced Examples”Complex Transformation
Section titled “Complex Transformation”$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" }}', ],];Nested Data
Section titled “Nested Data”$template = [ 'order' => [ 'customer' => [ 'name' => '{{ order.customer.name | ucfirst }}', 'email' => '{{ order.customer.email | lower }}', ], 'items' => [ '*' => [ 'name' => '{{ order.items.*.name | trim }}', 'price' => '{{ order.items.*.price | float }}', ], ], ],];With Aliases
Section titled “With Aliases”$template = [ 'firstName' => '{{ user.firstName | ucfirst }}', 'lastName' => '{{ user.lastName | ucfirst }}', 'fullName' => '{{ @firstName }} {{ @lastName }}', 'greeting' => 'Hello, {{ @fullName }}!',];Best Practices
Section titled “Best Practices”1. Use Filters for Transformations
Section titled “1. Use Filters for Transformations”// ✅ Good'{{ name | trim | ucfirst }}'
// ❌ Bad - Use callback instead'{{ name }}' // Then transform in PHP2. Provide Defaults
Section titled “2. Provide Defaults”// ✅ Good'{{ user.role ?? "guest" }}'
// ❌ Bad'{{ user.role }}' // May be null3. Chain Filters Logically
Section titled “3. Chain Filters Logically”// ✅ Good - Logical order'{{ name | trim | lower | ucfirst }}'
// ❌ Bad - Illogical order'{{ name | ucfirst | lower | trim }}'See Also
Section titled “See Also”- DataMapper - DataMapper guide
- Callback Filters - Custom callbacks
- Query Builder - Query builder