Passed
Push — master ( 3ed229...63a031 )
by Babak
02:33
created

QueryHelper::addWhereCondition()   C

Complexity

Conditions 16
Paths 20

Size

Total Lines 87
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 16
eloc 51
c 2
b 0
f 1
nc 20
nop 4
dl 0
loc 87
rs 5.5666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: alive
5
 * Date: 10/8/17
6
 * Time: 4:04 AM
7
 */
8
9
namespace Alive2212\LaravelQueryHelper;
10
11
use Illuminate\Database\Eloquent\Builder;
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\Builder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
13
class QueryHelper
14
{
15
    /**
16
     * filter Delimiter
17
     * @var
18
     */
19
    protected $filterDelimiter = '.';
20
21
    protected $orWhereConditionLevel = 1;
22
23
    /**
24
     * @var string
25
     */
26
    protected $queryFilterTitle = 'query_filters';
27
28
    /**
29
     * @param $query
30
     * @param $filters
31
     * @return mixed
32
     */
33
    public function orDeepFilter($query, $filters)
34
    {
35
        $result = $query;
36
        $keys = explode('.', $filters[0]['key']);
37
        if (collect($keys)->count() == 2) {
38
            $result = $result->orWhereHas($keys[0], function ($query) use ($filters, $keys) {
0 ignored issues
show
Unused Code introduced by
The import $keys is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
39
                foreach ($filters as $filter) {
40
                    $keys = explode('.', $filter['key']);
41
                    $query->where($keys[1], $filter['operator'], $filter['value']);
42
                }
43
            });
44
        }
45
        return $result;
46
    }
47
48
    /**
49
     * @param $query
50
     * @param $param
51
     * @return mixed
52
     */
53
    public function orderBy($query, $param)
54
    {
55
        $result = $query;
56
        if ($param->count()) {
57
            $query->orderBy($param[0], $param[1]);
58
        }
59
        return $result;
60
    }
61
62
    /**
63
     * @param $params
64
     * @param $field
65
     * @return array
66
     */
67
    public function getValueArray($params, $field)
68
    {
69
        $result = [];
70
        foreach ($params as $key => $item) {
71
            array_push($result, $item[$field]);
72
        }
73
        return $result;
74
    }
75
76
    /**
77
     * @param Builder $model
78
     * @param array $filters
79
     * @return Builder
80
     */
81
    public function smartDeepFilter(Builder $model, array $filters): Builder
82
    {
83
        $filters = $this->filterAdaptor($filters);
84
//        $model = $this->addCondition($model, $filters);
85
        $model = $this->addWhereCondition($model, $filters);
86
//        dd($model->toSql());
87
        return $model;
88
    }
89
90
    /**
91
     * @param array $filters
92
     * @param int $level
93
     * @param array $adaptedFilters
94
     * @return array
95
     */
96
    public function filterAdaptor(array $filters, $level = 0, $adaptedFilters = []): array
97
    {
98
        foreach ($filters as $filter) {
99
            if (is_array($filter[0])) {
100
                $result = $this->filterAdaptor($filter, $level + 1);
101
            } else {
102
                $result = $this->getHierarchyFilterKey($filter);
103
            }
104
            $key = $level % 2 == $this->orWhereConditionLevel ?
105
                'or_' . $this->queryFilterTitle :
106
                'and_' . $this->queryFilterTitle;
107
            $adaptedFilters = array_merge_recursive($adaptedFilters, [$key => $result]);
108
        }
109
        return $adaptedFilters;
110
    }
111
112
    /**
113
     * @param array $filters
114
     * @return array
115
     */
116
    public function filterWhereConditionAdaptor(array $filters): array
117
    {
118
        $result = [];
119
        foreach ($filters as $filter) {
120
            $result = array_merge_recursive($result, $this->getHierarchyFilterKey($filter));
121
        }
122
        return $result;
123
    }
124
125
    public function getHierarchyFilterKey(array $filter): array
126
    {
127
        $filterKeyParams = $this->getFilterKey($filter);
128
        $firstFilterKey = $filterKeyParams[0];
129
        if (count($filterKeyParams) > 1) {
130
            unset($filterKeyParams[0]);
131
            $filter[0] = implode($this->filterDelimiter, $filterKeyParams);
132
            $result[$firstFilterKey] = $this->getHierarchyFilterKey($filter);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
133
        } else {
134
            $firstFilterOperator = $this->getFilterOperator($filter);
135
            $firstFilterValue = $this->getFilterValue($filter);
136
            $result[$this->queryFilterTitle] = [
137
                [
138
                    $firstFilterKey,
139
                    $firstFilterOperator,
140
                    $firstFilterValue,
141
                ],
142
            ];
143
        }
144
        return $result;
145
    }
146
147
    /**
148
     * @param Builder $model
149
     * @param array $filters
150
     * @param string $type
151
     * @return Builder
152
     */
153
    public function addCondition(Builder $model, array $filters, string $type = ''): Builder
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

153
    public function addCondition(Builder $model, array $filters, /** @scrutinizer ignore-unused */ string $type = ''): Builder

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
154
    {
155
        foreach ($filters as $filterKey => $filterValues) {
156
            $currentFilter = [$filterKey => $filterValues];
157
            $model = $model->Where(function ($query) use ($currentFilter) {
158
                $this->addWhereCondition($query, $currentFilter, $this->orWhereConditionLevel == 0 ? 'and' : 'or');
159
            });
160
        }
161
        return $model;
162
    }
163
164
    /**
165
     * @param Builder $model
166
     * @param array $filters
167
     * @param string $type
168
     * @param int $index
169
     * @return Builder
170
     */
171
    public function addWhereCondition(Builder $model, array $filters, $type = 'and', $index = 0): Builder
172
    {
173
        // Get key and value
174
        $firstFilterKey = array_key_first($filters);
175
        if ($firstFilterKey === null) {
176
            return $model;
177
        }
178
        $firstFilterValue = $filters[$firstFilterKey];
179
180
        switch ($firstFilterKey) {
181
            // if final query
182
            case $this->queryFilterTitle:
183
184
                $firstFilterInnerKey = array_key_first($filters[$firstFilterKey]);
185
                if ($firstFilterInnerKey === null) {
186
                    return $model;
187
                }
188
189
                // if not first record and type is or
190
                if ($type == "or" && $index > 0) {
191
                    $model = $model->orWhere([$firstFilterValue[$firstFilterInnerKey]]);
192
                } else {
193
                    $model = $model->where([$firstFilterValue[$firstFilterInnerKey]]);
194
                }
195
196
                // Get Inner key and value
197
                if (count($filters[$firstFilterKey])===1) {
198
                    break;
199
//                    return $model;
200
                }
201
                unset($filters[$firstFilterKey][$firstFilterInnerKey]);
202
                return $this->addWhereCondition($model, $filters, $type, ++$index);
203
204
            // if and
205
            case 'and_' . $this->queryFilterTitle:
206
                // if not first record and type is or
207
                if ($type == "or" && $index > 0) {
208
                    $model = $model->orWhere(function (Builder $builder) use ($firstFilterValue) {
209
                        $builder = $this->addWhereCondition($builder, $firstFilterValue, 'and', 0);
210
                        return $builder;
211
                    });
212
                } else {
213
                    $model = $model->where(function (Builder $builder) use ($firstFilterValue) {
214
                        $builder = $this->addWhereCondition($builder, $firstFilterValue, 'and', 0);
215
                        return $builder;
216
                    });
217
                }
218
                break;
219
220
            // if or
221
            case 'or_' . $this->queryFilterTitle:
222
                // if not first record and type is or
223
                if ($type == "or" && $index > 0) {
224
                    $model = $model->orWhere(function (Builder $builder) use ($firstFilterValue) {
225
                        $builder = $this->addWhereCondition($builder, $firstFilterValue, 'or', 0);
226
                        return $builder;
227
                    });
228
                } else {
229
                    $model = $model->where(function (Builder $builder) use ($firstFilterValue) {
230
                        $builder = $this->addWhereCondition($builder, $firstFilterValue, 'or', 0);
231
                        return $builder;
232
                    });
233
                }
234
                break;
235
236
            // if relational
237
            default:
238
                // if not first record and type is or
239
                if ($type == "or" && $index > 0) {
240
                    $model = $model->orWhereHas($firstFilterKey, function (Builder $builder) use ($firstFilterValue) {
241
                        $builder = $this->addWhereCondition($builder, $firstFilterValue, 'or', 0);
242
                        return $builder;
243
                    });
244
                } else {
245
                    $model = $model->whereHas($firstFilterKey, function (Builder $builder) use ($firstFilterValue) {
246
                        $builder = $this->addWhereCondition($builder, $firstFilterValue, 'or', 0);
247
                        return $builder;
248
                    });
249
                }
250
                break;
251
        }
252
253
        unset($filters[$firstFilterKey]);
254
        if (count($filters)) {
255
            return $this->addWhereCondition($model, $filters, $type, ++$index);
256
        }
257
        return $model;
258
259
//        foreach ($filters as $filterKey => $filtersValues) {
260
//            if ($type == 'and') {
261
//                if ($filterKey == $this->queryFilterTitle) {
262
////                    dump('where(', $filtersValues,')');
263
//                    $model = $model->where($filtersValues);
264
//                } else {
265
////                    dump('whereHas('.$filterKey.'){', $filtersValues,'}');
266
//                    $model = $model->whereHas($filterKey, function (Builder $query) use ($model, $filtersValues) {
267
//                        $query = $this->addWhereCondition($query, $filtersValues);
268
//                    });
269
//                }
270
//            } elseif ($type == 'or') {
271
//                $index = 0;
272
//                foreach ($filtersValues as $filtersValue) {
273
//                    if ($filterKey == $this->queryFilterTitle) {
274
////                        dump('orWhere(', $filtersValue,')');
275
//                        $model = $index == 0 ?
276
//                            $model->where([$filtersValue]) :
277
//                            $model->orWhere([$filtersValue]);
278
//                    } else {
279
//                        $model = $index == 0 ?
280
//                            $model->whereHas($filterKey, function (Builder $query) use ($model, $filtersValues) {
281
//                                $query = $this->addWhereCondition($query, $filtersValues, 'or');
282
//                            }) :
283
//                            $model->orWhereHas($filterKey, function (Builder $query) use ($model, $filtersValues) {
284
//                                $query = $this->addWhereCondition($query, $filtersValues, 'or');
285
//                            });
286
//                    }
287
//                    $index += 1;
288
//                }
289
//            }
290
//        }
291
//        return $model;
292
    }
293
294
    /**
295
     * @return string
296
     */
297
    public function getFilterDelimiter(): string
298
    {
299
        return $this->filterDelimiter;
300
    }
301
302
    /**
303
     * @param $filterDelimiter
304
     * @return QueryHelper
305
     */
306
    public function setFilterDelimiter($filterDelimiter): QueryHelper
307
    {
308
        $this->filterDelimiter = $filterDelimiter;
309
        return $this;
310
    }
311
312
    /**
313
     * @param array $filter
314
     * @return array
315
     */
316
    public function getFilterKey(array $filter): array
317
    {
318
        if (array_key_exists('key', $filter)) {
319
            $result = explode($this->filterDelimiter, $filter['key']);
320
        } else {
321
            $result = explode($this->filterDelimiter, $filter[0]);
322
        }
323
        return $result;
324
    }
325
326
    /**
327
     * @param array $filter
328
     * @return string
329
     */
330
    public function getFilterOperator(array $filter): string
331
    {
332
        return array_key_exists('operator', $filter) ? $filter['operator'] : $filter[1];
333
    }
334
335
    /**
336
     * @param array $filter
337
     * @return string
338
     */
339
    public function getFilterValue(array $filter): string
340
    {
341
        return array_key_exists('value', $filter) ? $filter['value'] : $filter[2];
342
    }
343
344
    public function newSmartDeepFilter(Builder $model, array $filters, $level = 0): Builder
0 ignored issues
show
Unused Code introduced by
The parameter $filters is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

344
    public function newSmartDeepFilter(Builder $model, /** @scrutinizer ignore-unused */ array $filters, $level = 0): Builder

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
345
    {
346
        if ($level % 2 == $this->orWhereConditionLevel) {
347
348
        } else {
349
            dd('here');
350
        }
351
        return $model;
352
    }
353
}