elaitech/data-mapper

Elaitech DataMapper - A reusable data mapping and transformation component for Laravel.

Installs: 3

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/elaitech/data-mapper

0.0.1 2026-02-15 00:54 UTC

This package is auto-updated.

Last update: 2026-02-16 02:06:12 UTC


README

A reusable data mapping and transformation library for Laravel 12. Maps source fields to target fields with chained value transformers, dot-notation and wildcard field extraction, value mapping lookups, and full support for both associative and indexed (header-based) data rows.

Namespace: Elaitechx\DataMapper
Requires: PHP 8.4+ ยท Laravel 12 ยท spatie/laravel-data ^4.19

๐Ÿ“– Table of Contents

๐Ÿš€ Installation

As a local Composer package

In your root composer.json, add the package as a path repository:

{
    "repositories": [
        {
            "type": "path",
            "url": "./packages/data-mapper",
            "options": { "symlink": true }
        }
    ],
    "require": {
        "elaitech/data-mapper": "@dev"
    }
}

Then install:

composer update elaitech/data-mapper

The DataMapperServiceProvider is auto-discovered by Laravel. It registers:

  • DataMapperInterface โ†’ DataMapperService (binding)
  • ValueTransformer โ€” singleton with 10 built-in transformers
  • FieldExtractor โ€” singleton

๐Ÿ— Architecture

src/
โ”œโ”€โ”€ DataMapperService.php          # Main entry point โ€” maps data rows using rules
โ”œโ”€โ”€ DataMapperServiceProvider.php  # Laravel auto-discovery provider
โ”œโ”€โ”€ FieldExtractor.php             # Dot-notation & wildcard field extraction
โ”œโ”€โ”€ ValueTransformer.php           # Transformer registry & value transformation engine
โ”‚
โ”œโ”€โ”€ Contracts/
โ”‚   โ”œโ”€โ”€ DataMapperInterface.php    # Main service contract
โ”‚   โ””โ”€โ”€ TransformerInterface.php   # Interface for all transformers
โ”‚
โ”œโ”€โ”€ DTO/
โ”‚   โ”œโ”€โ”€ MappingConfigurationData.php  # Input: data + rules + headers
โ”‚   โ”œโ”€โ”€ MappingRuleData.php           # Single mapping rule definition
โ”‚   โ””โ”€โ”€ DataMappingResultData.php     # Output: mapped data + errors
โ”‚
โ””โ”€โ”€ Transformers/                  # 10 built-in transformers
    โ”œโ”€โ”€ NoneTransformer.php
    โ”œโ”€โ”€ TrimTransformer.php
    โ”œโ”€โ”€ UpperTransformer.php
    โ”œโ”€โ”€ LowerTransformer.php
    โ”œโ”€โ”€ IntegerTransformer.php
    โ”œโ”€โ”€ FloatTransformer.php
    โ”œโ”€โ”€ BooleanTransformer.php
    โ”œโ”€โ”€ DateTransformer.php
    โ”œโ”€โ”€ ArrayFirstTransformer.php
    โ””โ”€โ”€ ArrayJoinTransformer.php

โšก Quick Start

use Elaitech\DataMapper\Contracts\DataMapperInterface;
use Elaitech\DataMapper\DTO\MappingConfigurationData;
use Elaitech\DataMapper\DTO\MappingRuleData;
use Spatie\LaravelData\DataCollection;

$mapper = app(DataMapperInterface::class);

$config = new MappingConfigurationData(
    data: [
        ['name' => 'John Doe', 'email' => 'john@example.com', 'age' => '30'],
        ['name' => 'Jane Smith', 'email' => 'jane@example.com', 'age' => '25'],
    ],
    mappingRules: MappingRuleData::collection([
        new MappingRuleData(
            sourceField: 'name',
            targetField: 'full_name',
            transformation: 'trim',
        ),
        new MappingRuleData(
            sourceField: 'email',
            targetField: 'contact_email',
            transformation: 'lower',
        ),
        new MappingRuleData(
            sourceField: 'age',
            targetField: 'user_age',
            transformation: 'integer',
        ),
    ]),
);

$result = $mapper->map($config);

// $result->data = [
//     ['full_name' => 'John Doe', 'contact_email' => 'john@example.com', 'user_age' => 30],
//     ['full_name' => 'Jane Smith', 'contact_email' => 'jane@example.com', 'user_age' => 25],
// ]
// $result->errors = []

๐Ÿงฉ Core Components

DataMapperService

The main service class. Implements DataMapperInterface.

public function map(MappingConfigurationData $config): DataMappingResultData

Behaviour:

  • Automatically detects whether rows are associative (['name' => 'John']) or indexed (['John', 'john@example.com'])
  • For indexed rows, uses the headers array to resolve field positions
  • Wraps each row in a try/catch โ€” failed rows are captured as errors, not exceptions
  • Returns DataMappingResultData with mapped data and any per-row errors

FieldExtractor

Extracts values from data using:

Pattern Example Description
Direct access name Top-level field
Dot notation address.city Nested field traversal
Wildcard items.*.name Extract from all array elements
$extractor = app(FieldExtractor::class);

$data = [
    'user' => ['profile' => ['name' => 'John']],
    'items' => [
        ['name' => 'A', 'price' => 10],
        ['name' => 'B', 'price' => 20],
    ],
];

$extractor->extractValue($data, 'user.profile.name');  // 'John'
$extractor->extractArrayValues($data, 'items.*.name'); // ['A', 'B']
$extractor->hasField($data, 'user.profile.name');       // true

ValueTransformer

The transformer registry and execution engine. Manages all registered transformers and applies transformation chains.

$transformer = app(ValueTransformer::class);

