Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Awkan committed Mar 2, 2018
0 parents commit 68f278c
Show file tree
Hide file tree
Showing 6 changed files with 452 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/vendor/
composer.lock
96 changes: 96 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Array mapping

The goal of the library is to map two arrays (in and out) with an YAML file config.

## Install

WIP, not official lib yet

```bash
composer require valouleloup/array-mapping
```

## Usage

#### Yaml Mapping

```yaml
transformer:
mapping:
civilite:
from: 'demandeur.civilite'
to: 'civ'
function:
name: 'buildCivilite'
params:
- 'demandeur.civilite'
nom:
from: 'demandeur.nomUsage'
to: 'nom'
required: true
prenom:
from: 'demandeur.prenom'
to: 'prenom'
required: true
dateNaissance:
from: 'demandeur.dateNaissance'
to: 'dnat'
function:
name: 'convertDate'
params:
- 'demandeur.dateNaissance'
villeNaissance:
from: 'demandeur.naissanceVille'
to: 'villenaiss'
```
* ` from ` : string, the key's path of the ` in ` array
* ` to ` : string, the key's path of the ` out ` array
* ` function ` : array, the php function to use to transform the data
* ` name ` : string, the function's name
* ` params ` : array, the function's parameters
* ` required ` : boolean, if the key is ` required `
* WIP ` condition ` : array, the conditions that specify if the key is required or not

#### Transformer

Your transformer needs to extends the ` MappingTransformer ` class :

```php
<?php
namespace Transformer;
use DateTime;
use Valouleloup\ArrayMapping\AbstractMappingTransformer;
use Symfony\Component\Yaml\Yaml;
class MyTransformer extends AbstractMappingTransformer
{
/**
* {@inheritdoc}
*/
protected function transform(array $dataBefore)
{
// TODO use parseFile for SF >= 3.4
$config = Yaml::parse(file_get_contents(__DIR__ . '/mapping.yml'));
$result = $this->transformFromMapping($config['transformer']['mapping'], $dataBefore);
...
}
/**
* @param $value
*
* @return string
*/
public function convertDate($value)
{
$date = new DateTime($value);
return $date->format('d/m/Y');
}
}
```

The ` $result ` variable now contains the new array.
28 changes: 28 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "valouleloup/array-mapping",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Valentin Faugeroux",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {
"Valouleloup\\ArrayMapping\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Valouleloup\\ArrayMapping\\Tests\\": "tests/"
}
},
"require": {
"php": "^5.6|>=7.0.8",
"symfony/property-access": "^3.4"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
}
}
15 changes: 15 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit>
<testsuites>
<testsuite name="Valouleloup array-mapping">
<directory>./tests</directory>
</testsuite>
</testsuites>

<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</phpunit>
175 changes: 175 additions & 0 deletions src/AbstractMappingTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<?php

namespace Valouleloup\ArrayMapping;

use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;

abstract class AbstractMappingTransformer
{
/**
* @var PropertyAccessor
*/
protected $accessor;

public function __construct($accessor = null)
{
$this->accessor = $accessor ? $accessor : PropertyAccess::createPropertyAccessor();
}

/**
* Transform the array given in parameter to an output array
* Method goal:
* - Load transformer config (yaml file)
* - Transform input array to output formatted array
*
* @param array $data
* @return mixed
*/
abstract protected function transform(array $data);

/**
* Generate output array by given mapping & input array
*
* @param array $mapping The mapping to use to transform input array
* @param array $data The input array
* @return array The output array
* @throws \Exception
*/
protected function transformFromMapping(array $mapping, array $data)
{
$result = [];

foreach ($mapping as $node) {
// Only transform current node if no condition prevent it
if ($this->areConditionsValidated($node, $data)) {
// Get our node value
$nodeValue = $this->getNodeValue($node, $data);

// If we didn't get value but field was required, throw Exception
if (isset($node['required']) && true === $node['required'] && null === $nodeValue) {
throw new \Exception('Field ' . $node['from'] . ' required.');
}

if (isset($node['dependencies'])) {
$dependenciesExist = true;

foreach ($node['dependencies'] as $dependency) {
if (null === $this->getValue($result, $mapping[$dependency]['to'])) {
$dependenciesExist = false;
}
}

if ($dependenciesExist && null === $nodeValue) {
throw new \Exception('Field ' . $node['from'] . ' required if dependencies true.');
}
}

// nodeValue was found && acceptable, add value to result array
if (null !== $nodeValue) {
$this->accessor->setValue($result, $this->convertToBrackets($node['to']), $nodeValue);
}
}
}

return $result;
}

/**
* Check if a node can be transformed
*
* @param array $node The node to check
* @param array $data The input date to parse
* @return bool True if node is transformable, False if node isn't transformable
*/
private function areConditionsValidated(array $node, array $data)
{
// If node doesn't have condition, execute transformation
if (!isset($node['condition'])) {
return true;
}

// Only transform node if field exists in array to parse
if (isset($node['condition']['exists'])
&& \is_null($this->getValue($data, $node['condition']['exists']))) {
return false;
}

return true;
}

/**
* Get the value related to a node
*
* @param array $node
* @param array $data
*
* @return mixed
*/
private function getNodeValue(array $node, array $data)
{
//1. Get function value if is set so
//2. Else get raw value is no function is defined
//3. If keyValue is null and a default value is set, return default value
$nodeValue = null;

if (isset($node['function'])) {
$params = [];

foreach ($node['function']['params'] as $param) {
$params[] = $this->getValue($data, $param);
}

$nodeValue = call_user_func_array([$this, $node['function']['name']], $params);
} else {
if (isset($node['from'])) {
$nodeValue = $this->getValue($data, $node['from']);
}
}

if (null === $nodeValue && isset($node['default'])) {
// If default value is an array, try to get it node value
// Else return default value which is static
if (\is_array($node['default'])) {
$nodeValue = $this->getNodeValue($node['default'], $data);
} else {
$nodeValue = $node['default'];
}
}

return $nodeValue;
}

/**
* Get value set in the input array by finding them with key
*
* @param array $data The input array
* @param string $key The key which is the location of the value in the input array
*
* @return mixed|null The related value, if value doesn't exists return null
*/
private function getValue(array $data, $key)
{
return $this->accessor->getValue($data, $this->convertToBrackets($key));
}

/**
* Convert path separated with dot to path separated with brackets
* ie. path.to.convert will produce [path][to][convert]
*
* @param string $path The path to convert
* @return string the converted path
*/
private function convertToBrackets($path)
{
$keys = explode('.', $path);

$bracketPath = '';

foreach ($keys as $key) {
$bracketPath .= '[' . $key . ']';
}

return $bracketPath;
}
}
Loading

0 comments on commit 68f278c

Please sign in to comment.