DataMutator
DataMutator provides methods to modify nested data structures including arrays, objects, Dtos, Laravel Collections and Eloquent Models. All operations work with references and modify the target in-place using a fluent API.
Quick Example
Section titled “Quick Example”use event4u\DataHelpers\DataMutator;
// Set values in nested structure (fluent chaining)$target = [];DataMutator::make($target) ->set('user.profile.name', 'Alice') ->set('user.profile.email', 'alice@example.com');// $target is now: ['user' => ['profile' => ['name' => 'Alice', 'email' => 'alice@example.com']]]
// Merge dataDataMutator::make($target)->merge('user.profile', ['age' => 30, 'city' => 'Berlin']);// $target is now: ['user' => ['profile' => ['name' => 'Alice', 'email' => 'alice@example.com', 'age' => 30, 'city' => 'Berlin']]]
// Update multiple items with wildcard$data = ['users' => [['active' => false], ['active' => false]]];DataMutator::make($data)->set('users.*.active', true);// $data is now: ['users' => [['active' => true], ['active' => true]]]
// Remove values$data = ['users' => [['name' => 'Alice', 'password' => 'secret'], ['name' => 'Bob', 'password' => 'secret2']]];DataMutator::make($data)->unset('users.*.password');// $data is now: ['users' => [['name' => 'Alice'], ['name' => 'Bob']]]Introduction
Section titled “Introduction”DataMutator provides three main operations:
set($path, $value)- Set a value at a dot-pathmerge($path, $data)- Merge data into an existing branchunset($path)- Remove a value at a path
All operations are chainable and modify the target in-place.
Supported Data Types
Section titled “Supported Data Types”DataMutator works with:
- Arrays - Nested arrays with any depth
- Objects - Plain PHP objects with public properties
- Dtos - Data Transfer Objects
- Laravel Collections -
Illuminate\Support\Collection - Eloquent Models - Including relationships
Key Features
Section titled “Key Features”- Fluent API - Chainable methods for clean code
- Reference mutations - Modifies target in-place for performance
- Wildcard support - Modify multiple items at once
- Deep wildcards - Multiple wildcards in one path
- Type preservation - Maintains data types during operations
set() - Setting Values
Section titled “set() - Setting Values”Set a value at a dot-notation path.
Basic Usage
Section titled “Basic Usage”use event4u\DataHelpers\DataMutator;
// Start with empty array$target = [];
// Set nested value (fluent chaining)DataMutator::make($target) ->set('user.profile.name', 'Alice') ->set('user.profile.email', 'alice@example.com') ->set('user.settings.theme', 'dark');
// $target is now: [// 'user' => [// 'profile' => ['name' => 'Alice', 'email' => 'alice@example.com'],// 'settings' => ['theme' => 'dark']// ]// ]Overwriting Values
Section titled “Overwriting Values”$target = ['user' => ['name' => 'Alice']];
// Overwrite existing valueDataMutator::make($target)->set('user.name', 'Bob');// $target = ['user' => ['name' => 'Bob']]Numeric Indices
Section titled “Numeric Indices”$target = [];
// Set values at specific indicesDataMutator::make($target)->set('users.0.name', 'Alice');DataMutator::make($target)->set('users.1.name', 'Bob');// $target = ['users' => [['name' => 'Alice'], ['name' => 'Bob']]]Wildcards in set()
Section titled “Wildcards in set()”Use wildcards to set multiple values at once.
// Set same value for all items$data = ['users' => [['active' => false], ['active' => false], ['active' => false]]];DataMutator::make($data)->set('users.*.active', true);// $data = ['users' => [['active' => true], ['active' => true], ['active' => true]]]Wildcard with Accessor Results
Section titled “Wildcard with Accessor Results”When the source value comes from DataAccessor (with full path keys), wildcards expand automatically:
use event4u\DataHelpers\DataAccessor;
$source = ['users' => [ ['email' => 'alice@example.com'], ['email' => 'bob@example.com'],]];
$accessor = new DataAccessor($source);$emails = $accessor->get('users.*.email');// $emails = ['users.0.email' => 'alice@example.com', 'users.1.email' => 'bob@example.com']
// Write to new structure with wildcard$target = [];DataMutator::make($target)->set('contacts.*.email', $emails);// Result: ['contacts' => [// 0 => ['email' => 'alice@example.com'],// 1 => ['email' => 'bob@example.com']// ]]Deep Wildcards
Section titled “Deep Wildcards”Multiple wildcards are supported:
$data = [ 'departments' => [ ['users' => [['active' => false], ['active' => false]]], ['users' => [['active' => false]]], ],];
DataMutator::make($data)->set('departments.*.users.*.active', true);// $data = All 'active' fields set to true across all departments and usersmerge() - Merging Data
Section titled “merge() - Merging Data”Merge data into an existing branch at a path.
Basic Usage
Section titled “Basic Usage”$target = ['config' => ['limits' => ['cpu' => 1]]];
// Merge additional dataDataMutator::make($target)->merge('config.limits', ['memory' => 512, 'disk' => 1024]);// $target = ['config' => ['limits' => ['cpu' => 1, 'memory' => 512, 'disk' => 1024]]]Merge Strategy
Section titled “Merge Strategy”DataMutator uses different merge strategies depending on array type:
Associative Arrays
Section titled “Associative Arrays”Associative arrays are deep-merged recursively:
$target = [ 'config' => [ 'database' => ['host' => 'localhost', 'port' => 3306], 'cache' => ['driver' => 'redis'], ],];
DataMutator::make($target)->merge('config', [ 'database' => ['charset' => 'utf8mb4'], 'queue' => ['driver' => 'sync'],]);
// $target = [// 'config' => [// 'database' => ['host' => 'localhost', 'port' => 3306, 'charset' => 'utf8mb4'],// 'cache' => ['driver' => 'redis'],// 'queue' => ['driver' => 'sync']// ]// ]Numeric-Indexed Arrays
Section titled “Numeric-Indexed Arrays”Numeric-indexed arrays use index-based replacement (not append) for deterministic mapping:
$target = ['list' => [10 => 'x', 11 => 'y']];
DataMutator::make($target)->merge('list', [11 => 'Y', 12 => 'Z']);// $target = ['list' => [10 => 'x', 11 => 'Y', 12 => 'Z']]Merge Configuration
Section titled “Merge Configuration”$config = ['database' => ['host' => 'localhost']];
// Merge additional settingsDataMutator::make($config)->merge('database', [ 'port' => 3306, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci',]);
// $config = [// 'database' => [// 'host' => 'localhost',// 'port' => 3306,// 'charset' => 'utf8mb4',// 'collation' => 'utf8mb4_unicode_ci'// ]// ]Merge with Wildcards
Section titled “Merge with Wildcards”$data = [ 'users' => [ ['name' => 'Alice', 'role' => 'user'], ['name' => 'Bob', 'role' => 'user'], ],];
// Merge additional data for all usersDataMutator::make($data)->merge('users.*', ['active' => true, 'verified' => true]);
// $data = [// 'users' => [// ['name' => 'Alice', 'role' => 'user', 'active' => true, 'verified' => true],// ['name' => 'Bob', 'role' => 'user', 'active' => true, 'verified' => true]// ]// ]unset() - Removing Values
Section titled “unset() - Removing Values”Remove values at a path. Supports wildcards for bulk removal.
Basic Usage
Section titled “Basic Usage”$target = [ 'user' => [ 'name' => 'Alice', 'email' => 'alice@example.com', 'password' => 'secret', ],];
// Remove passwordDataMutator::make($target)->unset('user.password');// $target = ['user' => ['name' => 'Alice', 'email' => 'alice@example.com']]Remove with Wildcards
Section titled “Remove with Wildcards”$data = [ 'users' => [ ['name' => 'Alice', 'password' => 'secret1'], ['name' => 'Bob', 'password' => 'secret2'], ['name' => 'Charlie', 'password' => 'secret3'], ],];
// Remove all passwordsDataMutator::make($data)->unset('users.*.password');// $data = ['users' => [// ['name' => 'Alice'],// ['name' => 'Bob'],// ['name' => 'Charlie']// ]]Deep Wildcard Removal
Section titled “Deep Wildcard Removal”$data = [ 'orders' => [ [ 'items' => [ ['temp_id' => 'x', 'sku' => 'A', 'price' => 10], ['temp_id' => 'y', 'sku' => 'B', 'price' => 20], ], ], [ 'items' => [ ['temp_id' => 'z', 'sku' => 'C', 'price' => 30], ], ], ],];
// Remove all temp_id fieldsDataMutator::make($data)->unset('orders.*.items.*.temp_id');// Result: All temp_id fields removed from nested itemsRemove Sensitive Data
Section titled “Remove Sensitive Data”$response = [ 'users' => [ ['id' => 1, 'name' => 'Alice', 'password' => 'hash1', 'api_key' => 'key1'], ['id' => 2, 'name' => 'Bob', 'password' => 'hash2', 'api_key' => 'key2'], ],];
// Remove sensitive fieldsDataMutator::make($response)->unset('users.*.password');DataMutator::make($response)->unset('users.*.api_key');// $response = Only id and name remain for each userWorking with Dtos and Objects
Section titled “Working with Dtos and Objects”DataMutator works with plain PHP objects and Dtos.
Basic Object Mutation
Section titled “Basic Object Mutation”$dto = new #[\AllowDynamicProperties] class { public string $name = ''; public array $tags = [];};
// Set propertyDataMutator::make($dto)->set('name', 'Alice');
// Merge array propertyDataMutator::make($dto)->merge('tags', ['php', 'laravel']);
// Result: Object with name='Alice' and tags=['php', 'laravel']Nested Object Properties
Section titled “Nested Object Properties”class Profile { public string $bio = ''; public string $website = '';}
class User { public string $name = ''; public Profile $profile;
public function __construct() { $this->profile = new Profile(); }}
$user = new User();DataMutator::make($user)->set('name', 'Alice');DataMutator::make($user)->set('profile.bio', 'Software Engineer');DataMutator::make($user)->set('profile.website', 'https://example.com');Working with Dtos
Section titled “Working with Dtos”$dto = new UserWithRolesDto();DataMutator::make($dto)->set('name', 'Alice');DataMutator::make($dto)->set('email', 'alice@example.com');DataMutator::make($dto)->merge('roles', ['admin', 'editor']);Working with Eloquent Models
Section titled “Working with Eloquent Models”DataMutator works with Eloquent Models and their relationships.
Basic Model Mutation
Section titled “Basic Model Mutation”$user = User::first();
// Update model attributesDataMutator::make($user)->set('name', 'Alice Updated');DataMutator::make($user)->set('email', 'alice.updated@example.com');
// Note: Changes are not automatically saved to database$user->save();Updating Relationships
Section titled “Updating Relationships”$user = User::with('posts')->first();
// Update first post's titleDataMutator::make($user)->set('posts.0.title', 'Updated Title');
// Update all posts' statusDataMutator::make($user)->set('posts.*.published', true);
// Save changes$user->push(); // Saves model and relationshipsNested Relationships
Section titled “Nested Relationships”$user = User::with('posts.comments')->first();
// Update nested relationshipDataMutator::make($user)->set('posts.0.comments.0.text', 'Updated comment');
// Remove sensitive data from all commentsDataMutator::make($user)->unset('posts.*.comments.*.author_ip');Working with Collections
Section titled “Working with Collections”DataMutator works with Laravel Collections.
Collection Mutation
Section titled “Collection Mutation”use Illuminate\Support\Collection;
$data = [ 'users' => collect([ ['name' => 'Alice', 'active' => false], ['name' => 'Bob', 'active' => false], ]),];
// Update all usersDataMutator::make($data)->set('users.*.active', true);Nested Collections
Section titled “Nested Collections”$data = [ 'orders' => collect([ [ 'items' => collect([ ['sku' => 'A', 'qty' => 1], ['sku' => 'B', 'qty' => 2], ]), ], ]),];
// Update nested collection itemsDataMutator::make($data)->set('orders.0.items.*.qty', 5);Common Patterns
Section titled “Common Patterns”Build Nested Structure from Flat Data
Section titled “Build Nested Structure from Flat Data”$target = [];
// Build structure step by stepDataMutator::make($target)->set('user.profile.name', 'Alice');DataMutator::make($target)->set('user.profile.email', 'alice@example.com');DataMutator::make($target)->set('user.profile.age', 30);DataMutator::make($target)->set('user.settings.theme', 'dark');DataMutator::make($target)->set('user.settings.language', 'en');
// $target = [// 'user' => [// 'profile' => ['name' => 'Alice', 'email' => 'alice@example.com', 'age' => 30],// 'settings' => ['theme' => 'dark', 'language' => 'en']// ]// ]Bulk Update with Accessor Results
Section titled “Bulk Update with Accessor Results”use event4u\DataHelpers\DataAccessor;
// Read from source$source = ['users' => [ ['email' => 'alice@example.com', 'verified' => false], ['email' => 'bob@example.com', 'verified' => false],]];
$accessor = new DataAccessor($source);$emails = $accessor->get('users.*.email');
// Write to target$target = [];DataMutator::make($target)->set('contacts.*.email', $emails);DataMutator::make($target)->set('contacts.*.verified', true);Configuration Management
Section titled “Configuration Management”// Start with base config$config = [ 'app' => ['name' => 'MyApp', 'env' => 'production'], 'database' => ['host' => 'localhost'],];
// Merge environment-specific configDataMutator::make($config)->merge('database', [ 'port' => 3306, 'charset' => 'utf8mb4', 'prefix' => 'app_',]);
// Add cache configDataMutator::make($config)->merge('cache', [ 'driver' => 'redis', 'prefix' => 'myapp',]);Data Sanitization
Section titled “Data Sanitization”$data = [ 'users' => [ ['id' => 1, 'name' => 'Alice', 'password' => 'hash1', 'api_key' => 'key1', 'internal_notes' => 'notes1'], ['id' => 2, 'name' => 'Bob', 'password' => 'hash2', 'api_key' => 'key2', 'internal_notes' => 'notes2'], ],];
// Remove all sensitive fieldsDataMutator::make($data)->unset('users.*.password');DataMutator::make($data)->unset('users.*.api_key');DataMutator::make($data)->unset('users.*.internal_notes');
// $data = Only id and name remainBest Practices
Section titled “Best Practices”Use Fluent API
Section titled “Use Fluent API”DataMutator uses a fluent API with make() factory method:
// ✅ Correct - fluent API$data = [];DataMutator::make($data)->set('user.name', 'Alice');
// ✅ Also correct - chainingDataMutator::make($data) ->set('user.name', 'Alice') ->set('user.email', 'alice@example.com');Use Wildcards for Bulk Operations
Section titled “Use Wildcards for Bulk Operations”Instead of looping, use wildcards for better performance:
// ❌ Inefficientforeach ($data['users'] as $key => $user) { $data['users'][$key]['active'] = true;}
// ✅ EfficientDataMutator::make($data)->set('users.*.active', true);Combine with DataAccessor
Section titled “Combine with DataAccessor”Use DataAccessor to read and DataMutator to write:
use event4u\DataHelpers\DataAccessor;
// Read from source$source = ['users' => [['email' => 'alice@example.com'], ['email' => 'bob@example.com']]];$accessor = new DataAccessor($source);$emails = $accessor->get('users.*.email');
// Write to target$target = [];DataMutator::make($target)->set('contacts.*.email', $emails);Use DataMapper for Complex Transformations
Section titled “Use DataMapper for Complex Transformations”For complex data transformations, use DataMapper instead of multiple DataMutator calls:
// ❌ Multiple mutations$target = [];DataMutator::make($target)->set('name', $source['user']['name']);DataMutator::make($target)->set('email', $source['user']['email']);DataMutator::make($target)->set('age', $source['user']['profile']['age']);
// ✅ Use DataMapper$target = DataMapper::source($source) ->template([ 'name' => '{{ user.name }}', 'email' => '{{ user.email }}', 'age' => '{{ user.profile.age }}', ]) ->map() ->getTarget();Chain Operations
Section titled “Chain Operations”Chain multiple operations for cleaner code:
$config = [];DataMutator::make($config)->set('app.name', 'MyApp');DataMutator::make($config)->set('app.env', 'production');DataMutator::make($config)->merge('database', ['host' => 'localhost', 'port' => 3306]);DataMutator::make($config)->merge('cache', ['driver' => 'redis']);Performance Notes
Section titled “Performance Notes”Wildcard Performance
Section titled “Wildcard Performance”- Wildcards traverse all matching elements
- Performance scales with the number of matches
- For large datasets, consider filtering data first
// ❌ Slow on large datasetsDataMutator::make($hugeDataset)->set('users.*.active', true);
// ✅ Filter first$activeUsers = array_filter($hugeDataset['users'], fn($u) => $u['status'] === 'pending');$data = ['users' => $activeUsers];DataMutator::make($data)->set('users.*.active', true);Deep Wildcards
Section titled “Deep Wildcards”Multiple wildcards can be expensive on large nested structures:
// Can be slow on large datasetsDataMutator::make($data)->set('departments.*.teams.*.users.*.active', true);
// Consider limiting depth or using DataMapperBatch Operations
Section titled “Batch Operations”For very large datasets, consider batching operations:
// Process in chunks$chunks = array_chunk($data['users'], 1000);foreach ($chunks as $chunk) { $chunkData = ['users' => $chunk]; DataMutator::make($chunkData)->set('users.*.active', true); // Process chunk}Reference Mutations
Section titled “Reference Mutations”DataMutator works with references and modifies the target in-place:
$data = ['user' => ['name' => 'Alice']];DataMutator::make($data)->set('user.name', 'Bob');
// $data is now modified// ['user' => ['name' => 'Bob']]
// To preserve the original, clone it first$original = ['user' => ['name' => 'Alice']];$copy = $original;DataMutator::make($copy)->set('user.name', 'Bob');// $original is unchanged, $copy is modifiedThis ensures:
- Predictability - Original data is never modified
- Safety - No unexpected side effects
- Testability - Easy to test and debug
- Functional style - Supports functional programming patterns
Code Examples
Section titled “Code Examples”The following working examples demonstrate DataMutator in action:
- Basic Usage - Complete example showing set, merge and unset operations with wildcards
All examples are fully tested and can be run directly:
php examples/main-classes/data-mutator/basic-usage.phpRelated Tests
Section titled “Related Tests”The functionality is thoroughly tested. Key test files:
- DataMutatorTest.php - Core functionality tests
- DataMutatorDoctrineTest.php - Doctrine integration tests
- DataMutatorLaravelTest.php - Laravel integration tests
- DataMutatorIntegrationTest.php - End-to-end scenarios
Run the tests:
# Run all DataMutator teststask test:unit -- --filter=DataMutator
# Run specific test filevendor/bin/pest tests/Unit/DataMutator/DataMutatorTest.phpSee Also
Section titled “See Also”- DataAccessor - Read nested data
- DataMapper - Transform data structures
- DataFilter - Query and filter data
- Core Concepts: Dot-Notation - Path syntax
- Core Concepts: Wildcards - Wildcard operators
- Examples - 90+ code examples