Passed
Push — main ( 67fd11...cd2b2c )
by Mohammad
14:37
created

Criteriable::searchByCriteriaQueryPerColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Shamaseen\Repository\Utility\Models;
4
5
use Illuminate\Database\Eloquent\Builder;
6
7
/**
8
 * @method static Builder filterByCriteria(array $criteria)
9
 * @method static Builder searchByCriteria(array $criteria)
10
 * @method static Builder orderByCriteria(array $criteria)
11
 * @method static Builder setSearchables(array $searchables)
12
 * @method static Builder appendSearchables(array $searchables)
13
 * @method static Builder setFilterables(array $filterables)
14
 * @method static Builder appendFilterables(array $filterables)
15
 * @method static Builder setSortables(array $sortables)
16
 * @method static Builder appendSortables(array $sortables)
17
 */
18
trait Criteriable
19
{
20
    protected ?array $searchables = null;
21
    protected ?array $filterables = null;
22
    protected ?array $sortables = null;
23
    protected string $fullTextSearchMode = '';
24
25
    /**
26
     * @var array<array<string>>
27
     */
28
    protected ?array $fulltextSearch = [];
29
30
    public function getFilterKey()
31
    {
32
        return config('repository.filter_key');
33
    }
34
35
    protected function getFiltersFromCriteria(array $criteria): array
36
    {
37
        $filterKey = $this->getFilterKey();
38
39
        if ($filterKey) {
40
            return (array) ($criteria[$filterKey] ?? []);
41
        }
42
43
        return $criteria;
44
    }
45
46
    public function scopeFilterByCriteria($query, array $criteria): Builder
47
    {
48
        $requestFilters = $this->getFiltersFromCriteria($criteria);
49
50
        foreach ($this->getFilterables() as $method => $columns) {
51
            // if this is associative then it is a relation
52
            if ('string' === gettype($method)) {
53
                if (method_exists($this, $method) && array_key_exists($method, $requestFilters)) {
54
                    $query->whereHas($method, function ($query) use ($requestFilters, $columns, $method) {
55
                        /* @var $query Builder */
56
                        $query->where(function ($query2) use ($requestFilters, $columns, $method) {
57
                            /* @var $query2 Builder */
58
                            foreach ((array) $columns as $column) {
59
                                if (isset($requestFilters[$method][$column])) {
60
                                    $query2->where($column, $requestFilters[$method][$column]);
61
                                }
62
                            }
63
                        });
64
                    });
65
                }
66
            } elseif (array_key_exists($columns, $requestFilters)) {
67
                $query->where($columns, $requestFilters[$columns]);
68
            }
69
        }
70
71
        return $query;
72
    }
73
    private function buildMatchAgainst($query, array $columns, $search)
74
    {
75
        return $query->whereRaw('MATCH('.implode(',', $columns).') AGAINST (? '.$this->fullTextSearchMode.')', $search);
76
    }
77
    public function scopeSearchByCriteria($query, array $criteria): Builder
78
    {
79
        if (!isset($criteria['search'])) {
80
            return $query;
81
        }
82
83
        $query->where(function ($q) use ($criteria) {
84
            foreach ($this->fulltextSearch as $method => $columns) {
85
                if (method_exists($this, $method)) {
86
                    $q->orWhereHas($method, function ($q2) use ($criteria, $columns) {
87
                        $this->buildMatchAgainst($q2, $columns, $criteria['search']);
88
                    });
89
                } else {
90
                    $this->buildMatchAgainst($q, $columns, $criteria['search']);
91
                }
92
            }
93
94
            /*
95
             * @var Builder $q
96
             */
97
            foreach ($this->getSearchables() as $method => $columns) {
98
                if (method_exists($this, $method)) {
99
                    $q->orWhereHas($method, function ($query) use ($criteria, $columns) {
100
                        /* @var $query Builder */
101
                        $query->where(function ($query2) use ($criteria, $columns) {
102
                            /* @var $query2 Builder */
103
                            foreach ((array) $columns as $column) {
104
                                $this->searchByCriteriaQueryPerColumn($query2, $column, $criteria['search']);
105
                            }
106
                        });
107
                    });
108
                } else {
109
                    $this->searchByCriteriaQueryPerColumn($q, $columns, $criteria['search']);
110
                }
111
            }
112
        });
113
114
        return $query;
115
    }
116
117
    protected function searchByCriteriaQueryPerColumn(Builder $query, string $column, string $search): void
118
    {
119
        $query->orWhere($column, 'like', $search.'%');
120
    }
121
122
    public function scopeOrderByCriteria($query, array $criteria): Builder
123
    {
124
        if (isset($criteria['order']) && in_array($criteria['order'], $this->getSortables())) {
125
            $query->orderBy($criteria['order'], $criteria['direction'] ?? 'desc');
126
        }
127
128
        return $query;
129
    }
130
131
    /**
132
     * By default, all fillables and not hidden are searchables, if you want to override that explicitly set an array of searchables.
133
     * For a relation this is the syntax [ relationName => [columns in the relation] ].
134
     */
135
    public function getSearchables(): array
136
    {
137
        if (null !== $this->searchables) {
138
            return $this->searchables;
139
        }
140
141
        return array_diff($this->getFillable(), $this->getHidden());
0 ignored issues
show
Bug introduced by
The method getFillable() does not exist on Shamaseen\Repository\Utility\Models\Criteriable. Did you maybe mean getFilterables()? ( Ignorable by Annotation )

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

141
        return array_diff($this->/** @scrutinizer ignore-call */ getFillable(), $this->getHidden());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
It seems like getHidden() 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

141
        return array_diff($this->getFillable(), $this->/** @scrutinizer ignore-call */ getHidden());
Loading history...
142
    }
143
144
    public function scopeAppendSearchables($query, array $searchables): Builder
145
    {
146
        $this->searchables = collect($this->searchables)->merge($searchables)->unique()->toArray();
147
148
        return $query;
149
    }
150
151
    public function scopeSetSearchables($query, array $searchables): Builder
152
    {
153
        $this->searchables = $searchables;
154
155
        return $query;
156
    }
157
158
    /**
159
     * By default, all fillables and not hidden are sortables, if you want to override that explicitly set an array of sortables.
160
     */
161
    public function getSortables(): array
162
    {
163
        if (null !== $this->sortables) {
164
            return $this->sortables;
165
        }
166
167
        return array_diff($this->getFillable(), $this->getHidden());
168
    }
169
170
    public function scopeAppendSortables($query, array $sortables): Builder
171
    {
172
        $this->sortables = collect($this->sortables)->merge($sortables)->unique()->toArray();
173
174
        return $query;
175
    }
176
177
    public function scopeSetSortables($query, array $sortables): Builder
178
    {
179
        $this->sortables = $sortables;
180
181
        return $query;
182
    }
183
184
    /**
185
     * By default, all fillables and not hidden are filterables, if you want to override that explicitly set an array of searchables.
186
     * For a relation this is the syntax [ relationName => [columns in the relation] ].
187
     */
188
    public function getFilterables(): array
189
    {
190
        if (null !== $this->filterables) {
191
            return $this->filterables;
192
        }
193
194
        return array_diff($this->getFillable(), $this->getHidden());
195
    }
196
197
    public function scopeAppendFilterables($query, array $filterables): Builder
198
    {
199
        $this->filterables = collect($this->filterables)->merge($filterables)->unique()->toArray();
200
201
        return $query;
202
    }
203
204
    public function scopeSetFilterables($query, array $filterables): Builder
205
    {
206
        $this->filterables = $filterables;
207
208
        return $query;
209
    }
210
}
211