Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 60dd5d8

Browse files
authoredMar 20, 2023
Merge pull request #1 from worksome/feature/generics
feat: add generics and fix PHPStan
2 parents 5654d47 + 7398500 commit 60dd5d8

File tree

6 files changed

+98
-40
lines changed

6 files changed

+98
-40
lines changed
 

‎composer.json

+1-10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"require": {
1919
"php": "^8.2",
2020
"illuminate/contracts": "^9.0 || ^10.0",
21+
"illuminate/database": "^9.0 || ^10.0",
2122
"spatie/laravel-package-tools": "^1.14.1",
2223
"tucker-eric/eloquentfilter": "^3.2"
2324
},
@@ -62,16 +63,6 @@
6263
"worksome/coding-style": true
6364
}
6465
},
65-
"extra": {
66-
"laravel": {
67-
"providers": [
68-
"Worksome\\Filters\\FiltersServiceProvider"
69-
],
70-
"aliases": {
71-
"Filters": "Worksome\\Filters\\Facades\\Filters"
72-
}
73-
}
74-
},
7566
"minimum-stability": "dev",
7667
"prefer-stable": true
7768
}

‎phpstan-baseline.neon

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: "#^Call to an undefined method Worksome\\\\Filters\\\\ModelFilter\\:\\:withTrashed\\(\\)\\.$#"
5+
count: 1
6+
path: src/ModelFilter.php

‎src/Exceptions/MissingRequiredFilterException.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Exception;
88

