schemator-php
Schematic data mapper
Schematic data mapper is a tool for converting nested data structures (any composition of associative arrays, non-associative arrays and objects) according to the given conversion schema.
How to install to your project
composer require smoren/schemator
Usage
Simple usage
use Smoren\Schemator\Factories\SchematorFactory;
$input = [ 'id' => 100, 'name' => 'Oxford', 'country' => [ 'id' => 10, 'name' => 'UK', 'neighbours' => ['Ireland', 'Sweden', 'France'], 'capitals' => [ 'lnd' => 'London', 'edb' => 'Edinburgh', ], ], 'streets' => [ [ 'id' => 1000, 'name' => 'Woodstock Rd', 'houses' => [1, 5, 9], ], [ 'id' => 1002, 'name' => 'Banbury Rd', 'houses' => [22, 35, 49], ], [ 'id' => 1003, 'name' => 'Beamont St', 'houses' => [11, 12, 15], ], ], 'lnd_path' => 'country.capitals.lnd',];
$schema = [ 'city_id' => 'id', 'city_name' => 'name', 'city_street_names' => 'streets.*.name', 'country_id' => 'country.id', 'country_name' => 'country.name', 'country_neighbours' => 'country.neighbours', 'country_neighbour' => 'country.neighbours', 'country_first_capital' => 'country.capitals.lnd', 'country_second_capital' => 'country.capitals.edb', 'country_data.country_id' => 'country.id', 'country_data.country_name' => 'country.name',];
$schemator = SchematorFactory::create();$output = $schemator->convert($input, $schema);
print_r($output);/* Array( [city_id] => 100 [city_name] => Oxford [city_street_names] => Array ( [0] => Woodstock Rd [1] => Banbury Rd [2] => Beamont St )
[country_id] => 10 [country_name] => UK [country_neighbours] => Array ( [0] => Ireland [1] => Sweden [2] => France )
[country_neighbour] => Array ( [0] => Ireland [1] => Sweden [2] => France )
[country_first_capital] => London [country_second_capital] => Edinburgh [country_data] => Array ( [country_id] => 10 [country_name] => UK )
)*/
Setting errors level
use Smoren\Schemator\Factories\SchematorFactory;use Smoren\Schemator\Structs\ErrorsLevelMask;use Smoren\Schemator\Exceptions\SchematorException;
$input = [ 'some_key' => null,];$schema = [ 'my_value' => ['some_key', ['date', 'Y-m-d']],];
$schemator = SchematorFactory::createBuilder() ->withErrorsLevelMask( ErrorsLevelMask::nothing() ->add([SchematorException::FILTER_ERROR, SchematorException::CANNOT_GET_VALUE]) ) ->get();
try { $schemator->convert($input, $schema);} catch(SchematorException $e) { echo $e->getMessage(); // filter error: 'date'}
Using base filters
use Smoren\Schemator\Factories\SchematorFactory;use Smoren\Schemator\Filters\BaseFiltersStorage;
$input = [ 'id' => 100, 'name' => 'Oxford', 'country' => [ 'id' => 10, 'name' => 'UK', 'neighbours' => ['Ireland', 'Sweden', 'France'], 'capitals' => [ 'lnd' => 'London', 'edb' => 'Edinburgh', ], ], 'streets' => [ [ 'id' => 1000, 'name' => 'Woodstock Rd', 'houses' => [1, 5, 9], ], [ 'id' => 1002, 'name' => 'Banbury Rd', 'houses' => [22, 35, 49], ], [ 'id' => 1003, 'name' => 'Beamont St', 'houses' => [11, 12, 15], ], ], 'lnd_path' => 'country.capitals.lnd',];
$schema = [ 'city_street_names.all' => ['streets.*.name', ['implode', ', ']], 'city_street_names.sorted' => ['streets.*.name', ['sort'], ['implode', ', ']], 'city_street_names.filtered' => ['streets.*.name', ['filter', fn (string $candidate) => strpos($candidate, 'Ban') !== false]], 'lnd' => ['lnd_path', ['path']], 'city_street_houses' => ['streets.*.houses', ['flatten']],];
$schemator = SchematorFactory::create();$output = $schemator->convert($input, $schema);
print_r($output);/*Array( [city_street_names] => Array ( [all] => Woodstock Rd, Banbury Rd, Beamont St [sorted] => Banbury Rd, Beamont St, Woodstock Rd [filtered] => Array ( [0] => Banbury Rd )
)
[lnd] => London [city_street_houses] => Array ( [0] => 1 [1] => 5 [2] => 9 [3] => 22 [4] => 35 [5] => 49 [6] => 11 [7] => 12 [8] => 15 )
)*/
Using smart filter and replace
use Smoren\Schemator\Factories\SchematorFactory;use Smoren\Schemator\Filters\BaseFiltersStorage;
$schemator = SchematorFactory::create();$input = [ 'numbers' => [-1, 10, 5, 22, -10, 0, 35, 7, 8, 9, 0],];
$output = $schemator->convert($input, [ 'positive' => [ 'numbers', ['filter', [['>', 0]]], ['sort'], ], 'negative' => [ 'numbers', ['filter', [['<', 0]]], ['sort'], ], 'complicated' => [ 'numbers', ['filter', [['>=', 8], ['<', 0]]], ['filter', [['<', 22]]], ['sort'], ],]);
print_r($output);/*Array( [positive] => Array ( [0] => 5 [1] => 7 [2] => 8 [3] => 9 [4] => 10 [5] => 22 [6] => 35 )
[negative] => Array ( [0] => -10 [1] => -1 )
[complicated] => Array ( [0] => -10 [1] => -1 [2] => 8 [3] => 9 [4] => 10 )
)*/
$output = $schemator->convert($input, [ 'number_types' => ['numbers', [ 'replace', [ ['=0', '=', 0], ['>9', '>', 9], ['<0', '<', 0], ['1-8', 'between', 1, 8], ] ]]]);
print_r($output);/*Array( [number_types] => Array ( [0] => <0 [1] => >9 [2] => 1-8 [3] => >9 [4] => <0 [5] => =0 [6] => >9 [7] => 1-8 [8] => 1-8 [9] => 9 [10] => =0 )
)*/
Using custom filters
use Smoren\Schemator\Factories\SchematorFactory;use Smoren\Schemator\Interfaces\FilterContextInterface;
$schemator = SchematorFactory::createBuilder() ->withFilters([ 'startsWith' => function (FilterContextInterface $context, string $start) { return array_filter($context->getSource(), function (string $candidate) use ($start) { return strpos($candidate, $start) === 0; }); }, ]) ->get();
$input = [ 'streets' => ['Woodstock Rd', 'Banbury Rd', 'Beamont St'],];
$schema = [ 'street_names' => ['streets', ['startsWith', 'T'], ['implode', ', ']],];
$output = $schemator->convert($input, $schema);
print_r($output);/*Array( [street_names] => Woodstock Rd, Beamont St)*/
Mass usage
use Smoren\Schemator\Factories\SchematorFactory;
$massSchemator = SchematorFactory::createMass();
$cities = [ [ 'id' => 100, 'name' => 'London', 'country' => [ 'id' => 10, 'name' => 'UK', ], 'streets' => [ [ 'id' => 1001, 'name' => 'The Mall', ], [ 'id' => 1002, 'name' => 'Carnaby Street', ], ], ], [ 'id' => 101, 'name' => 'Oxford', 'country' => [ 'id' => 10, 'name' => 'UK', ], 'streets' => [ [ 'id' => 1003, 'name' => 'Turl Street', ], [ 'id' => 1004, 'name' => 'Holywell Street', ], ], ],];
$schema = [ 'city_id' => 'id', 'city_name' => 'name', 'city_street_names' => 'streets.*.name', 'country_id' => 'country.id', 'country_name' => 'country.name',];
$gen = $massSchemator->generate($cities, $schema);
$result = [];foreach($gen as $item) { $result[] = $item;}
print_r($result);/*Array( [0] => Array ( [city_id] => 100 [city_name] => London [city_street_names] => Array ( [0] => The Mall [1] => Carnaby Street )
[country_id] => 10 [country_name] => UK ) [1] => Array ( [city_id] => 101 [city_name] => Oxford [city_street_names] => Array ( [0] => Turl Street [1] => Holywell Street )
[country_id] => 10 [country_name] => UK ))*/
Filters
const
Sets the value from const param.
Schema:
["value" => [["const", "My const value"]]]
Result:
["value" => "My const value"]
sum
Returns the sum of given array.
Given:
["numbers" => [1, 2, 3, 4, 5]]
Schema:
["value" => ["numbers", ["sum"]]]
Result:
["value" => 15]
average
Returns the average of given array.
Given:
["numbers" => [1, 2, 3, 4, 5]]
Schema:
["value" => ["numbers", ["average"]]]
Result:
["value" => 3]
date
Returns formatted date from the Unix timestamp given value.
Params:
- Date format
- required
- data type — string
- example: d.m.Y H:i:s
- more about formats
- Time zone offset from GMT
- optional
- data type — integer
- default — 0
Given:
["some_date" => 1651481881]
Schema:
["value" => ["some_date", ["date", "d.m.Y H:i:s", 3]]]
Result:
["value" => "02.05.2022 11:58:01"]
implode
Returns string of imploded items of given array with separator from args list.
params:
- Separator
- required
- data type — string
- example: ;
Given:
["numbers" => [1, 2, 3, 4, 5]]
Schema:
["value" => ["numbers", ["implode", "; "]]]
Result:
["value" => "1; 2; 3; 4; 5"]
explode
Returns array of exploded strings from given string with separator from args list
params:
- Separator
- required
- data type — string
- example: ;
Given:
["numbers" => "1; 2; 3; 4; 5"]
Schema:
["value" => ["numbers", ["explode", "; "]]]
Result:
["value" => ["1", "2", "3", "4", "5"]]
flatten
Returns flat array contains all the dead end leaves of tree array.
Given:
[ "numbers" => [ [ [1, 2, 3], [4, 5, 6] ], [7, 8, 9] ],]
Schema:
["value" => ["numbers", ["flatten"]]]
Result:
["value" => [1, 2, 3, 4, 5, 6, 7, 8, 9]]
sort
Sorts and returns given array.
Given:
["numbers" => [3, 5, 4, 1, 2]]
Schema:
["value" => ["numbers", ["sort"]]]
Result:
["value" => [1, 2, 3, 4, 5]]
rsort
Sorts reversely and returns given array.
Given:
["numbers" => [3, 5, 4, 1, 2]]
Schema:
["value" => ["numbers", ["sort"]]]
Result:
["value" => [5, 4, 3, 2, 1]]
filter
Returns array containing elements from given array that match the predicates from the params list.
Rules:
- Every predicate has such format
.["predicate name", ...parans] - Predicates in one filter apply according the "OR" logic.
- To apply "AND" logic use chain of filters.
- Available predicates:
means["=", 10]value = 10
means[">", 10]value > 10
means[">=", 10]value >= 10
means["<", 10]value < 10
means["<=", 10]value <= 10
means["in", [1, 2]]value = 1 OR value = 2
means["not in", [1, 2]]value != 1 AND value != 2
means["between", 1, 5]1 <= value <= 5
means["between strict", 1, 5]1 < value < 5
Given:
["numbers" => [-5, -3, -1, 1, 3, 5]]
Schema:
[ "value" => [ "numbers", [ "filter", [[">", 1], ["<", -1]] // value > 1 OR value < -1 ], ],]
Result:
["value" => [-5, -3, 3, 5]]
replace
Returns array of elements with values replaced according to the rules in the params list.
Rules:
- Every rule has such format
.["value to replace", "rule name", ...params] - Rules in one filter apply according the "OR" logic.
- To apply "AND" logic use chain of filters.
- Available rules:
means["=", 10]value = 10
means[">", 10]value > 10
means[">=", 10]value >= 10
means["<", 10]value < 10
means["<=", 10]value <= 10
means["in", [1, 2]]value = 1 или value = 2
means["not in", [1, 2]]value != 1 и value != 2
means["between", 1, 5]1 <= value <= 5
means["between strict", 1, 5]1 < value < 5
— no rules matched for value (If rule["else"]
did not use, by default such values are replaced withelse
)null
Given:
["numbers" => [-5, -3, -1, 0, 1, 3, 5]]
Schema:
[ "value" => [ "numbers", [ "replace", [ ["positive", ">", 0], ["negative", "<", 0], ["zero", "else"] ], ], ],]
Result:
["value" => ["negative", "negative", "negative", "zero", "positive", "positive", "positive"]]
Chain of filters
Given:
["numbers" => [-5, -3, -1, 1, 3, 5]]
Schema:
[ "value" => [ "numbers", [ "filter", [[">", 1], ["<", -1]] // (value > 1 OR value < -1) ], // AND [ "filter", [[">=", -3]] // value >= -3 ], ],]
Result:
["value" => [-3, 3, 5]]
Unit testing
composer install
composer test-init
composer test
Standards
Schemator conforms to the following standards:
- PSR-1 — Basic coding standard
- PSR-4 — Autoloader
- PSR-12 — Extended coding style guide
License
Schemator is licensed under the MIT License.
Описание
Schematic data mapper and nested access tools
Языки
PHP