1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace R2\Helpers; |
4
|
|
|
|
5
|
|
|
use Spatie\Macroable\Macroable; |
6
|
|
|
|
7
|
|
|
class ArrayRecordset |
8
|
|
|
{ |
9
|
|
|
const CASE_SENSITIVE = 1; |
10
|
|
|
const PRESERVE_KEYS = 2; |
11
|
|
|
|
12
|
|
|
/** @var string */ |
13
|
|
|
protected $name = 'Recordset'; |
14
|
|
|
|
15
|
|
|
use Macroable; |
16
|
|
|
|
17
|
|
|
/** @var array */ |
18
|
|
|
protected $data; |
19
|
|
|
|
20
|
|
|
/** @var int */ |
21
|
|
|
protected $options; |
22
|
|
|
|
23
|
|
|
/** @var callable[] */ |
24
|
|
|
protected $comparators; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Chain constructor. |
28
|
|
|
* |
29
|
|
|
* @param array $data |
30
|
|
|
*/ |
31
|
|
|
public function __construct(array $data = [], int $options = 0) |
32
|
|
|
{ |
33
|
|
|
$this->data = $data; |
34
|
|
|
$this->options = $options; |
35
|
|
|
$this->comparators = []; |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Set source data. |
40
|
|
|
* |
41
|
|
|
* @param array $data |
42
|
|
|
* @return self |
43
|
|
|
*/ |
44
|
|
|
public function data(array $data = []): self |
45
|
|
|
{ |
46
|
|
|
$this->data = $data; |
47
|
|
|
return $this; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Simple sort. |
52
|
|
|
* |
53
|
|
|
* @param string $field |
54
|
|
|
* @param string $direction |
55
|
|
|
* @return self |
56
|
|
|
*/ |
57
|
|
|
public function orderBy(string $field, string $direction = 'asc'): self |
58
|
|
|
{ |
59
|
|
|
$sign = strtolower($direction) === 'asc' ? 1 : -1; |
60
|
|
|
if ($this->options & self::CASE_SENSITIVE) { |
61
|
|
|
$this->comparators[] = function ($a, $b) use ($field, $sign) { |
62
|
|
|
return strnatcmp($a[$field], $b[$field]) * $sign; |
63
|
|
|
}; |
64
|
|
|
} else { |
65
|
|
|
$this->comparators[] = function ($a, $b) use ($field, $sign) { |
66
|
|
|
return strnatcasecmp($a[$field], $b[$field]) * $sign; |
67
|
|
|
}; |
68
|
|
|
} |
69
|
|
|
return $this; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Sort by arbitrary function applicable for PHP usort(). |
74
|
|
|
* |
75
|
|
|
* @param callable $func |
76
|
|
|
* @return self |
77
|
|
|
*/ |
78
|
|
|
public function orderByCallable(callable $func): self |
79
|
|
|
{ |
80
|
|
|
$this->comparators[] = $func; |
81
|
|
|
return $this; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Group by field. |
86
|
|
|
* It cause additional dimension of array. Should be called right before the get() or first() call. |
87
|
|
|
* |
88
|
|
|
* @param string $field |
89
|
|
|
* @return self |
90
|
|
|
*/ |
91
|
|
|
public function groupBy(string $field): self |
92
|
|
|
{ |
93
|
|
|
$grouped = []; |
94
|
|
|
foreach ($this->get() as $item) { |
95
|
|
|
$key = $item[$field]; |
96
|
|
|
$grouped[$key][] = $item; |
97
|
|
|
} |
98
|
|
|
$this->data = $grouped; |
99
|
|
|
$this->comparators = []; |
100
|
|
|
return $this; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Get result array. |
105
|
|
|
* |
106
|
|
|
* @return array |
107
|
|
|
*/ |
108
|
|
|
public function get(): array |
109
|
|
|
{ |
110
|
|
|
if ($this->comparators) { |
|
|
|
|
111
|
|
|
$sort = $this->options & self::PRESERVE_KEYS ? 'uasort' : 'usort'; |
112
|
|
|
$sort($this->data, function ($a, $b) { |
113
|
|
|
foreach ($this->comparators as $f) { |
114
|
|
|
$result = $f($a, $b); |
115
|
|
|
if ($result !== 0) { |
116
|
|
|
return $result; |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
return 0; |
120
|
|
|
}); |
121
|
|
|
} |
122
|
|
|
return $this->data; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Get first record only. |
127
|
|
|
* |
128
|
|
|
* @return array |
129
|
|
|
*/ |
130
|
|
|
public function first(): array |
131
|
|
|
{ |
132
|
|
|
$result = $this->get(); |
133
|
|
|
return reset($result); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Get first given field value of first record only. |
138
|
|
|
* |
139
|
|
|
* @param string $field |
140
|
|
|
* @return mixed |
141
|
|
|
*/ |
142
|
|
|
public function value(string $field) |
143
|
|
|
{ |
144
|
|
|
$result = $this->get(); |
145
|
|
|
return reset($result)[$field]; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Plucka n array of values. |
150
|
|
|
* |
151
|
|
|
* @param mixed $value |
152
|
|
|
* @param mixed|null $key |
153
|
|
|
* @return array |
154
|
|
|
*/ |
155
|
|
|
public function pluck($value, $key = null): array |
156
|
|
|
{ |
157
|
|
|
$results = []; |
158
|
|
|
foreach ($this->get() as $item) { |
159
|
|
|
$itemValue = $item[$value]; |
160
|
|
|
if (is_null($key)) { |
161
|
|
|
$results[] = $itemValue; |
162
|
|
|
} else { |
163
|
|
|
$itemKey = $item[$key]; |
164
|
|
|
$results[$itemKey] = $itemValue; |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
return $results; |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.