• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

contributte / datagrid / 15346172910

30 May 2025 11:52AM UTC coverage: 35.492% (+1.4%) from 34.086%
15346172910

push

github

f3l1x
Fixed "Attempt to read property on array" error in ArrayDataSource

3 of 4 new or added lines in 1 file covered. (75.0%)

34 existing lines in 4 files now uncovered.

1200 of 3381 relevant lines covered (35.49%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.52
/src/DataSource/ArrayDataSource.php
1
<?php declare(strict_types = 1);
2

3
namespace Contributte\Datagrid\DataSource;
4

5
use ArrayAccess;
6
use Contributte\Datagrid\Exception\DatagridArrayDataSourceException;
7
use Contributte\Datagrid\Exception\DatagridDateTimeHelperException;
8
use Contributte\Datagrid\Filter\Filter;
9
use Contributte\Datagrid\Filter\FilterDate;
10
use Contributte\Datagrid\Filter\FilterDateRange;
11
use Contributte\Datagrid\Filter\FilterMultiSelect;
12
use Contributte\Datagrid\Filter\FilterRange;
13
use Contributte\Datagrid\Filter\FilterSelect;
14
use Contributte\Datagrid\Filter\FilterText;
15
use Contributte\Datagrid\Utils\DateTimeHelper;
16
use Contributte\Datagrid\Utils\Sorting;
17
use DateTime;
18
use DateTimeInterface;
19
use Nette\Utils\Strings;
20

21
class ArrayDataSource implements IDataSource
22
{
23

24
        protected array $data = [];
25

26
        protected int $count = 0;
27

28
        public function __construct(array $dataSource)
1✔
29
        {
30
                $this->setData($dataSource);
1✔
31
        }
1✔
32

33
        public function getCount(): int
34
        {
35
                return count($this->data);
1✔
36
        }
37

38
        /**
39
         * {@inheritDoc}
40
         */
41
        public function getData(): array
42
        {
43
                return $this->data;
1✔
44
        }
45

46
        /**
47
         * {@inheritDoc}
48
         */
49
        public function filter(array $filters): void
1✔
50
        {
51
                foreach ($filters as $filter) {
1✔
52
                        if ($filter->isValueSet()) {
1✔
53
                                if ($filter->getConditionCallback() !== null) {
1✔
54
                                        $data = (array) call_user_func_array(
×
55
                                                $filter->getConditionCallback(),
×
56
                                                [$this->data, $filter->getValue()]
×
57
                                        );
58
                                        $this->setData($data);
×
59
                                } else {
60
                                        $data = array_filter($this->data, fn ($row) => $this->applyFilter($row, $filter));
1✔
61
                                        $this->setData($data);
1✔
62
                                }
63
                        }
64
                }
65
        }
1✔
66

67
        /**
68
         * {@inheritDoc}
69
         */
70
        public function filterOne(array $condition): IDataSource
1✔
71
        {
72
                foreach ($this->data as $item) {
1✔
73
                        foreach ($condition as $key => $value) {
1✔
74
                                if ($item[$key] === $value) {
1✔
75
                                        $this->setData([$item]);
1✔
76

77
                                        return $this;
1✔
78
                                }
79
                        }
80
                }
81

82
                $this->setData([]);
×
83

84
                return $this;
×
85
        }
86

87
        public function limit(int $offset, int $limit): IDataSource
1✔
88
        {
89
                $data = array_slice($this->data, $offset, $limit);
1✔
90
                $this->setData($data);
1✔
91

92
                return $this;
1✔
93
        }
94

95
        public function sort(Sorting $sorting): IDataSource
1✔
96
        {
97
                if (is_callable($sorting->getSortCallback())) {
1✔
98
                        $data = call_user_func(
×
99
                                $sorting->getSortCallback(),
×
100
                                $this->data,
×
101
                                $sorting->getSort()
×
102
                        );
103

104
                        if (!is_array($data)) {
×
105
                                throw new DatagridArrayDataSourceException('Sorting callback has to return array');
×
106
                        }
107

108
                        $this->setData($data);
×
109

110
                        return $this;
×
111
                }
112

113
                $sort = $sorting->getSort();
1✔
114

115
                foreach ($sort as $column => $order) {
1✔
116
                        $data = [];
1✔
117

118
                        foreach ($this->data as $item) {
1✔
119
                                $value = is_object($item) ? $item->$column : $item[$column];
1✔
120
                                if ($value instanceof \DateTimeInterface) {
1✔
NEW
121
                                        $sort_by = $value->format('Y-m-d H:i:s');
×
122
                                } else {
123
                                        $sort_by = (string) $value;
1✔
124
                                }
125

126
                                $data[$sort_by][] = $item;
1✔
127
                        }
128

129
                        if ($order === 'ASC') {
1✔
UNCOV
130
                                ksort($data, SORT_LOCALE_STRING);
×
131
                        } else {
132
                                krsort($data, SORT_LOCALE_STRING);
1✔
133
                        }
134

135
                        $dataSource = [];
1✔
136

137
                        foreach ($data as $i) {
1✔
138
                                foreach ($i as $item) {
1✔
139
                                        $dataSource[] = $item;
1✔
140
                                }
141
                        }
142

143
                        $this->setData($dataSource);
1✔
144
                }
145

146
                return $this;
1✔
147
        }
148

149
        protected function applyFilter(mixed $row, Filter $filter): mixed
1✔
150
        {
151
                if (is_array($row) || $row instanceof ArrayAccess) {
1✔
152
                        if ($filter instanceof FilterDate) {
1✔
153
                                return $this->applyFilterDate($row, $filter);
×
154
                        }
155

156
                        if ($filter instanceof FilterMultiSelect) {
1✔
157
                                return $this->applyFilterMultiSelect($row, $filter);
×
158
                        }
159

160
                        if ($filter instanceof FilterDateRange) {
1✔
UNCOV
161
                                return $this->applyFilterDateRange($row, $filter);
×
162
                        }
163

164
                        if ($filter instanceof FilterRange) {
1✔
165
                                return $this->applyFilterRange($row, $filter);
1✔
166
                        }
167

168
                        $condition = $filter->getCondition();
1✔
169

170
                        foreach ($condition as $column => $value) {
1✔
171
                                $value = (string) $value;
1✔
172
                                $rowVal = (string) $row[$column];
1✔
173

174
                                if ($filter instanceof FilterSelect) {
1✔
175
                                        return $rowVal === $value;
1✔
176
                                }
177

178
                                if ($filter instanceof FilterText && $filter->isExactSearch()) {
1✔
179
                                        return $rowVal === $value;
1✔
180
                                }
181

182
                                $words = $filter instanceof FilterText && $filter->hasSplitWordsSearch() === false ? [$value] : explode(' ', $value);
1✔
183

184
                                $row_value = strtolower(Strings::toAscii((string) $row[$column]));
1✔
185

186
                                $found = [];
1✔
187

188
                                foreach ($words as $word) {
1✔
189
                                        if (str_contains($row_value, strtolower(Strings::toAscii($word)))) {
1✔
190
                                                if ($filter instanceof FilterText && !$filter->hasConjunctionSearch()) {
1✔
191
                                                        return $row;
1✔
192
                                                } else {
193
                                                        $found[] = true;
1✔
194
                                                }
195
                                        }
196
                                }
197

198
                                if (count($found) === count($words)) {
1✔
199
                                        return $row;
1✔
200
                                }
201
                        }
202
                }
203

204
                return false;
1✔
205
        }
206

207
        protected function applyFilterMultiSelect(mixed $row, FilterMultiSelect $filter): bool
208
        {
UNCOV
209
                $condition = $filter->getCondition();
×
UNCOV
210
                $values = $condition[$filter->getColumn()];
×
211

UNCOV
212
                return in_array($row[$filter->getColumn()], $values, true);
×
213
        }
214

215
        protected function applyFilterRange(mixed $row, FilterRange $filter): bool
1✔
216
        {
217
                $condition = $filter->getCondition();
1✔
218
                $values = $condition[$filter->getColumn()];
1✔
219

220
                if ($values['from'] !== null && $values['from'] !== '') {
1✔
221
                        if ($values['from'] > $row[$filter->getColumn()]) {
1✔
222
                                return false;
1✔
223
                        }
224
                }
225

226
                if ($values['to'] !== null && $values['to'] !== '') {
1✔
227
                        if ($values['to'] < $row[$filter->getColumn()]) {
1✔
228
                                return false;
1✔
229
                        }
230
                }
231

232
                return true;
1✔
233
        }
234

235
        protected function applyFilterDateRange(mixed $row, FilterDateRange $filter): bool
236
        {
237
                $format = $filter->getPhpFormat();
×
238
                $condition = $filter->getCondition();
×
UNCOV
239
                $values = $condition[$filter->getColumn()];
×
UNCOV
240
                $row_value = $row[$filter->getColumn()];
×
241

242
                if ($values['from'] !== null && $values['from'] !== '') {
×
UNCOV
243
                        $date_from = DateTimeHelper::tryConvertToDate($values['from'], [$format]);
×
UNCOV
244
                        $date_from->setTime(0, 0, 0);
×
245

246
                        if (!($row_value instanceof DateTime)) {
×
247
                                /**
248
                                 * Try to convert string to DateTime object
249
                                 */
250
                                try {
251
                                        $row_value = DateTimeHelper::tryConvertToDate($row_value);
×
252
                                } catch (DatagridDateTimeHelperException) {
×
253
                                        /**
254
                                         * Otherwise just return raw string
255
                                         */
UNCOV
256
                                        return false;
×
257
                                }
258
                        }
259

260
                        if ($row_value->getTimestamp() < $date_from->getTimestamp()) {
×
261
                                return false;
×
262
                        }
263
                }
264

265
                if ($values['to'] !== null && $values['to'] !== '') {
×
UNCOV
266
                        $date_to = DateTimeHelper::tryConvertToDate($values['to'], [$format]);
×
UNCOV
267
                        $date_to->setTime(23, 59, 59);
×
268

269
                        if (!($row_value instanceof DateTime)) {
×
270
                                /**
271
                                 * Try to convert string to DateTime object
272
                                 */
273
                                try {
274
                                        $row_value = DateTimeHelper::tryConvertToDate($row_value);
×
UNCOV
275
                                } catch (DatagridDateTimeHelperException) {
×
276
                                        /**
277
                                         * Otherwise just return raw string
278
                                         */
UNCOV
279
                                        return false;
×
280
                                }
281
                        }
282

283
                        if ($row_value->getTimestamp() > $date_to->getTimestamp()) {
×
UNCOV
284
                                return false;
×
285
                        }
286
                }
287

288
                return true;
×
289
        }
290

291
        /**
292
         * Apply fitler date and tell whether row value matches or not
293
         */
294
        protected function applyFilterDate(mixed $row, FilterDate $filter): bool
295
        {
296
                $format = $filter->getPhpFormat();
×
UNCOV
297
                $condition = $filter->getCondition();
×
298

UNCOV
299
                foreach ($condition as $column => $value) {
×
300
                        $row_value = $row[$column];
×
301

UNCOV
302
                        $date = DateTimeHelper::tryConvertToDateTime($value, [$format]);
×
303

304
                        if (!($row_value instanceof DateTime)) {
×
305
                                /**
306
                                 * Try to convert string to DateTime object
307
                                 */
308
                                try {
UNCOV
309
                                        $row_value = DateTimeHelper::tryConvertToDateTime($row_value);
×
UNCOV
310
                                } catch (DatagridDateTimeHelperException) {
×
311
                                        /**
312
                                         * Otherwise just return raw string
313
                                         */
UNCOV
314
                                        return false;
×
315
                                }
316
                        }
317

UNCOV
318
                        return $row_value->format($format) === $date->format($format);
×
319
                }
320

UNCOV
321
                return false;
×
322
        }
323

324
        /**
325
         * Set the data
326
         */
327
        private function setData(array $dataSource): IDataSource
1✔
328
        {
329
                $this->data = $dataSource;
1✔
330

331
                return $this;
1✔
332
        }
333

334
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc