Dot Path Syntax
Learn how to use dot-path notation and wildcards to access nested data.
Introduction
Section titled “Introduction”All helpers (DataAccessor, DataMutator, DataMapper) use the same dot-path syntax and wildcard semantics.
Dot-path notation allows you to access deeply nested data structures using a simple string syntax:
// Access nested data$accessor->get('user.profile.name');
// Access array elements$accessor->get('users.0.email');
// Use wildcards$accessor->get('users.*.email');Segments
Section titled “Segments”Basic Syntax
Section titled “Basic Syntax”Segments are separated by . (dot):
// Simple path'user.profile.name' // Accesses $data['user']['profile']['name']
// Numeric index'users.0.email' // Accesses $data['users'][0]['email']
// Multiple levels'company.departments.0.employees.5.name'- Segments are separated by
.(dot) - Numeric segments index arrays/collections
- Empty segments are not allowed
- Invalid syntax throws
InvalidArgumentException:- Leading dot:
.a - Trailing dot:
a. - Double dots:
a..b
- Leading dot:
- Empty path
""is allowed and yields no segments
Wildcards
Section titled “Wildcards”Single Wildcard
Section titled “Single Wildcard”* matches any single segment at that position:
// Match all emails in users array'users.*.email'
// Returns:[ 'users.0.email' => 'alice@example.com', 'users.1.email' => 'bob@example.com', 'users.2.email' => 'charlie@example.com',]Deep Wildcards
Section titled “Deep Wildcards”Multiple * can appear in one path:
// Match all SKUs in all items across all orders'orders.*.items.*.sku'
// Returns:[ 'orders.0.items.0.sku' => 'WIDGET-A', 'orders.0.items.1.sku' => 'GADGET-B', 'orders.1.items.0.sku' => 'TOOL-C',]Wildcard Validation
Section titled “Wildcard Validation”// Check if path contains wildcard$accessor->containsWildcard('users.*.email'); // true$accessor->containsWildcard('users.0.email'); // false
// Throws on invalid syntax$accessor->containsWildcard('users..*.email'); // InvalidArgumentExceptionDataAccessor with Wildcards
Section titled “DataAccessor with Wildcards”Return Format
Section titled “Return Format”DataAccessor returns an associative array keyed by the resolved dot-path for each match:
$data = [ 'users' => [ ['email' => 'a@example.com'], ['email' => null], ['email' => 'b@example.com'], ],];
$accessor = new DataAccessor($data);$result = $accessor->get('users.*.email');
// Returns:[ 'users.0.email' => 'a@example.com', 'users.1.email' => null, 'users.2.email' => 'b@example.com',]DataMapper Wildcard Expansion
Section titled “DataMapper Wildcard Expansion”Basic Expansion
Section titled “Basic Expansion”When mapping from users.*.email to emails.*:
$mapper = new DataMapper($data);$mapper->map('users.*.email', 'emails.*');
// Each matched value is placed into emails.{i}Skip Null Values
Section titled “Skip Null Values”$mapper->map('users.*.email', 'emails.*', skipNull: true);
// Nulls are skippedIndex Handling
Section titled “Index Handling”Default (preserve gaps)
Section titled “Default (preserve gaps)”$mapper->map('users.*.email', 'emails.*', reindexWildcard: false);
// Preserves numeric gaps (e.g., keep 0 and 2)Reindex (compact)
Section titled “Reindex (compact)”$mapper->map('users.*.email', 'emails.*', reindexWildcard: true);
// Compacts indices to sequential array [0..n-1]Root Level Numeric Indices
Section titled “Root Level Numeric Indices”Paths like 0.name are valid and target the root-level array index:
$data = [ ['name' => 'Alice'], ['name' => 'Bob'],];
$accessor = new DataAccessor($data);$name = $accessor->get('0.name'); // 'Alice'Missing Keys
Section titled “Missing Keys”DataAccessor
Section titled “DataAccessor”Returns null if a path does not exist:
$accessor->get('user.nonexistent.key'); // nullDataMapper
Section titled “DataMapper”With skipNull=true, values that resolve to null are skipped:
$mapper->map('user.email', 'contact.email', skipNull: true);Examples
Section titled “Examples”Simple Path
Section titled “Simple Path”'user.profile.name' // Accesses $data['user']['profile']['name']Numeric Index
Section titled “Numeric Index”'users.0.email' // Accesses $data['users'][0]['email']Single Wildcard
Section titled “Single Wildcard”'users.*.email' // Matches all emails in users array// Returns: ['users.0.email' => 'a@x', 'users.1.email' => 'b@x']Deep Wildcards
Section titled “Deep Wildcards”'orders.*.items.*.sku' // Matches all SKUs in all items across all orders// Returns: ['orders.0.items.0.sku' => 'A', 'orders.0.items.1.sku' => 'B']Root-Level Index
Section titled “Root-Level Index”'0.name' // Accesses $data[0]['name'] when $data is a numeric arrayEdge Cases
Section titled “Edge Cases”Empty Path
Section titled “Empty Path”An empty path "" is allowed and yields no segments. DataAccessor returns the entire data structure:
$accessor = new DataAccessor(['a' => 1]);$result = $accessor->get(''); // ['a' => 1]Invalid Paths
Section titled “Invalid Paths”These paths throw InvalidArgumentException:
'.user.name' // Leading dot'user.name.' // Trailing dot'user..name' // Double dotsBest Practices
Section titled “Best Practices”Use Wildcards for Collections
Section titled “Use Wildcards for Collections”// ✅ Good - use wildcard$accessor->get('users.*.email');
// ❌ Bad - manual loopforeach ($data['users'] as $i => $user) { $accessor->get("users.{$i}.email");}Validate Paths
Section titled “Validate Paths”// ✅ Good - validate before useif ($accessor->containsWildcard($path)) { // Handle wildcard path}
// ❌ Bad - assume path format$accessor->get($path);See Also
Section titled “See Also”- DataAccessor - Read nested data
- DataMutator - Modify nested data
- DataMapper - Transform data