Criteriable::scopeAppendSortables()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 2
b 0
f 1
nc 1
nop 2
dl 0
loc 5
rs 10
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
    protected bool $fullTextSearchExpansion = false;
25
26
    /**
27
     * @var array<array<string>>
28
     */
29
    protected ?array $fulltextSearch = [];
30
31
    public function getFilterKey()
32
    {
33
        return config('repository.filter_key');
34
    }
35
36
    protected function getFiltersFromCriteria(array $criteria): array
37
    {
38
        $filterKey = $this->getFilterKey();
39
40
        if ($filterKey) {
41
            return (array) ($criteria[$filterKey] ?? []);
42
        }
43
44
        return $criteria;
45
    }
46
47
    public function scopeFilterByCriteria($query, array $criteria): Builder
48
    {
49
        $requestFilters = $this->getFiltersFromCriteria($criteria);
50
51
        foreach ($this->getFilterables() as $method => $columns) {
52
            // if this is associative then it is a relation
53
            if ('string' === gettype($method)) {
54
                if (method_exists($this, $method) && array_key_exists($method, $requestFilters)) {
55
                    $query->whereHas($method, function ($query) use ($requestFilters, $columns, $method) {
56
                        /* @var $query Builder */
57
                        $query->where(function ($query2) use ($requestFilters, $columns, $method) {
58
                            /* @var $query2 Builder */
59
                            foreach ((array) $columns as $column) {
60
                                if (isset($requestFilters[$method][$column])) {
61
                                    $query2->where($column, $requestFilters[$method][$column]);
62
                                }
63
                            }
64
                        });
65
                    });
66
                }
67
            } elseif (array_key_exists($columns, $requestFilters)) {
68
                $query->where($columns, $requestFilters[$columns]);
69
            }
70
        }
71
72
        return $query;
73
    }
74
75
    public function scopeSearchByCriteria($query, array $criteria): Builder
76
    {
77
        if (!isset($criteria['search'])) {
78
            return $query;
79
        }
80
81
        $fullTextOptions = [
82
            'mode' => $this->fullTextSearchMode,
83
            'expanded' => $this->fullTextSearchExpansion
84
        ];
85
86
        $query->where(function ($q) use ($criteria, $fullTextOptions) {
87
            foreach ($this->fulltextSearch as $method => $columns) {
88
                if (method_exists($this, $method)) {
89
                    $q->orWhereHas($method, function ($q2) use ($criteria, $columns, $fullTextOptions) {
90
                        $q2->whereFullText($columns, $criteria['search'], $fullTextOptions);
91
                    });
92
                } else {
93
                    $q->whereFullText($columns, $criteria['search'], $fullTextOptions);
94
                }
95
            }
96
97
            /*
98
             * @var Builder $q
99
             */
100
            foreach ($this->getSearchables() as $method => $columns) {
101
                if (method_exists($this, $method)) {
102
                    $q->orWhereHas($method, function ($query) use ($criteria, $columns) {
103
                        /* @var $query Builder */
104
                        $query->where(function ($query2) use ($criteria, $columns) {
105
                            /* @var $query2 Builder */
106
                            foreach ((array) $columns as $column) {
107
                                $this->searchByCriteriaQueryPerColumn($query2, $column, $criteria['search']);
108
                            }
109
                        });
110
                    });
111
                } else {
112
                    $this->searchByCriteriaQueryPerColumn($q, $columns, $criteria['search']);
113
                }
114
            }
115
        });
116
117
        return $query;
118
    }
119
120
    protected function searchByCriteriaQueryPerColumn(Builder $query, string $column, string $search): void
121
    {
122
        $query->orWhere($column, 'like', $search.'%');
123
    }
124
125
    public function scopeOrderByCriteria($query, array $criteria): Builder
126
    {
127
        if (isset($criteria['order']) && in_array($criteria['order'], $this->getSortables())) {
128
            $query->orderBy($criteria['order'], $criteria['direction'] ?? 'desc');
129
        }
130
131
        return $query;
132
    }
133
134
    /**
135
     * By default, all fillables and not hidden are searchables, if you want to override that explicitly set an array of searchables.
136
     * For a relation this is the syntax [ relationName => [columns in the relation] ].
137
     */
