ArrayMapper   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 121
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 54
c 1
b 0
f 0
dl 0
loc 121
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A isFieldsNotNull() 0 13 4
B getFieldValue() 0 32 9
B map() 0 47 9
1
<?php
2
3
namespace Smoren\ArrayMapper;
4
5
/**
6
 * Mapper helper
7
 */
8
class ArrayMapper
9
{
10
    /**
11
     * Maps array with map fields
12
     * @param array<array<mixed>>|array<object> $input array of arrays or objects you want to map
13
     * @param array<scalar|callable> $mapFields fields for mapping (scalar or callable)
14
     * @param bool $multiple support multiple results on mapping
15
     * @param bool $ignoreNulls ignore items with nullable mapping fields values
16
     * @param callable|null $valueGetter callable value getter
17
     * @return array<mixed> mapped array
18
     * @throws ArrayMapperException
19
     */
20
    public static function map(
21
        array $input,
22
        array $mapFields,
23
        bool $multiple,
24
        bool $ignoreNulls = true,
25
        ?callable $valueGetter = null
26
    ): array {
27
        $result = [];
28
29
        foreach($input as $item) {
30
            if($ignoreNulls && !static::isFieldsNotNull($item, $mapFields)) {
31
                continue;
32
            }
33
            $resultPointer = &$result;
34
            foreach($mapFields as $field) {
35
                $fieldValue = static::getFieldValue($item, $field);
36
                if(!is_scalar($fieldValue)) {
37
                    $field = strval($field);
38
                    throw new ArrayMapperException(
39
                        "field value of '{$field}' is not scalar",
40
                        ArrayMapperException::STATUS_NON_SCALAR_FIELD_VALUE,
41
                        null,
42
                        [
43
                            'fieldName' => $field,
44
                            'fieldValue' => $fieldValue,
45
                            'source' => $item,
46
                        ]
47
                    );
48
                }
49
                /** @var array<string|int|bool|null, mixed> $resultPointer */
50
                if(!isset($resultPointer[$fieldValue])) {
51
                    $resultPointer[$fieldValue] = [];
52
                }
53
                $resultPointer = &$resultPointer[$fieldValue];
54
            }
55
56
            $value = is_callable($valueGetter) ? $valueGetter($item) : $item;
57
58
            if($multiple) {
59
                /** @var array<mixed> $resultPointer */
60
                $resultPointer[] = $value;
61
            } else {
62
                $resultPointer = $value;
63
            }
64
        }
65
66
        return $result;
67
    }
68
69
    /**
70
     * Checks that field values are not null for source item
71
     * @param array<mixed>|object $source source item
72
     * @param array<scalar|callable> $fieldNames field names
73
     * @return bool
74
     */
75
    protected static function isFieldsNotNull($source, array $fieldNames): bool
76
    {
77
        foreach($fieldNames as $fieldName) {
78
            try {
79
                if(static::getFieldValue($source, $fieldName) === null) {
80
                    return false;
81
                }
82
            } catch(ArrayMapperException $e) {
83
                return false;
84
            }
85
        }
86
87
        return true;
88
    }
89
90
    /**
91
     * Returns field value for source item and field name
92
     * @param array<mixed>|object $source source item
93
     * @param scalar|callable $fieldName field name
94
     * @return mixed field value
95
     * @throws ArrayMapperException
96
     */
97
    protected static function getFieldValue($source, $fieldName)
98
    {
99
        if(is_callable($fieldName)) {
100
            return $fieldName($source);
101
        }
102
103
        if(
104
            is_array($source) && !array_key_exists((string)$fieldName, $source)
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (is_array($source) && ! ...ce, (string)$fieldName), Probably Intended Meaning: is_array($source) && (! ...e, (string)$fieldName))
Loading history...
105
            || is_object($source) && !isset($source->{$fieldName}) && !property_exists($source, (string)$fieldName)
106
        ) {
107
            throw new ArrayMapperException(
108
                "field '{$fieldName}' not exist",
109
                ArrayMapperException::STATUS_FIELD_NOT_EXIST,
110
                null,
111
                [
112
                    'fieldName' => $fieldName,
113
                    'source' => $source,
114
                ]
115
            );
116
        }
117
118
        if(is_array($source)) {
119
            return $source[$fieldName];
120
        } elseif(is_object($source)) {
121
            return $source->{$fieldName};
122
        } else {
123
            throw new ArrayMapperException(
124
                "source item is scalar",
125
                ArrayMapperException::STATUS_SCALAR_SOURCE_ITEM,
126
                null,
127
                [
128
                    'source' => $source,
129
                ]
130
            );
131
        }
132
    }
133
}
134