Passed
Pull Request — master (#136)
by Zing
05:46
created

WithSearchable::searchable()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 8
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 5
rs 9.6111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Zing\QueryBuilder\Concerns;
6
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Support\Str;
9
use Zing\QueryBuilder\Exceptions\ParameterException;
10
use Zing\QueryBuilder\Filter;
11
12
trait WithSearchable
13
{
14
    use NestedRelation;
15
16
    /**
17
     * @param string|\Zing\QueryBuilder\Filter|array<(string|\Zing\QueryBuilder\Filter)> $searchable
18
     *
19
     * @return $this
20
     */
21 5
    public function searchable($searchable)
22
    {
23 5
        $searchable = is_array($searchable) ? $searchable : func_get_args();
24 5
        $search = $this->request->input('search');
25 5
        if ($search === null) {
26 1
            return $this;
27
        }
28
29 4
        if (is_string($search) && trim($search) === '') {
30 1
            return $this;
31
        }
32
33 3
        $searchable = $this->resolveNestedSearchable($searchable);
34
35 3
        return $this->applySearchable($search, $searchable);
36
    }
37
38
    /**
39
     * @param mixed $search
40
     * @param array<(int|string), (string|array<string>|\Zing\QueryBuilder\Filter)> $searchable
41
     *
42
     * @return $this
43
     */
44 3
    protected function applySearchable($search, array $searchable = [])
45
    {
46 3
        $this->where(
0 ignored issues
show
Bug introduced by
It seems like where() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

46
        $this->/** @scrutinizer ignore-call */ 
47
               where(
Loading history...
47 3
            function (Builder $query) use ($search, $searchable): void {
48 3
                collect($searchable)->each(
49 3
                    function ($value, $key) use ($query, $search): void {
50 3
                        if ($value instanceof Filter) {
51 1
                            $query->orWhere(function ($query) use ($value, $search): void {
52 1
                                $value->filter($query, $search);
53 1
                            });
54
55 1
                            return;
56
                        }
57
58 3
                        if (is_numeric($key)) {
59 2
                            $query->orWhere($value, 'like', sprintf('%%%s%%', $search));
60
61 2
                            return;
62
                        }
63
64 1
                        $this->applyRelationSearchable($query, $key, (array) $value, $search);
65 3
                    }
66
                );
67 3
            }
68
        );
69
70 3
        return $this;
71
    }
72
73
    /**
74
     * @param array<string> $fields
75
     * @param mixed $search
76
     */
77 1
    protected function applyRelationSearchable(Builder $query, string $relation, array $fields, $search): Builder
78
    {
79 1
        return $query->orWhereHas(
80 1
            $relation,
81 1
            function (Builder $query) use ($fields, $search): void {
82 1
                $query->where(
83 1
                    function (Builder $query) use ($fields, $search): void {
84 1
                        foreach ($fields as $field) {
85 1
                            $query->orWhere($field, 'like', sprintf('%%%s%%', $search));
86
                        }
87 1
                    }
88
                );
89 1
            }
90
        );
91
    }
92
93
    /**
94
     * @param array<(string|\Zing\QueryBuilder\Filter)> $searchable
95
     *
96
     * @return array<(int|string), (string|array<string>|\Zing\QueryBuilder\Filter)>
97
     */
98 3
    private function resolveNestedSearchable(array $searchable)
99
    {
100 3
        $results = [];
101 3
        foreach ($searchable as $singleSearchable) {
102 3
            if ($this->isFilterWithDefault($singleSearchable)) {
103 1
                throw ParameterException::unsupportedFilterWithDefaultValueForSearch();
104
            }
105
106 3
            if ($this->isNestedRelation($singleSearchable)) {
107 1
                [$relation, $property] = $this->resolveNestedRelation($singleSearchable);
0 ignored issues
show
Bug introduced by
It seems like $singleSearchable can also be of type Zing\QueryBuilder\Filter; however, parameter $property of Zing\QueryBuilder\Concer...resolveNestedRelation() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

107
                [$relation, $property] = $this->resolveNestedRelation(/** @scrutinizer ignore-type */ $singleSearchable);
Loading history...
108
109 1
                $results[$relation][] = $property;
110 1
                continue;
111
            }
112
113 2
            $results[] = $singleSearchable;
114
        }
115
116 3
        return $results;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $results returns an array which contains values of type Zing\QueryBuilder\Filter which are incompatible with the documented value type string|string[]&Zing\QueryBuilder\Filter.
Loading history...
117
    }
118
119 3
    protected function isFilterWithDefault($value): bool
120
    {
121 3
     return $value instanceof Filter && $value->getDefault() !== null;
122
    }
123
124 3
    protected function isNestedRelation($value): bool
125
    {
126 3
        return ! $value instanceof Filter && Str::contains($value, '.');
127
    }
128
}
129