Skip to content

Reverse Mapping

Reverse mapping enables bidirectional data transformation using a single template definition with the Fluent API.

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

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']]

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']]

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 data
  • getSource() - Get the original source data
  • getTemplate() - Get the template used

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();
$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();
$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();
$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();
$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();
$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();
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();

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 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();
// ✅ Good - Reversible
$template = [
'target' => [
'name' => '{{ source.name }}',
'email' => '{{ source.email }}',
],
];
// ❌ Bad - Not reversible (concatenation)
$template = [
'target' => [
'fullName' => '{{ source.firstName }} {{ source.lastName }}',
],
];
$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();
// Verify
assert($originalData === $reverse);
// ⚠️ WARNING: This mapping is not reversible
// Forward only - use for read operations
$template = [
'display' => [
'fullName' => '{{ user.firstName }} {{ user.lastName }}',
],
];