138
    public function getSearchables(): array
139
    {
140
        if (null !== $this->searchables) {
141
            return $this->searchables;
142
        }
143
144
        return array_diff($this->getFillable(), $this->getHidden());
0 ignored issues
show
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

144
        return array_diff($this->getFillable(), $this->/** @scrutinizer ignore-call */ getHidden());
Loading history...
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

144
        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...
145
    }
146
147
    public function scopeAppendSearchables($query, array $searchables): Builder
148
    {
149
        $this->searchables = collect($this->searchables)->merge($searchables)->unique()->toArray();
0 ignored issues
show
Bug introduced by
It seems like $this->searchables can also be of type array; however, parameter $value of collect() does only seem to accept Illuminate\Contracts\Support\Arrayable, 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

149
        $this->searchables = collect(/** @scrutinizer ignore-type */ $this->searchables)->merge($searchables)->unique()->toArray();
Loading history...
Bug introduced by
$searchables of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::merge(). ( Ignorable by Annotation )

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

149
        $this->searchables = collect($this->searchables)->merge(/** @scrutinizer ignore-type */ $searchables)->unique()->toArray();
Loading history...
150
151
        return $query;
152
    }
153
154
    public function scopeSetSearchables($query, array $searchables): Builder
155
    {
156
        $this->searchables = $searchables;
157
158
        return $query;
159
    }
160
161
    /**
162
     * By default, all fillables and not hidden are sortables, if you want to override that explicitly set an array of sortables.
163
     */
164
    public function getSortables(): array
165
    {
166
        if (null !== $this->sortables) {
167
            return $this->sortables;
168
        }
169
170
        return array_diff($this->getFillable(), $this->getHidden());
171
    }
172
173
    public function scopeAppendSortables($query, array $sortables): Builder
174
    {
175
        $this->sortables = collect($this->sortables)->merge($sortables)->unique()->toArray();
0 ignored issues
show
Bug introduced by
$sortables of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::merge(). ( Ignorable by Annotation )

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

175
        $this->sortables = collect($this->sortables)->merge(/** @scrutinizer ignore-type */ $sortables)->unique()->toArray();
Loading history...
Bug introduced by
It seems like $this->sortables can also be of type array; however, parameter $value of collect() does only seem to accept Illuminate\Contracts\Support\Arrayable, 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

175
        $this->sortables = collect(/** @scrutinizer ignore-type */ $this->sortables)->merge($sortables)->unique()->toArray();
Loading history...
176
177
        return $query;
178
    }
179
180
    public function scopeSetSortables($query, array $sortables): Builder
181
    {
182
        $this->sortables = $sortables;
183
184
        return $query;
185
    }
186
187
    /**
188
     * By default, all fillables and not hidden are filterables, if you want to override that explicitly set an array of searchables.
189
     * For a relation this is the syntax [ relationName => [columns in the relation] ].
190
     */
191
    public function getFilterables(): array
192
    {
193
        if (null !== $this->filterables) {
194
            return $this->filterables;
195
        }
196
197
        return array_diff($this->getFillable(), $this->getHidden());
198
    }
199
200
    public function scopeAppendFilterables($query, array $filterables): Builder
201
    {
202
        $this->filterables = collect($this->filterables)->merge($filterables)->unique()->toArray();
0 ignored issues
show
Bug introduced by
It seems like $this->filterables can also be of type array; however, parameter $value of collect() does only seem to accept Illuminate\Contracts\Support\Arrayable, 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

202
        $this->filterables = collect(/** @scrutinizer ignore-type */ $this->filterables)->merge($filterables)->unique()->toArray();
Loading history...
Bug introduced by
$filterables of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::merge(). ( Ignorable by Annotation )

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

202
        $this->filterables = collect($this->filterables)->merge(/** @scrutinizer ignore-type */ $filterables)->unique()->toArray();
Loading history...
203
204
        return $query;
205
    }
206
207
    public function scopeSetFilterables($query, array $filterables): Builder
208
    {
209
        $this->filterables = $filterables;
210
211
        return $query;
212
    }
213
}
214