1
1
<?php
2
2
3
+ declare (strict_types=1 );
4
+
3
5
namespace Worksome \Filters ;
4
6
5
7
use Illuminate \Contracts \Config \Repository ;
6
8
use Illuminate \Contracts \Pagination \LengthAwarePaginator ;
7
9
use Illuminate \Contracts \Pagination \Paginator ;
8
10
use Illuminate \Database \Eloquent \Builder ;
9
11
use Illuminate \Database \Eloquent \Collection ;
12
+ use Illuminate \Database \Eloquent \Model ;
10
13
use Worksome \Filters \Exceptions \MissingRequiredFilterException ;
11
14
use Worksome \Filters \Exceptions \MissingRequiredModelOrQueryException ;
12
15
13
- /** Our own implementation for filtering based on `EloquentFilter\Filterable`, but without the usage of scopes. */
16
+ /**
17
+ * Our own implementation for filtering based on `EloquentFilter\Filterable`, but without the usage of scopes.
18
+ *
19
+ * @template TModel of Model
20
+ * @template TFilter of ModelFilter
21
+ */
14
22
class FilterQuery
15
23
{
16
- /** @var array<int, string> */
24
+ /** @var array */
17
25
protected array $ filtered = [];
18
26
19
- /** @var array<int, mixed> */
27
+ /** @var array<int|string , mixed> */
20
28
protected array $ input = [];
21
29
22
30
protected Repository $ config ;
23
31
32
+ /** @var class-string<TModel>|null */
24
33
private string |null $ modelClass = null ;
25
34
35
+ /** @var class-string<TFilter>|null */
26
36
private string |null $ filterClass = null ;
27
37
38
+ /** @var Builder<TModel>|null */
28
39
private Builder |null $ query = null ;
29
40
30
41
public function __construct (Repository $ config )
31
42
{
32
43
$ this ->config = $ config ;
33
44
}
34
45
46
+ /**
47
+ * @param class-string<TModel> $modelClass
48
+ *
49
+ * @return self<TModel, TFilter>
50
+ */
35
51
public function model (string $ modelClass ): self
36
52
{
37
53
$ this ->modelClass = $ modelClass ;
@@ -40,29 +56,47 @@ public function model(string $modelClass): self
40
56
return $ this ;
41
57
}
42
58
59
+ /**
60
+ * @param class-string<TFilter> $filterClass
61
+ *
62
+ * @return self<TModel, TFilter>
63
+ */
43
64
public function apply (string $ filterClass ): self
44
65
{
45
66
if ($ this ->filterClass && $ this ->filterClass != $ filterClass ) {
46
67
$ this ->resetQuery ();
47
68
}
69
+
48
70
$ this ->filterClass = $ filterClass ;
49
71
50
72
return $ this ;
51
73
}
52
74
53
- /** @param array<string, mixed> $input */
75
+ /**
76
+ * @param array<string, mixed> $input
77
+ *
78
+ * @return self<TModel, TFilter>
79
+ */
54
80
public function input (array $ input = []): self
55
81
{
56
82
$ this ->input = $ input ;
83
+
57
84
return $ this ;
58
85
}
59
86
87
+ /**
88
+ * @param Builder<TModel> $query
89
+ *
90
+ * @return self<TModel, TFilter>
91
+ */
60
92
public function query (Builder $ query ): self
61
93
{
62
94
$ this ->query = $ query ;
95
+
63
96
return $ this ;
64
97
}
65
98
99
+ /** @return Builder<TModel> */
66
100
public function getQuery (): Builder
67
101
{
68
102
if (! isset ($ this ->query )) {
@@ -75,35 +109,40 @@ public function getQuery(): Builder
75
109
}
76
110
77
111
/**
78
- * @return Builder[]| Collection
112
+ * @return Collection<int, TModel>
79
113
*
80
114
* @throws MissingRequiredFilterException|MissingRequiredModelOrQueryException
81
115
*/
82
- public function get ()
116
+ public function get (): Collection
83
117
{
84
118
return $ this ->getQuery ()->get ();
85
119
}
86
120
87
121
/**
88
- * @param array<string> $columns
122
+ * @param array<string> $columns
123
+ *
124
+ * @return LengthAwarePaginator<TModel>
89
125
*
90
126
* @throws MissingRequiredFilterException|MissingRequiredModelOrQueryException
91
127
*/
92
128
public function paginateFilter (
93
- ? int $ perPage = null ,
129
+ int | null $ perPage = null ,
94
130
array $ columns = ['* ' ],
95
131
string $ pageName = 'page ' ,
96
- ? int $ page = null ,
132
+ int | null $ page = null ,
97
133
): LengthAwarePaginator {
98
134
$ perPage = $ perPage ?: $ this ->config ->get ('eloquentfilter.paginate_limit ' );
135
+ assert ($ perPage === null || is_int ($ perPage ));
99
136
$ paginator = $ this ->getQuery ()->paginate ($ perPage , $ columns , $ pageName , $ page );
100
137
$ paginator ->appends ($ this ->filtered );
101
138
102
139
return $ paginator ;
103
140
}
104
141
105
142
/**
106
- * @param array<int, string> $columns
143
+ * @param array<int, string> $columns
144
+ *
145
+ * @return Paginator<TModel>
107
146
*
108
147
* @throws MissingRequiredFilterException|MissingRequiredModelOrQueryException
109
148
*/
@@ -114,49 +153,61 @@ public function simplePaginateFilter(
114
153
int |null $ page = null ,
115
154
): Paginator {
116
155
$ perPage = $ perPage ?: $ this ->config ->get ('eloquentfilter.paginate_limit ' );
156
+ assert ($ perPage === null || is_int ($ perPage ));
117
157
$ paginator = $ this ->getQuery ()->simplePaginate ($ perPage , $ columns , $ pageName , $ page );
118
158
$ paginator ->appends ($ this ->filtered );
119
159
120
160
return $ paginator ;
121
161
}
122
162
163
+ /** @return self<TModel, TFilter> */
123
164
protected function run (): self
124
165
{
125
166
$ filter = $ this ->provideFilter ();
126
167
127
168
$ modelFilter = new $ filter ($ this ->query , $ this ->input );
128
169
170
+ // @phpstan-ignore-next-line
129
171
$ this ->filtered = $ modelFilter ->input ();
130
172
131
173
$ modelFilter ->handle ();
132
174
133
175
return $ this ;
134
176
}
135
177
178
+ /** @return class-string<TModel> */
136
179
private function getModelClass (): string
137
180
{
138
181
if (! isset ($ this ->modelClass )) {
139
182
throw new MissingRequiredModelOrQueryException ('Filter is missing required eloquent Model class. ' );
140
183
}
184
+
141
185
return $ this ->modelClass ;
142
186
}
143
187
188
+ /** @return class-string<TFilter>|class-string<ModelFilter> */
144
189
private function provideFilter (): string
145
190
{
146
191
if ($ this ->filterClass ) {
147
192
return $ this ->filterClass ;
148
193
}
149
194
150
- $ filter = $ this ->config ->get (
195
+ $ namespace = $ this ->config ->get (
151
196
'eloquentfilter.namespace ' ,
152
197
'App \\ModelFilters \\'
153
- ) . class_basename ( $ this ) . ' Filter ' ;
198
+ );
154
199
155
- if (class_exists ($ filter )) {
156
- return $ filter ;
200
+ assert (is_string ($ namespace ));
201
+
202
+ $ filter = sprintf ('%s%sFilter ' , $ namespace , class_basename ($ this ));
203
+
204
+ if (! class_exists ($ filter )) {
205
+ throw new MissingRequiredFilterException ();
157
206
}
158
207
159
- throw new MissingRequiredFilterException ();
208
+ assert (is_subclass_of ($ filter , ModelFilter::class));
209
+
210
+ return $ filter ;
160
211
}
161
212
162
213
private function resetQuery (): void
0 commit comments