uuur86 / dalue
Map nested arrays, objects and JSON documents into a new structure with a declarative schema.
Requires
- php: >=7.2
- ext-json: *
- ext-mbstring: *
- uuur86/strobj: ^2.1.7
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.16 || ^3.0
- phpstan/phpstan: ^0.12.25
- phpstan/phpstan-phpunit: ^0.12.8
- phpstan/phpstan-strict-rules: ^0.12.3
- phpunit/phpunit: ^8.5
- squizlabs/php_codesniffer: ^3.5
- symfony/console: ^4.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-05 03:07:02 UTC
README
The Dalue library provides a powerful, recursive, and memory-efficient method for data mapping and structural transformation in PHP. It allows you to ingest complex, unstructured data sets (like API responses or raw JSON payloads) and map them directly into strict domain models, NoSQL item structures, or specific array formats while optionally mutating the values during the allocation phase.
It eliminates the need for repetitive array traversal, nested loops, and deep conditional type casting in your application layer.
Requirements
- PHP 7.4 or higher (tested on PHP 7.4 – 8.5).
- uuur86/strobj 3.x (installed automatically by Composer).
Full documentation is available in the wiki.
Installation
You can install the package via Composer:
composer require uuur86/dalue
How It Works
Dalue relies on a defined schema array where you specify the target structure. It supports:
- Dot/Path Notation: Using the
@data/prefix, you can fetch deeply nested values instantly. Use/*wildcard notation to extract columns across array items (e.g.,@data/items/*/sku). - Recursive Array Mapping: You can define nested arrays in your target schema.
- Collection Merging (
[]notation): Automatically resolves and mergeshas_manyrelationships into structured row arrays. - On-the-fly Mutation: Allows passing a callback function to manipulate keys and values (e.g., formatting dates, prefixing keys, casting types) during the mapping loop.
- Predictable Missing Values: Paths that do not exist in the source data return
null, or the default you pass as the fourth argument. Stored values such asfalse,0and''are returned unchanged.
Schema Reference
| Schema value | Result |
|---|---|
'@data/user/name' |
Value at user/name, or the default (null) when the path does not exist |
'@data/items/*/sku' |
List with one sku per item (null for items without one) |
['a' => ..., 'b' => ...] |
Nested schema, mapped recursively |
'rows[]' => ['a' => '@data/items/*/a', ...] |
Columns turned into rows: [['a' => ...], ['a' => ...]] |
Anything else ('text', 42, true, null, objects) |
Copied unchanged |
Mapper::map(StringObjects $data, array $schema, ?callable $callback = null, $default = null): array
Basic Usage: Transforming a Dataset
Below is a basic example of extracting specific data from a deep array. Use StringObjects::instance() to ingest array or JSON payloads.
use Dalue\Mapper; use StrObj\StringObjects; // 1. Ingest your source data $data = StringObjects::instance([ 'user' => [ 'id' => 1, 'name' => 'John Doe', 'email' => 'john.doe@example.com', 'details' => [ 'age' => 30, 'city' => 'New York' ] ] ]); // 2. Define the structural schema you want to achieve // Note the '@data/' prefix to read from the source StringObject $schema = [ 'userID' => '@data/user/id', 'userName' => '@data/user/name', 'userDetails' => [ 'userAge' => '@data/user/details/age', 'userCity' => '@data/user/details/city' ], 'mapped_at' => time() // Static values can be assigned directly ]; // 3. Execute the transformation $mappedData = Mapper::map($data, $schema); print_r($mappedData);
Output:
Array ( [userID] => 1 [userName] => John Doe [userDetails] => Array ( [userAge] => 30 [userCity] => New York ) [mapped_at] => 1723126306 )
Advanced Usage: Type Casting & Callbacks
You can perform complex transformations by providing a callback function as the third parameter. This is especially useful for formatting strings, rounding floats, or mutating specific fields during mapping.
The callback receives the target key and the mapped value for every entry, including nested arrays and collections (children first, then their parent), and its return value is stored.
use Dalue\Mapper; use StrObj\StringObjects; // ... (using the same $data from above) $schema = [ 'userID' => '@data/user/id', 'userName' => '@data/user/name', 'userEmail' => '@data/user/email', ]; $mappedData = Mapper::map( $data, $schema, function (string $key, $value) { if ($key === 'userEmail') { return strtoupper($value); } return $value; } );
Default Values for Missing Paths
$mappedData = Mapper::map($data, ['phone' => '@data/user/phone'], null, 'N/A'); // ['phone' => 'N/A']
Real-World Example: Adapting API JSON for AWS DynamoDB
NoSQL databases like AWS DynamoDB require strict type markers (e.g., S for String, N for Number, L for List, M for Map). Dalue cleanly maps complex webhook structures into clean domain models, which can then be formatted into DynamoDB AttributeValue format using AWS SDK's Marshaler.
Scenario:
We receive an e-commerce webhook payload and need to format it for a DynamoDB PutItem operation.
use Dalue\Mapper; use StrObj\StringObjects; use Aws\DynamoDb\DynamoDbClient; use Aws\DynamoDb\Marshaler; $apiPayload = [ "transaction_id" => "tx_987654321", "customer" => [ "account_no" => "CUST-1002", "full_name" => "John Doe", ], "purchase_details" => [ "total_amount" => 150.50, "items" => [ ["sku" => "ITEM-01", "qty" => 2, "price" => 50.00], ["sku" => "ITEM-02", "qty" => 1, "price" => 50.50] ] ] ]; $stringObject = StringObjects::instance($apiPayload); // The '[]' notation triggers Dalue's mergeColumns logic for collections. // The '/*' wildcard extracts array column values across all item rows. $schema = [ 'PK' => '@data/transaction_id', 'SK' => 'METADATA', 'CustomerId' => '@data/customer/account_no', 'TotalAmount' => '@data/purchase_details/total_amount', 'OrderItems[]' => [ 'SKU' => '@data/purchase_details/items/*/sku', 'Quantity' => '@data/purchase_details/items/*/qty', 'Price' => '@data/purchase_details/items/*/price' ] ]; // Step 1: Perform structural mapping and value mutations $mappedData = Mapper::map( $stringObject, $schema, function (string $key, $value) { if ($key === 'PK') { return 'ORDER#' . $value; } return $value; } ); // Step 2: Format the clean mapped array into DynamoDB AttributeValue format $marshaler = new Marshaler(); $dynamoItem = $marshaler->marshalItem($mappedData); // Result is ready to be sent to AWS DynamoDB PutItem /* $dynamoDbClient = new DynamoDbClient([ 'region' => 'us-east-1', 'version' => 'latest' ]); $dynamoDbClient->putItem([ 'TableName' => 'OrdersTable', 'Item' => $dynamoItem ]); */
Examples
Runnable examples live in examples/. Clone the repository and run:
composer install php examples/01-basic-mapping.php # one example composer examples # all examples
Each script's expected output is stored in examples/output/ and checked by the test suite. See examples/README.md or the Examples wiki page.
Development
composer install composer test # PHPUnit (includes the example output checks) composer examples # Run all examples composer phpstan # Static analysis composer cs # PSR-12 coding standard composer check # All of the above
See CHANGELOG.md for release notes and the migration guide when upgrading from earlier versions.
LICENSE
This project is licensed under the GPL-3.0-or-later License. See the LICENSE file for more details.
AUTHOR
Uğur Biçer - @uuur86
CONTRIBUTING
If you want to contribute to this project, you can send pull requests. Please read the contributing guide first.
CONTACT
You can contact me via email: contact@codeplus.dev
BUGS
You can report bugs via GitHub issues.
SECURITY
If you find a security issue, please report it privately as described in SECURITY.md.
DONATE
If you want to support me, you can donate via GitHub sponsors: https://github.com/sponsors/uuur86
SEE ALSO
- uuur86/strobj - PHP String Objects
- uuur86/wasp - WordPress Advanced Settings Page
- @codeplusdev - Codeplus Development