99
/**
10-
* Exception thrown by FWorksome\Filters\FilterQuery when no required FilterClass property is not provided to it
10+
* Exception thrown by `Worksome\Filters\FilterQuery` when a required FilterClass property is not provided to it.
1111
*/
1212
class MissingRequiredFilterException extends Exception
1313
{

‎src/Filter.php

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Worksome\Filters;
46

57
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
@@ -8,17 +10,16 @@
810
use Illuminate\Support\Facades\Facade;
911

1012
/**
11-
* Facade for Worksome\Filters\FilterQuery
12-
*
13-
* @method static FilterQuery model(string $modelClass)
14-
* @method static FilterQuery apply(string $filterClass)
15-
* @method static FilterQuery input(array $input = [])
16-
* @method static FilterQuery query(Builder $query)
17-
* @method static Builder getQuery
18-
* @method static \Illuminate\Support\Collection|static[] get
19-
* @method static LengthAwarePaginator paginateFilter($perPage = null, $columns = ['*'], $pageName = 'page', $page =null)
20-
* @method static Paginator simplePaginateFilter($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
13+
* @method static FilterQuery model(string $modelClass)
14+
* @method static FilterQuery apply(string $filterClass)
15+
* @method static FilterQuery input(array $input = [])
16+
* @method static FilterQuery query(Builder $query)
17+
* @method static Builder getQuery()
18+
* @method static \Illuminate\Support\Collection|static[] get()
19+
* @method static LengthAwarePaginator paginateFilter($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
20+
* @method static Paginator simplePaginateFilter($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
2121
*
22+
* @see FilterQuery
2223
*/
2324
class Filter extends Facade
2425
{

‎src/FilterQuery.php

+66-15
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,53 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Worksome\Filters;
46

57
use Illuminate\Contracts\Config\Repository;
68
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
79
use Illuminate\Contracts\Pagination\Paginator;
810
use Illuminate\Database\Eloquent\Builder;
911
use Illuminate\Database\Eloquent\Collection;
12+
use Illuminate\Database\Eloquent\Model;
1013
use Worksome\Filters\Exceptions\MissingRequiredFilterException;
1114
use Worksome\Filters\Exceptions\MissingRequiredModelOrQueryException;
1215

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+
*/
1422
class FilterQuery
1523
{
16-
/** @var array<int, string> */
24+
/** @var array */
1725
protected array $filtered = [];
1826

19-
/** @var array<int, mixed> */
27+
/** @var array<int|string, mixed> */
2028
protected array $input = [];
2129

2230
protected Repository $config;
2331

32+
/** @var class-string<TModel>|null */
2433
private string|null $modelClass = null;
2534

35+
/** @var class-string<TFilter>|null */
2636
private string|null $filterClass = null;
2737

38+
/** @var Builder<TModel>|null */
2839
private Builder|null $query = null;
2940

3041
public function __construct(Repository $config)
3142
{
3243
$this->config = $config;
3344
}
3445

46+
/**
47+
* @param class-string<TModel> $modelClass
48+
*
49+
* @return self<TModel, TFilter>
50+
*/
3551
public function model(string $modelClass): self
3652
{
3753
$this->modelClass = $modelClass;
@@ -40,29 +56,47 @@ public function model(string $modelClass): self
4056
return $this;
4157
}
4258

59+
/**
60+
* @param class-string<TFilter> $filterClass
61+
*
62+
* @return self<TModel, TFilter>
63+
*/
4364
public function apply(string $filterClass): self
4465
{
4566
if ($this->filterClass && $this->filterClass != $filterClass) {
4667
$this->resetQuery();
4768
}
69+
4870
$this->filterClass = $filterClass;
4971

5072
return $this;
5173
}
5274

53-
/** @param array<string, mixed> $input */
75+
/**
76+
* @param array<string, mixed> $input
77+
*
78+
* @return self<TModel, TFilter>
79+
*/
5480
public function input(array $input = []): self
5581
{
5682
$this->input = $input;
83+
5784
return $this;
5885
}
5986

87+
/**
88+
* @param Builder<TModel> $query
89+
*
90+
* @return self<TModel, TFilter>
91+
*/
6092
public function query(Builder $query): self
6193
{
6294
$this->query = $query;
95+
6396
return $this;
6497
}
6598

99+
/** @return Builder<TModel> */
66100
public function getQuery(): Builder
67101
{
68102
if (! isset($this->query)) {
@@ -75,35 +109,40 @@ public function getQuery(): Builder
75109
}
76110

77111
/**
78-
* @return Builder[]|Collection
112+
* @return Collection<int, TModel>
79113
*
80114
* @throws MissingRequiredFilterException|MissingRequiredModelOrQueryException
81115
*/
82-
public function get()
116+
public function get(): Collection
83117
{
84118
return $this->getQuery()->get();
85119
}
86120

87121
/**
88-
* @param array<string> $columns
122+
* @param array<string> $columns
123+
*
124+
* @return LengthAwarePaginator<TModel>
89125
*
90126
* @throws MissingRequiredFilterException|MissingRequiredModelOrQueryException
91127
*/
92128
public function paginateFilter(
93-
?int $perPage = null,
129+
int|null $perPage = null,
94130
array $columns = ['*'],
95131
string $pageName = 'page',
96-
?int $page = null,
132+
int|null $page = null,
97133
): LengthAwarePaginator {
98134
$perPage = $perPage ?: $this->config->get('eloquentfilter.paginate_limit');
135+
assert($perPage === null || is_int($perPage));
99136
$paginator = $this->getQuery()->paginate($perPage, $columns, $pageName, $page);
100137
$paginator->appends($this->filtered);
101138

102139
return $paginator;
103140
}
104141

105142
/**
106-
* @param array<int, string> $columns
143+
* @param array<int, string> $columns
144+
*
145+
* @return Paginator<TModel>
107146
*
108147
* @throws MissingRequiredFilterException|MissingRequiredModelOrQueryException
109148
*/
@@ -114,49 +153,61 @@ public function simplePaginateFilter(
114153
int|null $page = null,
115154
): Paginator {
116155
$perPage = $perPage ?: $this->config->get('eloquentfilter.paginate_limit');
156+
assert($perPage === null || is_int($perPage));
117157
$paginator = $this->getQuery()->simplePaginate($perPage, $columns, $pageName, $page);
118158
$paginator->appends($this->filtered);
119159

120160
return $paginator;
121161
}
122162

163+
/** @return self<TModel, TFilter> */
123164
protected function run(): self
124165
{
125166
$filter = $this->provideFilter();
126167

127168
$modelFilter = new $filter($this->query, $this->input);
128169

170+
// @phpstan-ignore-next-line
129171
$this->filtered = $modelFilter->input();
130172

131173
$modelFilter->handle();
132174

133175
return $this;
134176
}
135177

178+
/** @return class-string<TModel> */
136179
private function getModelClass(): string
137180
{
138181
if (! isset($this->modelClass)) {
139182
throw new MissingRequiredModelOrQueryException('Filter is missing required eloquent Model class.');
140183
}
184+
141185
return $this->modelClass;
142186
}
143187

188+
/** @return class-string<TFilter>|class-string<ModelFilter> */
144189
private function provideFilter(): string
145190
{
146191
if ($this->filterClass) {
147192
return $this->filterClass;
148193
}
149194

150-
$filter = $this->config->get(
195+
$namespace = $this->config->get(
151196
'eloquentfilter.namespace',
152197
'App\\ModelFilters\\'
153-
) . class_basename($this) . 'Filter';
198+
);
154199

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();
157206
}
158207

159-
throw new MissingRequiredFilterException();
208+
assert(is_subclass_of($filter, ModelFilter::class));
209+
210+
return $filter;
160211
}
161212

162213
private function resetQuery(): void

‎src/ModelFilter.php

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Worksome\Filters;
46

57
use EloquentFilter\ModelFilter as BaseModelFilter;
8+
use Illuminate\Database\Eloquent\SoftDeletes;
69
use Illuminate\Support\Str;
710

8-
/** This class wraps around the `EloquentFilter\ModelFilter` to add sorting functionality and `IncludeTrash` helper. */
11+
/**
12+
* This class wraps around the `EloquentFilter\ModelFilter` to add sorting functionality and `IncludeTrash` helper.
13+
*/
914
class ModelFilter extends BaseModelFilter
1015
{
1116
/**
@@ -44,22 +49,26 @@ public function sortBy(array $sorts): void
4449
// Check if pre-defined orderBy method exists to sort by
4550
if (method_exists($this, $method = 'sortBy' . Str::studly($column))) {
4651
$this->$method($direction);
52+
4753
continue;
4854
}
4955

5056
// Otherwise order by the column in a specific direction, default DESC
5157
if ($this->isSortable($column)) {
5258
$this->orderBy($column, $direction);
59+
5360
continue;
5461
}
5562
}
5663
}
5764

58-
public function includeTrashed(bool $value): void
65+
public function includeTrashed(bool $includeTrashed = true): void
5966
{
60-
if ($value) {
61-
$this->withTrashed();
67+
if (! $includeTrashed) {
68+
return;
6269
}
70+
71+
$this->withTrashed();
6372
}
6473

6574
protected function prefixWithModelTable(string $field): string

0 commit comments

Comments
 (0)
Please sign in to comment.