Completed
Push — master ( 01f863...e04cdd )
by Arjay
11:27
created

CollectionEngine::globalSearch()   C

Complexity

Conditions 7
Paths 2

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 19
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 35
rs 6.7272
1
<?php
2
3
namespace Yajra\Datatables\Engines;
4
5
use Closure;
6
use Illuminate\Contracts\Support\Arrayable;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Str;
10
use Yajra\Datatables\Request;
11
12
/**
13
 * Class CollectionEngine.
14
 *
15
 * @package Yajra\Datatables\Engines
16
 * @author  Arjay Angeles <[email protected]>
17
 */
18
class CollectionEngine extends BaseEngine
19
{
20
    /**
21
     * Collection object
22
     *
23
     * @var \Illuminate\Support\Collection
24
     */
25
    public $collection;
26
27
    /**
28
     * Collection object
29
     *
30
     * @var \Illuminate\Support\Collection
31
     */
32
    public $original_collection;
33
34
    /**
35
     * CollectionEngine constructor.
36
     *
37
     * @param \Illuminate\Support\Collection $collection
38
     * @param \Yajra\Datatables\Request $request
39
     */
40
    public function __construct(Collection $collection, Request $request)
41
    {
42
        $this->request             = $request;
43
        $this->collection          = $collection;
44
        $this->original_collection = $collection;
45
        $this->columns             = array_keys($this->serialize($collection->first()));
46
    }
47
48
    /**
49
     * Serialize collection
50
     *
51
     * @param  mixed $collection
52
     * @return mixed|null
53
     */
54
    protected function serialize($collection)
55
    {
56
        return $collection instanceof Arrayable ? $collection->toArray() : (array) $collection;
57
    }
58
59
    /**
60
     * Set auto filter off and run your own filter.
61
     * Overrides global search.
62
     *
63
     * @param \Closure $callback
64
     * @param bool $globalSearch
65
     * @return $this
66
     */
67
    public function filter(Closure $callback, $globalSearch = false)
68
    {
69
        $this->overrideGlobalSearch($callback, $this, $globalSearch);
70
71
        return $this;
72
    }
73
74
    /**
75
     * Append debug parameters on output.
76
     *
77
     * @param  array $output
78
     * @return array
79
     */
80
    public function showDebugger(array $output)
81
    {
82
        $output["input"] = $this->request->all();
0 ignored issues
show
Documentation Bug introduced by
The method all does not exist on object<Yajra\Datatables\Request>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
83
84
        return $output;
85
    }
86
87
    /**
88
     * Count total items.
89
     *
90
     * @return integer
91
     */
92
    public function totalCount()
93
    {
94
        return $this->totalRecords ? $this->totalRecords : $this->collection->count();
95
    }
96
97
    /**
98
     * Count results.
99
     *
100
     * @return integer
101
     */
102
    public function count()
103
    {
104
        return $this->collection->count() > $this->totalRecords ? $this->totalRecords : $this->collection->count();
105
    }
106
107
    /**
108
     * Perform sorting of columns.
109
     *
110
     * @return void
111
     */
112
    public function ordering()
113
    {
114
        if ($this->orderCallback) {
115
            call_user_func($this->orderCallback, $this);
116
117
            return;
118
        }
119
120
        foreach ($this->request->orderableColumns() as $orderable) {
121
            $column = $this->getColumnName($orderable['column']);
122
123
            $options = SORT_NATURAL;
124
            if ($this->isCaseInsensitive()) {
125
                $options = SORT_NATURAL | SORT_FLAG_CASE;
126
            }
127
128
            $this->collection = $this->collection->sortBy(function ($row) use ($column) {
129
                $data = $this->serialize($row);
130
131
                return Arr::get($data, $column);
132
            }, $options);
133
134
            if ($orderable['direction'] == 'desc') {
135
                $this->collection = $this->collection->reverse();
136
            }
137
        }
138
    }
139
140
    /**
141
     * Perform global search.
142
     *
143
     * @return void
144
     */
145
    public function filtering()
146
    {
147
        $keyword = $this->request->keyword();
148
149
        if ($this->isSmartSearch()) {
150
            $this->smartGlobalSearch($keyword);
0 ignored issues
show
Bug introduced by
It seems like $keyword defined by $this->request->keyword() on line 147 can also be of type array; however, Yajra\Datatables\Engines...ne::smartGlobalSearch() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
151
152
            return;
153
        }
154
155
        $this->globalSearch($keyword);
0 ignored issues
show
Bug introduced by
It seems like $keyword defined by $this->request->keyword() on line 147 can also be of type array; however, Yajra\Datatables\Engines...nEngine::globalSearch() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
156
    }
157
158
    /**
159
     * Perform multi-term search by splitting keyword into
160
     * individual words and searches for each of them.
161
     *
162
     * @param string $keyword
163
     */
164
    private function smartGlobalSearch($keyword)
165
    {
166
        $keywords = array_filter(explode(' ', $keyword));
167
168
        foreach ($keywords as $keyword) {
169
            $this->globalSearch($keyword);
170
        }
171
    }
172
173
    /**
174
     * Perform global search for the given keyword.
175
     *
176
     * @param string $keyword
177
     */
178
    private function globalSearch($keyword)
179
    {
180
        if ($this->isCaseInsensitive()) {
181
            $keyword = Str::lower($keyword);
182
        }
183
184
        $columns          = $this->request->columns();
185
        $this->collection = $this->collection->filter(
186
            function ($row) use ($columns, $keyword) {
187
                $data                  = $this->serialize($row);
188
                $this->isFilterApplied = true;
189
190
                foreach ($this->request->searchableColumnIndex() as $index) {
191
                    $column = $this->getColumnName($index);
192
                    if (! $value = Arr::get($data, $column)) {
193
                        continue;
194
                    }
195
                    
196
                    if (is_array($value)) {
197
                        continue;
198
                    }
199
                    
200
                    if ($this->isCaseInsensitive()) {
201
                        $value = Str::lower($value);
202
                    }
203
204
                    if (Str::contains($value, $keyword)) {
205
                        return true;
206
                    }
207
                }
208
209
                return false;
210
            }
211
        );
212
    }
213
214
    /**
215
     * Perform column search.
216
     *
217
     * @return void
218
     */
219
    public function columnSearch()
220
    {
221
        $columns = $this->request->get('columns');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Yajra\Datatables\Request>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
222
        for ($i = 0, $c = count($columns); $i < $c; $i++) {
223
            if ($this->request->isColumnSearchable($i)) {
224
                $this->isFilterApplied = true;
225
                $regex                 = $this->request->isRegex($i);
226
227
                $column  = $this->getColumnName($i);
228
                $keyword = $this->request->columnKeyword($i);
229
230
                $this->collection = $this->collection->filter(
231
                    function ($row) use ($column, $keyword, $regex) {
232
                        $data = $this->serialize($row);
233
234
                        $value = Arr::get($data, $column);
235
236
                        if ($this->isCaseInsensitive()) {
237 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...
238
                                return preg_match('/' . $keyword . '/i', $value) == 1;
239
                            } else {
240
                                return strpos(Str::lower($value), Str::lower($keyword)) !== false;
241
                            }
242 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...
243
                            if ($regex) {
244
                                return preg_match('/' . $keyword . '/', $value) == 1;
245
                            } else {
246
                                return strpos($value, $keyword) !== false;
247
                            }
248
                        }
249
                    }
250
                );
251
            }
252
        }
253
    }
254
255
    /**
256
     * Perform pagination.
257
     *
258
     * @return void
259
     */
260
    public function paging()
261
    {
262
        $this->collection = $this->collection->slice(
263
            $this->request->input('start'),
264
            (int) $this->request->input('length') > 0 ? $this->request->input('length') : 10
265
        );
266
    }
267
268
    /**
269
     * Get results.
270
     *
271
     * @return mixed
272
     */
273
    public function results()
274
    {
275
        return $this->collection->all();
276
    }
277
278
    /**
279
     * Organizes works.
280
     *
281
     * @param bool $mDataSupport
282
     * @param bool $orderFirst
283
     * @return \Illuminate\Http\JsonResponse
284
     */
285
    public function make($mDataSupport = false, $orderFirst = true)
286
    {
287
        return parent::make($mDataSupport, $orderFirst);
288
    }
289
}
290