Passed
Push — master ( 4d6bca...ad73d8 )
by Sergei
02:47
created

ArArrayHelper   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 14
eloc 25
c 2
b 0
f 0
dl 0
loc 141
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getColumn() 0 5 1
A index() 0 18 4
B getValueByPath() 0 26 9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord;
6
7
use Closure;
8
9
use function array_combine;
10
use function array_key_exists;
11
use function array_map;
12
use function get_object_vars;
13
use function property_exists;
14
use function strrpos;
15
use function substr;
16
17
/**
18
 * Array manipulation methods for ActiveRecord.
19
 *
20
 * @psalm-type Row = ActiveRecordInterface|array
21
 * @psalm-type IndexKey = string|Closure(Row, mixed=):mixed
22
 */
23
final class ArArrayHelper
24
{
25
    /**
26
     * Returns the values of a specified column in an array.
27
     *
28
     * The input array should be multidimensional or an array of {@see ActiveRecordInterface} instances.
29
     *
30
     * For example,
31
     *
32
     * ```php
33
     * $array = [
34
     *     ['id' => '123', 'data' => 'abc'],
35
     *     ['id' => '345', 'data' => 'def'],
36
     * ];
37
     * $result = ArArrayHelper::getColumn($array, 'id');
38
     * // the result is: ['123', '345']
39
     * ```
40
     *
41
     * @param array $array Array to extract values from.
42
     * @param string $name The column name.
43
     *
44
     * @psalm-param Row[] $array
45
     *
46
     * @return array The list of column values.
47
     */
48
    public static function getColumn(array $array, string $name): array
49
    {
50
        return array_map(
51
            static fn (ActiveRecordInterface|array $element): mixed => self::getValueByPath($element, $name),
52
            $array
53
        );
54
    }
55
56
    /**
57
     * Retrieves a value from the array by the given key or from the {@see ActiveRecordInterface} instance
58
     * by the given property or relation name.
59
     *
60
     * If the key doesn't exist, the default value will be returned instead.
61
     *
62
     * The key may be specified in a dot format to retrieve the value of a sub-array or a property or relation of the
63
     * {@see ActiveRecordInterface} instance.
64
     *
65
     * In particular, if the key is `x.y.z`, then the returned value would be `$array['x']['y']['z']` or
66
     * `$array->x->y->z` (if `$array` is an {@see ActiveRecordInterface} instance).
67
     *
68
     * Note that if the array already has an element `x.y.z`, then its value will be returned instead of going through
69
     * the sub-arrays.
70
     *
71
     * Below are some usage examples.
72
     *
73
     * ```php
74
     * // working with an array
75
     * $username = ArArrayHelper::getValueByPath($array, 'username');
76
     * // working with an {@see ActiveRecordInterface} instance
77
     * $username = ArArrayHelper::getValueByPath($user, 'username');
78
     * // using dot format to retrieve the property of an {@see ActiveRecordInterface} instance
79
     * $street = ArArrayHelper::getValue($users, 'address.street');
80
     * ```
81
     *
82
     * @param ActiveRecordInterface|array $array Array or an {@see ActiveRecordInterface} instance to extract value from.
83
     * @param string $key Key name of the array element or a property or relation name
84
     * of the {@see ActiveRecordInterface} instance.
85
     * @param mixed|null $default The default value to be returned if the specified `$key` doesn't exist.
86
     *
87
     * @psalm-param Row $array
88
     *
89
     * @return mixed The value of the element if found, default value otherwise
90
     */
91
    public static function getValueByPath(ActiveRecordInterface|array $array, string $key, mixed $default = null): mixed
92
    {
93
        if ($array instanceof ActiveRecordInterface) {
0 ignored issues
show
introduced by
$array is never a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface.
Loading history...
94
            if ($array->hasAttribute($key)) {
95
                return $array->getAttribute($key);
96
            }
97
98
            if (property_exists($array, $key)) {
99
                return array_key_exists($key, get_object_vars($array)) ? $array->$key : $default;
100
            }
101
102
            if ($array->isRelationPopulated($key)) {
103
                return $array->relation($key);
104
            }
105
        } elseif (array_key_exists($key, $array)) {
106
            return $array[$key];
107
        }
108
109
        if (!empty($key) && ($pos = strrpos($key, '.')) !== false) {
110
            $array = self::getValueByPath($array, substr($key, 0, $pos), $default);
111
            $key = substr($key, $pos + 1);
112
113
            return self::getValueByPath($array, $key, $default);
114
        }
115
116
        return $default;
117
    }
118
119
    /**
120
     * Indexes an array of rows with the specified column value as keys.
121
     *
122
     * The input array should be multidimensional or an array of {@see ActiveRecordInterface} instances.
123
     *
124
     * For example,
125
     *
126
     * ```php
127
     * $rows = [
128
     *     ['id' => '123', 'data' => 'abc'],
129
     *     ['id' => '345', 'data' => 'def'],
130
     * ];
131
     * $result = ArArrayHelper::populate($rows, 'id');
132
     * // the result is: ['123' => ['id' => '123', 'data' => 'abc'], '345' => ['id' => '345', 'data' => 'def']]
133
     * ```
134
     *
135
     * @param array[] $rows Array to populate.
136
     * @param Closure|string|null $indexBy The column name or anonymous function that specifies the index by which to
137
     * populate the array of rows.
138
     *
139
     * @psalm-template TRow of Row
140
     * @psalm-param array<TRow> $rows
141
     * @psalm-param IndexKey|null $indexBy
142
     * @psalm-return array<TRow>
143
     *
144
     * @return array[]
145
     */
146
    public static function index(array $rows, Closure|string|null $indexBy = null): array
147
    {
148
        if ($indexBy === null) {
149
            return $rows;
150
        }
151
152
        if ($indexBy instanceof Closure) {
153
            return array_combine(array_map($indexBy, $rows), $rows);
154
        }
155
156
        $result = [];
157
158
        foreach ($rows as $row) {
159
            /** @psalm-suppress MixedArrayOffset */
160
            $result[self::getValueByPath($row, $indexBy)] = $row;
161
        }
162
163
        return $result;
164
    }
165
}
166