Completed
Pull Request — master (#1488)
by Elf
01:39
created

CollectionDataTable   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 279
Duplicated Lines 4.3 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 12
loc 279
rs 8.2608
c 0
b 0
f 0
wmc 40
lcom 1
cbo 7

13 Methods

Rating   Name   Duplication   Size   Complexity  
A serialize() 0 4 2
A count() 0 4 2
B columnSearch() 12 35 6
A paging() 0 7 2
A make() 0 23 3
A totalCount() 0 4 2
A results() 0 4 1
A revertIndexColumn() 0 12 3
A __construct() 0 8 2
C globalSearch() 0 29 8
A defaultOrdering() 0 21 3
B getSorter() 0 29 5
A resolveCallbackParameter() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CollectionDataTable often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CollectionDataTable, and based on these observations, apply Extract Interface, too.

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
     * The original source.
21
     *
22
     * @var mixed
23
     */
24
    public $original;
25
26
    /**
27
     * CollectionDataTable constructor.
28
     *
29
     * @param mixed $source
30
     */
31
    public function __construct($source)
32
    {
33
        $this->request    = app('datatables.request');
34
        $this->config     = app('datatables.config');
35
        $this->original   = $source;
36
        $this->collection = $source instanceof Collection ? $source : new Collection($source);
37
        $this->columns    = array_keys($this->serialize($this->collection->first()));
38
    }
39
40
    /**
41
     * Serialize collection.
42
     *
43
     * @param  mixed $collection
44
     * @return mixed|null
45
     */
46
    protected function serialize($collection)
47
    {
48
        return $collection instanceof Arrayable ? $collection->toArray() : (array) $collection;
49
    }
50
51
    /**
52
     * Count results.
53
     *
54
     * @return int
55
     */
56
    public function count()
57
    {
58
        return $this->collection->count() > $this->totalRecords ? $this->totalRecords : $this->collection->count();
59
    }
60
61
    /**
62
     * Perform column search.
63
     *
64
     * @return void
65
     */
66
    public function columnSearch()
67
    {
68
        $columns = $this->request->get('columns', []);
69
        for ($i = 0, $c = count($columns); $i < $c; $i++) {
70
            if ($this->request->isColumnSearchable($i)) {
71
                $this->isFilterApplied = true;
72
73
                $regex   = $this->request->isRegex($i);
74
                $column  = $this->getColumnName($i);
75
                $keyword = $this->request->columnKeyword($i);
76
77
                $this->collection = $this->collection->filter(
78
                    function ($row) use ($column, $keyword, $regex) {
79
                        $data = $this->serialize($row);
80
81
                        $value = Arr::get($data, $column);
82
83
                        if ($this->config->isCaseInsensitive()) {
84 View Code Duplication
                            if ($regex) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
85
                                return preg_match('/' . $keyword . '/i', $value) == 1;
86
                            } else {
87
                                return strpos(Str::lower($value), Str::lower($keyword)) !== false;
88
                            }
89 View Code Duplication
                        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

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