Completed
Push — master ( 411481...177b43 )
by Arjay
01:34
created

CollectionDataTable::setOffset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Yajra\DataTables;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
use Illuminate\Support\Collection;
8
use Illuminate\Contracts\Support\Arrayable;
9
10
class CollectionDataTable extends DataTableAbstract
11
{
12
    /**
13
     * Collection object.
14
     *
15
     * @var \Illuminate\Support\Collection
16
     */
17
    public $collection;
18
19
    /**
20
     * Collection object.
21
     *
22
     * @var \Illuminate\Support\Collection
23
     */
24
    public $original;
25
26
    /**
27
     * The offset of the first record in the full dataset.
28
     *
29
     * @var int
30
     */
31
    private $offset = 0;
32
33
    /**
34
     * Can the DataTable engine be created with these parameters.
35
     *
36
     * @param mixed $source
37
     * @return bool
38
     */
39
    public static function canCreate($source)
40
    {
41
        return is_array($source) || $source instanceof Collection;
0 ignored issues
show
Bug introduced by
The class Illuminate\Support\Collection does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
42
    }
43
44
    /**
45
     * Factory method, create and return an instance for the DataTable engine.
46
     *
47
     * @param array|\Illuminate\Support\Collection $source
48
     * @return CollectionDataTable|DataTableAbstract
49
     */
50
    public static function create($source)
51
    {
52
        if (is_array($source)) {
53
            $source = new Collection($source);
54
        }
55
56
        return parent::create($source);
57
    }
58
59
    /**
60
     * CollectionEngine constructor.
61
     *
62
     * @param \Illuminate\Support\Collection $collection
63
     */
64
    public function __construct(Collection $collection)
65
    {
66
        $this->request    = app('datatables.request');
67
        $this->config     = app('datatables.config');
68
        $this->collection = $collection;
69
        $this->original   = $collection;
70
        $this->columns    = array_keys($this->serialize($collection->first()));
71
    }
72
73
    /**
74
     * Serialize collection.
75
     *
76
     * @param  mixed $collection
77
     * @return mixed|null
78
     */
79
    protected function serialize($collection)
80
    {
81
        return $collection instanceof Arrayable ? $collection->toArray() : (array) $collection;
0 ignored issues
show
Bug introduced by
The class Illuminate\Contracts\Support\Arrayable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
82
    }
83
84
    /**
85
     * Count results.
86
     *
87
     * @return int
88
     */
89
    public function count()
90
    {
91
        return $this->collection->count() > $this->totalRecords ? $this->totalRecords : $this->collection->count();
92
    }
93
94
    /**
95
     * Perform column search.
96
     *
97
     * @return void
98
     */
99
    public function columnSearch()
100
    {
101
        $columns = $this->request->get('columns', []);
102
        for ($i = 0, $c = count($columns); $i < $c; $i++) {
103
            $column  = $this->getColumnName($i);
104
105
            if (! $this->request->isColumnSearchable($i) || $this->isBlacklisted($column)) {
106
                continue;
107
            }
108
109
            $this->isFilterApplied = true;
110
111
            $regex   = $this->request->isRegex($i);
112
            $keyword = $this->request->columnKeyword($i);
113
114
            $this->collection = $this->collection->filter(
115
                function ($row) use ($column, $keyword, $regex) {
116
                    $data = $this->serialize($row);
117
118
                    $value = Arr::get($data, $column);
119
120
                    if ($this->config->isCaseInsensitive()) {
121
                        if ($regex) {
122
                            return preg_match('/' . $keyword . '/i', $value) == 1;
123
                        }
124
125
                        return strpos(Str::lower($value), Str::lower($keyword)) !== false;
126
                    }
127
128
                    if ($regex) {
129
                        return preg_match('/' . $keyword . '/', $value) == 1;
130
                    }
131
132
                    return strpos($value, $keyword) !== false;
133
                }
134
            );
135
        }
136
    }
137
138
    /**
139
     * Perform pagination.
140
     *
141
     * @return void
142
     */
143
    public function paging()
144
    {
145
        $this->collection = $this->collection->slice(
146
            $this->request->input('start') - $this->offset,
147
            (int) $this->request->input('length') > 0 ? $this->request->input('length') : 10
148
        );
149
    }
150
151
    /**
152
     * Organizes works.
153
     *
154
     * @param bool $mDataSupport
155
     * @return \Illuminate\Http\JsonResponse
156
     */
157
    public function make($mDataSupport = true)
158
    {
159
        try {
160
            $this->totalRecords = $this->totalCount();
161
162
            if ($this->totalRecords) {
163
                $results   = $this->results();
164
                $processed = $this->processResults($results, $mDataSupport);
165
                $output    = $this->transform($results, $processed);
166
167
                $this->collection = collect($output);
168
                $this->ordering();
169
                $this->filterRecords();
170
                $this->paginate();
171
172
                $this->revertIndexColumn($mDataSupport);
173
            }
174
175
            return $this->render($this->collection->values()->all());
176
        } catch (\Exception $exception) {
177
            return $this->errorResponse($exception);
178
        }
179
    }
180
181
    /**
182
     * Count total items.
183
     *
184
     * @return int
185
     */
186
    public function totalCount()
187
    {
188
        return $this->totalRecords ? $this->totalRecords : $this->collection->count();
189
    }
190
191
    /**
192
     * Get results.
193
     *
194
     * @return mixed
195
     */
196
    public function results()
197
    {
198
        return $this->collection->all();
199
    }
200
201
    /**
202
     * Revert transformed DT_RowIndex back to it's original values.
203
     *
204
     * @param bool $mDataSupport
205
     */
206
    private function revertIndexColumn($mDataSupport)
207
    {
208
        if ($this->columnDef['index']) {
209
            $index = $mDataSupport ? config('datatables.index_column', 'DT_RowIndex') : 0;
210
            $start = (int) $this->request->input('start');
211
            $this->collection->transform(function ($data) use ($index, &$start) {
212
                $data[$index] = ++$start;
213
214
                return $data;
215
            });
216
        }
217
    }
218
219
    /**
220
     * Perform global search for the given keyword.
221
     *
222
     * @param string $keyword
223
     */
224
    protected function globalSearch($keyword)
225
    {
226
        $keyword = $this->config->isCaseInsensitive() ? Str::lower($keyword) : $keyword;
227
228
        $this->collection = $this->collection->filter(function ($row) use ($keyword) {
229
            $this->isFilterApplied = true;
230
231
            $data = $this->serialize($row);
232
            foreach ($this->request->searchableColumnIndex() as $index) {
233
                $column = $this->getColumnName($index);
234
                $value = Arr::get($data, $column);
235
                if (! $value || is_array($value)) {
236
                    if (! is_numeric($value)) {
237
                        continue;
238
                    }
239
240
                    $value = (string) $value;
241
                }
242
243
                $value = $this->config->isCaseInsensitive() ? Str::lower($value) : $value;
244
                if (Str::contains($value, $keyword)) {
245
                    return true;
246
                }
247
            }
248
249
            return false;
250
        });
251
    }
252
253
    /**
254
     * Perform default query orderBy clause.
255
     */
256
    protected function defaultOrdering()
257
    {
258
        $criteria = $this->request->orderableColumns();
259
        if (! empty($criteria)) {
260
            $sorter = $this->getSorter($criteria);
261
262
            $this->collection = $this->collection
263
                ->map(function ($data) {
264
                    return array_dot($data);
265
                })
266
                ->sort($sorter)
267
                ->map(function ($data) {
268
                    foreach ($data as $key => $value) {
269
                        unset($data[$key]);
270
                        array_set($data, $key, $value);
271
                    }
272
273
                    return $data;
274
                });
275
        }
276
    }
277
278
    /**
279
     * Get array sorter closure.
280
     *
281
     * @param array $criteria
282
     * @return \Closure
283
     */
284
    protected function getSorter(array $criteria)
285
    {
286
        $sorter = function ($a, $b) use ($criteria) {
287
            foreach ($criteria as $orderable) {
288
                $column    = $this->getColumnName($orderable['column']);
289
                $direction = $orderable['direction'];
290
                if ($direction === 'desc') {
291
                    $first  = $b;
292
                    $second = $a;
293
                } else {
294
                    $first  = $a;
295
                    $second = $b;
296
                }
297
                if ($this->config->isCaseInsensitive()) {
298
                    $cmp = strnatcasecmp($first[$column], $second[$column]);
299
                } else {
300
                    $cmp = strnatcmp($first[$column], $second[$column]);
301
                }
302
                if ($cmp != 0) {
303
                    return $cmp;
304
                }
305
            }
306
307
            // all elements were equal
308
            return 0;
309
        };
310
311
        return $sorter;
312
    }
313
314
    /**
315
     * Resolve callback parameter instance.
316
     *
317
     * @return $this
318
     */
319
    protected function resolveCallbackParameter()
320
    {
321
        return $this;
322
    }
323
324
    /**
325
     * Define the offset of the first item of the collection with respect to
326
     * the FULL dataset the collection was sliced from. It effectively allows the
327
     * collection to be "pre-sliced".
328
     *
329
     * @param int $offset
330
     * @return $this
331
     */
332
    public function setOffset(int $offset)
333
    {
334
        $this->offset = $offset;
335
336
        return $this;
337
    }
338
}
339