Passed
Push — master ( 804ca3...3beda5 )
by Sergei
12:28
created

ArArrayHelper::toArray()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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