// Check available transformers
$transformer->getTransformerOptions(); // ['none' => 'None', 'trim' => 'Trim', ...]

// Register a custom transformer
$transformer->registerTransformer(new MyCustomTransformer());

Transformation flow:

  1. Check if value is empty โ†’ return defaultValue (unless it's an array transformer)
  2. Apply value mapping if configured (lookup table)
  3. Apply transformer (type conversion, formatting)
  4. If result is empty string and defaultValue is set โ†’ return defaultValue

๐Ÿ”ง Built-in Transformers

Name Label Description Requires Format
none None Pass-through, no transformation โŒ
trim Trim Remove leading/trailing whitespace โŒ
upper Uppercase Convert to UPPERCASE โŒ
lower Lowercase Convert to lowercase โŒ
integer Integer Cast to int โŒ
float Float Cast to float with precision control โœ… (decimals)
boolean Boolean Cast to bool (handles "true", "1", "yes", etc.) โŒ
date Date Parse and reformat dates โœ… (date format)
array_first Array First Extract first element from array โŒ
array_join Array Join Join array elements with separator โœ… (separator)

๐Ÿ—บ Field Extraction

Dot Notation

Access nested fields in associative arrays:

// Source data
['address' => ['street' => '123 Main St', 'city' => 'NYC']]

// Mapping rule: sourceField = 'address.city'
// Extracted value: 'NYC'

Wildcard Notation

Extract values from arrays of objects:

// Source data
['images' => [
    ['url' => 'img1.jpg', 'alt' => 'First'],
    ['url' => 'img2.jpg', 'alt' => 'Second'],
]]

// Mapping rule: sourceField = 'images.*.url'
// Extracted value: ['img1.jpg', 'img2.jpg']

Indexed (Header-Based) Rows

For data without keys (e.g., CSV rows), provide headers:

$config = new MappingConfigurationData(
    data: [
        ['John', 'john@example.com', '30'],
        ['Jane', 'jane@example.com', '25'],
    ],
    mappingRules: MappingRuleData::collection([
        new MappingRuleData(sourceField: 'name', targetField: 'full_name'),
        new MappingRuleData(sourceField: 'email', targetField: 'contact'),
    ]),
    headers: ['name', 'email', 'age'],
);

๐Ÿ”€ Value Mapping

Map specific values using a lookup table. Useful for code-to-label conversions:

new MappingRuleData(
    sourceField: 'condition_code',
    targetField: 'condition',
    transformation: 'none',
    valueMapping: [
        ['from' => '0', 'to' => 'Used'],
        ['from' => '1', 'to' => 'New'],
        ['from' => '2', 'to' => 'Refurbished'],
    ],
);

// Input: '1' โ†’ Output: 'New'
// Input: '0' โ†’ Output: 'Used'
// Input: '99' โ†’ Output: '99' (unmapped values pass through)

Value mapping is applied before the transformer, so you can combine both:

new MappingRuleData(
    sourceField: 'status',
    targetField: 'display_status',
    transformation: 'upper',
    valueMapping: [['from' => '1', 'to' => 'active'], ['from' => '0', 'to' => 'inactive']],
);
// Input: '1' โ†’ mapped to 'active' โ†’ transformed to 'ACTIVE'

๐Ÿ›  Creating Custom Transformers

Implement the TransformerInterface:

use Elaitech\DataMapper\Contracts\TransformerInterface;

final class SlugTransformer implements TransformerInterface
{
    public function getName(): string
    {
        return 'slug';
    }

    public function getLabel(): string
    {
        return 'Slugify';
    }

    public function getDescription(): string
    {
        return 'Converts text to URL-friendly slug';
    }

    public function transform($value, ?string $format = null, $defaultValue = null)
    {
        if ($value === null) {
            return $defaultValue;
        }

        return \Illuminate\Support\Str::slug((string) $value);
    }

    public function requiresFormat(): bool
    {
        return false;
    }
}

Register it:

$transformer = app(ValueTransformer::class);
$transformer->registerTransformer(new SlugTransformer());

Or register in a service provider for global availability:

public function boot(): void
{
    $this->app->make(ValueTransformer::class)
        ->registerTransformer(new SlugTransformer());
}

๐Ÿ“‹ DTOs

MappingConfigurationData

Input to the mapper:

Property Type Description
data array Array of rows to map
mappingRules DataCollection<MappingRuleData> Mapping rules to apply
headers ?array Column headers for indexed rows

MappingRuleData

A single field mapping rule:

Property Type Default Description
sourceField string โ€” Source field name (supports dot/wildcard notation)
targetField string โ€” Target field name in output
transformation string 'none' Transformer name to apply
isRequired bool false Throw if source field is missing
defaultValue mixed null Fallback for empty values
format ?string null Format parameter for transformers (e.g., date format)
valueMapping ?array null Value lookup table ([['from' => ..., 'to' => ...]])

DataMappingResultData

Output from the mapper:

Property Type Description
data array Successfully mapped rows
errors array Per-row error messages

๐Ÿ“œ Contracts

DataMapperInterface

interface DataMapperInterface
{
    public function map(MappingConfigurationData $config): DataMappingResultData;
}

TransformerInterface

interface TransformerInterface
{
    public function getName(): string;
    public function getLabel(): string;
    public function getDescription(): string;
    public function transform($value, ?string $format = null, $defaultValue = null);
    public function requiresFormat(): bool;
}

๐Ÿงช Testing

# From the package directory
./vendor/bin/phpunit

# From the root project
php artisan test

๐Ÿ“ฆ Dependencies

Package Version Purpose
illuminate/support ^12.0 Laravel framework support
spatie/laravel-data ^4.19 Typed DTOs with auto-mapping

๐Ÿ“„ License

MIT