Reverse Mapping
Reverse mapping enables bidirectional data transformation using a single template definition with the Fluent API.
Introduction
Section titled “Introduction”Reverse mapping provides bidirectional data transformation:
- Forward mapping: Transform data from source to target using
->map() - Reverse mapping: Transform data from target back to source using
->reverseMap()
This is particularly useful when:
- Converting between DTOs and domain models
- Synchronizing data between different formats
- Implementing undo/redo functionality
- Building bidirectional API transformations
Basic Concepts
Section titled “Basic Concepts”How Reverse Mapping Works
Section titled “How Reverse Mapping Works”The Fluent API provides ->reverseMap() method that reverses the template direction:
use Event4u\DataHelpers\DataMapper;
$template = [ 'profile' => [ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', ],];
// Forward: user -> profile$userData = ['user' => ['name' => 'John', 'email' => 'john@example.com']];$result = DataMapper::from($userData) ->template($template) ->map() ->getTarget();// Result: ['profile' => ['name' => 'John', 'email' => 'john@example.com']]
// Reverse: profile -> user (using the SAME template!)$profileData = ['profile' => ['name' => 'Jane', 'email' => 'jane@example.com']];$result = DataMapper::from($profileData) ->template($template) ->reverseMap() ->getTarget();// Result: ['user' => ['name' => 'Jane', 'email' => 'jane@example.com']]Template Reversal
Section titled “Template Reversal”Templates are reversed automatically when using ->reverseMap():
$template = [ 'profile' => [ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', ],];
// Forward: user -> profile$userData = ['user' => ['name' => 'John', 'email' => 'john@example.com']];$profile = DataMapper::from($userData) ->template($template) ->map() ->getTarget();// Result: ['profile' => ['name' => 'John', 'email' => 'john@example.com']]
// Reverse: profile -> user$profileData = ['profile' => ['name' => 'Jane', 'email' => 'jane@example.com']];$user = DataMapper::from($profileData) ->template($template) ->reverseMap() ->getTarget();// Result: ['user' => ['name' => 'Jane', 'email' => 'jane@example.com']]API Reference
Section titled “API Reference”reverseMap()
Section titled “reverseMap()”Execute reverse mapping using the configured template.
$result = DataMapper::from($source) ->template($template) ->reverseMap();Returns: DataMapperResult with reversed mapping
Methods on Result:
getTarget()- Get the reversed target datagetSource()- Get the original source datagetTemplate()- Get the template used
Configuration Options
Section titled “Configuration Options”All standard DataMapper configuration options work with reverse mapping:
$result = DataMapper::from($source) ->template($template) ->skipNull(true) // Skip null values ->reindexWildcard(false) // Don't reindex arrays ->reverseMap();Use Cases
Section titled “Use Cases”DTO to Model Conversion
Section titled “DTO to Model Conversion”$template = [ 'id' => '{{ user.id }}', 'name' => '{{ user.name }}', 'email' => '{{ user.email }}',];
// Forward: Model -> DTO$userDTO = DataMapper::from(['user' => $userModel]) ->target(UserDTO::class) ->template($template) ->map() ->getTarget();
// Reverse: DTO -> Model$userModel = DataMapper::from(['user' => $userDTO]) ->target(User::class) ->template($template) ->reverseMap() ->getTarget();API Request/Response
Section titled “API Request/Response”$template = [ 'user' => [ 'firstName' => '{{ request.first_name }}', 'lastName' => '{{ request.last_name }}', 'email' => '{{ request.email }}', ],];
// Forward: API request -> Internal format$internal = DataMapper::from(['request' => $apiRequest]) ->template($template) ->map() ->getTarget();
// Reverse: Internal format -> API response$apiResponse = DataMapper::from($internal) ->template($template) ->reverseMap() ->getTarget();Data Synchronization
Section titled “Data Synchronization”$template = [ 'external' => [ 'user_id' => '{{ internal.userId }}', 'user_name' => '{{ internal.userName }}', ],];
// Sync to external system$externalData = DataMapper::from(['internal' => $internalData]) ->template($template) ->map() ->getTarget();
// Sync back from external system$internalData = DataMapper::from($externalData) ->template($template) ->reverseMap() ->getTarget();Advanced Examples
Section titled “Advanced Examples”With Nested Data
Section titled “With Nested Data”$template = [ 'profile' => [ 'personal' => [ 'name' => '{{ user.name }}', 'age' => '{{ user.age }}', ], 'contact' => [ 'email' => '{{ user.email }}', 'phone' => '{{ user.phone }}', ], ],];
// Forward$profile = DataMapper::from(['user' => $userData]) ->template($template) ->map() ->getTarget();
// Reverse$userData = DataMapper::from($profile) ->template($template) ->reverseMap() ->getTarget();With Wildcards
Section titled “With Wildcards”$template = [ 'products' => [ '*' => [ 'name' => '{{ items.*.title }}', 'price' => '{{ items.*.cost }}', ], ],];
// Forward$products = DataMapper::from(['items' => $itemsData]) ->template($template) ->map() ->getTarget();
// Reverse$items = DataMapper::from($products) ->template($template) ->reverseMap() ->getTarget();With Pipeline Filters
Section titled “With Pipeline Filters”use Event4u\DataHelpers\DataMapper\Pipeline\Filters\TrimStrings;use Event4u\DataHelpers\DataMapper\Pipeline\Filters\UppercaseStrings;
$template = [ 'user' => [ 'name' => '{{ person.name }}', 'email' => '{{ person.email }}', ],];
// Forward: Applies filters$user = DataMapper::from(['person' => $data]) ->template($template) ->pipeline([new TrimStrings(), new UppercaseStrings()]) ->map() ->getTarget();
// Reverse: Filters are NOT reversed (data flows as-is)$person = DataMapper::from($user) ->template($template) ->reverseMap() ->getTarget();Limitations
Section titled “Limitations”Non-Reversible Transformations
Section titled “Non-Reversible Transformations”Some transformations cannot be reversed due to information loss:
// ❌ Cannot reverse - Information loss$template = [ 'initials' => '{{ user.firstName }}{{ user.lastName }}',];
// Forward: 'John' + 'Doe' -> 'JohnDoe'// Reverse: 'JohnDoe' -> Cannot split back to 'John' and 'Doe'Pipeline Filters
Section titled “Pipeline Filters”Pipeline filters are NOT automatically reversed:
use Event4u\DataHelpers\DataMapper\Pipeline\Filters\UppercaseStrings;
$template = [ 'name' => '{{ user.name }}',];
// Forward: 'john' -> 'JOHN' (with UppercaseStrings filter)$result = DataMapper::from(['user' => ['name' => 'john']]) ->template($template) ->pipeline([new UppercaseStrings()]) ->map() ->getTarget();
// Reverse: 'JOHN' -> 'JOHN' (filter NOT reversed)$reverse = DataMapper::from($result) ->template($template) ->reverseMap() ->getTarget();Best Practices
Section titled “Best Practices”1. Use Simple 1:1 Mappings
Section titled “1. Use Simple 1:1 Mappings”// ✅ Good - Reversible$template = [ 'target' => [ 'name' => '{{ source.name }}', 'email' => '{{ source.email }}', ],];
// ❌ Bad - Not reversible (concatenation)$template = [ 'target' => [ 'fullName' => '{{ source.firstName }} {{ source.lastName }}', ],];2. Test Both Directions
Section titled “2. Test Both Directions”$template = [ 'profile' => [ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', ],];
$originalData = ['user' => ['name' => 'John', 'email' => 'john@example.com']];
// Test forward$forward = DataMapper::from($originalData) ->template($template) ->map() ->getTarget();
// Test reverse$reverse = DataMapper::from($forward) ->template($template) ->reverseMap() ->getTarget();
// Verifyassert($originalData === $reverse);3. Document Non-Reversible Mappings
Section titled “3. Document Non-Reversible Mappings”// ⚠️ WARNING: This mapping is not reversible// Forward only - use for read operations$template = [ 'display' => [ 'fullName' => '{{ user.firstName }} {{ user.lastName }}', ],];See Also
Section titled “See Also”- DataMapper - DataMapper guide
- Template Expressions - Template syntax
- Pipelines - Pipeline processing