/automapper

Powerful declarative data mapper for PHP 8

Primary LanguagePHPMIT LicenseMIT

โ„น๏ธ You are on a branch with the second version of the acelot/automapper. If you want a previous version, then proceed to 1.x branch.

AutoMapper

build coverage packagist dependencies MIT

AutoMapper is a powerful declarative data mapper for PHP 8. AutoMapper can map data from any source data (usually array/object) to the existing array/object or marshal a new ones.

๐Ÿ’ฟ Install

composer require acelot/automapper:^2.0

๐Ÿ—ฟ Usage

How to marshal new array from the another?

use Acelot\AutoMapper\Context;
use Acelot\AutoMapper\AutoMapper as a;

$source = [
    'id' => '99',
    'name' => [
        'lastname' => 'Doe',
        'firstname' => 'John',
    ],
    'skills' => 'Php, CSS,JS,html, MySql,  brainfuck,'
];

$result = a::marshalArray(
    new Context(),
    $source,
    a::toKey('id', a::pipe(
        a::get('[id]'),
        a::toInt()
    )),
    a::toKey('fullname', a::pipe(
        a::get('[name]'),
        a::call(fn($v) => $v['firstname'] . ' ' . $v['lastname'])
    )),
    a::toKey('skills', a::pipe(
        a::get('[skills]'),
        a::explodeString(','),
        a::mapIterable(a::pipe(
            a::trimString(),
            a::ifEmpty(a::ignore()),
            a::call('strtolower')
        )),
        a::toArray(),
        a::sortArray()
    ))
);

// Output of `var_export($result)`
array(
  'id' => 99,
  'fullname' => 'John Doe',
  'skills' => [
    0 => 'brainfuck',
    1 => 'css',
    2 => 'html',
    3 => 'js',
    4 => 'mysql',
    5 => 'php',
  ],
)

How to map data from source to the existing array?

Show the code
use Acelot\AutoMapper\Context;
use Acelot\AutoMapper\AutoMapper as a;

$source = [
    'title' => '  Product title  ',
    'desc' => [
        'Product short description',
        'Product regular description',
        'Product descriptive description',
    ]
];

$target = [
    'id' => 5,
    'title' => 'Current title',
];

$result = a::map(
    new Context(),
    $source,
    $target,
    a::toKey('title', a::pipe(
        a::get('[title]'),
        a::trimString()
    )),
    a::toKey('description', a::get('[desc][#last]')),
);

// Output of `var_export($result)`
array (
  'id' => 5,
  'title' => 'Product title',
  'description' => 'Product descriptive description',
)

๐Ÿ“Œ Examples

All examples can be found in tests/Functional directory.

๐Ÿ—„๏ธ Reference

No need to use concrete classes, it's better to use the AutoMapper API static functions. It is very convenient to import the AutoMapper as a short alias, for example use Acelot\AutoMapper\AutoMapper as a.

Main functions

The main functions of AutoMapper.

Function Description
map Maps data from the source to the target. Target is passing by reference.
marshalArray Maps data from the source to the keys of the new array.
marshalObject Maps data from the source to the properties of the new stdClass.

Field definitions

Definitions that helps you to shape the target structure.

Function Description
toKey Sets/creates the value by key with given name in the target array.
toProp Sets/creates the value by property with given name in the target object.
toMethod Calls a method with given name with value as an argument in the target object.
toSelf Assigns a value to the target.

Processors

Core value processors. The purpose of processors is to retrieve the value or mutate the incoming value and pass it to the next one.

Function Description
assertType Asserts the type of value. Throws UnexpectedValueException if assert is failed.
call Process the value by user defined function.
callCtx Same as call but context will be passed as a first argument.
condition Condition processor. If the user-defined function returns true, then the value will be passed to the first processor, otherwise to the second.
conditionCtx Same as condition but context will be passed to the user-defined function.
find Finds the element in iterable by the given predicate function.
findCtx Same as find but context will be passed to the predicate function.
get Most useful processor. Fetches the value from the source by given path.
getFromCtx Fetches the value from the context.
ignore Always returns the IgnoreValue. This value will be ignored by field definition, mapArray and mapIterator
mapIterable Iterates over elements of an iterable and applies the given sub-processor. โ„น๏ธ Produces values by yield operator, so in order to retrieve them you should to iterate the result or call toArray helper.
marshalNestedArray The same function as mapArray only in the form of a processor. Accepts the value from the previous processor as a source.
marshalNestedObject Same as marshalNestedArray only produces object.
notFound Always returns the NotFoundValue.
pass This processor do nothing and just returns the value untouched.
pipe Conveyor processor. Accepts multiple sub-processors and pass the value to the first sub-processor, then pass the result of the first to the second, then to the third and so on.
value Just returns the given value.

Helpers

Helpers are the processors that built on top of the another processors. Some helpers are just a shorthands to the core processors with specific arguments, some of them are combination of the multiple processors.

Function Description
joinArray Joins the incoming array using the given separator. Throws UnexpectedValueException if incoming value is not an array.
sortArray Sorts the incoming array using built-in sort function. Throws UnexpectedValueException if incoming value is not an array.
uniqueArray Returns only unique elements of the incoming array. Throws UnexpectedValueException if incoming value is not an array.
ifNotFound Checks if the incoming value is NotFoundValue and passes the value to other processors depending on the result.
ifEmpty Same as ifNotFound but checks if the value is empty.
ifNull Same as ifNotFound but checks if the value is_null.
IfEqual Checks if the incoming value is equal to the given value.
ifNotEqual Checks if the incoming value is not equal to the given value.
explodeString Splits the incoming string into parts using built-in explode function. Throws UnexpectedValueException if incoming value is not a string.
trimString Trims the incoming string using built-in trim function. Throws UnexpectedValueException if incoming value is not a string.
toBool Converts the incoming value to boolean type.
toFloat Converts the incoming value to float type. Throws UnexpectedValueException if incoming value is not a scalar.
toInt Converts the incoming value to integer type. Throws UnexpectedValueException if incoming value is not a scalar.
toString Converts the incoming value to string type. Throws UnexpectedValueException if incoming value is not a scalar or an object that not implements __toString.
toArray Converts the incoming value to array. Usually used with mapIterable processor.

๐Ÿงฉ Integrations

Name Description
RespectValidation Provides validation processor via respect/validation library.

๐Ÿคจ FAQ

What is Context?

The Context is a special DTO class for storing any kind of data: configs, DB connections, fixtures, etc. This DTO is passed to the mapper, and you can use your data inside the processors. Processors capable of working with the context end with Ctx suffix, callCtx for example.

How to use get processor?

You can obtain any key/prop/method from the source using the get processor which accepts a special path string. The processor parses the given path and divides it into parts, then pulls out the data following the parts of the path.

Available path parts:

Part Description
@ "Self Pointer" โ€“ returns the source itself
[0] Returns an array value by index
[key] Returns an array value by key
[some key] Returns an array value by key with spaces
[#first] Returns an array first value
[#last] Returns an array last value
->property Returns an object value by property
->{some property} Returns an object value by property name with spaces
->someMethod() Calls an object method and returns the value

You can combine the parts to obtain the deep values:

[array_key][array key with spaces][#first][#last]->property->{property with spaces}->someMethod()

If any part of the path is not found, then the processor will return NotFoundValue value. This value throws an NotFoundException but you can recover it using ifNotFound helper.

๐Ÿ–‹๏ธ License

Licensed under MIT. Copyright (c) 2017-present, Valeriy Protopopov