Smoren /
array-mapper-php
| 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
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 |