Complex classes like QueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use QueryBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class QueryBuilder extends Builder |
||
14 | { |
||
15 | /** @var \Illuminate\Support\Collection */ |
||
16 | protected $allowedFilters; |
||
17 | |||
18 | /** @var string|null */ |
||
19 | protected $defaultSort; |
||
20 | |||
21 | /** @var \Illuminate\Support\Collection */ |
||
22 | protected $allowedSorts; |
||
23 | |||
24 | /** @var \Illuminate\Support\Collection */ |
||
25 | protected $allowedIncludes; |
||
26 | |||
27 | /** @var \Illuminate\Support\Collection */ |
||
28 | protected $allowedAppends; |
||
29 | |||
30 | /** @var \Illuminate\Support\Collection */ |
||
31 | protected $fields; |
||
32 | |||
33 | /** @var array */ |
||
34 | protected $appends = []; |
||
35 | |||
36 | /** @var \Illuminate\Http\Request */ |
||
37 | protected $request; |
||
38 | |||
39 | public function __construct(Builder $builder, ? Request $request = null) |
||
40 | { |
||
41 | parent::__construct(clone $builder->getQuery()); |
||
42 | |||
43 | $this->initializeFromBuilder($builder); |
||
44 | |||
45 | $this->request = $request ?? request(); |
||
46 | |||
47 | $this->parseSelectedFields(); |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * Add the model, scopes, eager loaded relationships, local macro's and onDelete callback |
||
52 | * from the $builder to this query builder. |
||
53 | * |
||
54 | * @param \Illuminate\Database\Eloquent\Builder $builder |
||
55 | */ |
||
56 | protected function initializeFromBuilder(Builder $builder) |
||
57 | { |
||
58 | $this->setModel($builder->getModel()) |
||
59 | ->setEagerLoads($builder->getEagerLoads()); |
||
60 | |||
61 | $builder->macro('getProtected', function (Builder $builder, string $property) { |
||
62 | return $builder->{$property}; |
||
63 | }); |
||
64 | |||
65 | $this->scopes = $builder->getProtected('scopes'); |
||
66 | |||
67 | $this->localMacros = $builder->getProtected('localMacros'); |
||
68 | |||
69 | $this->onDelete = $builder->getProtected('onDelete'); |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * Create a new QueryBuilder for a request and model. |
||
74 | * |
||
75 | * @param string|\Illuminate\Database\Query\Builder $baseQuery Model class or base query builder |
||
76 | * @param Request $request |
||
77 | * |
||
78 | * @return \Spatie\QueryBuilder\QueryBuilder |
||
79 | */ |
||
80 | public static function for($baseQuery, ? Request $request = null) : self |
||
81 | { |
||
82 | if (is_string($baseQuery)) { |
||
83 | $baseQuery = ($baseQuery)::query(); |
||
84 | } |
||
85 | |||
86 | return new static($baseQuery, $request ?? request()); |
||
87 | } |
||
88 | |||
89 | public function allowedFilters($filters) : self |
||
90 | { |
||
91 | $filters = is_array($filters) ? $filters : func_get_args(); |
||
92 | $this->allowedFilters = collect($filters)->map(function ($filter) { |
||
93 | if ($filter instanceof Filter) { |
||
94 | return $filter; |
||
95 | } |
||
96 | |||
97 | return Filter::partial($filter); |
||
98 | }); |
||
99 | |||
100 | $this->guardAgainstUnknownFilters(); |
||
101 | |||
102 | $this->addFiltersToQuery($this->request->filters()); |
||
103 | |||
104 | return $this; |
||
105 | } |
||
106 | |||
107 | public function defaultSort($sort) : self |
||
108 | { |
||
109 | $this->defaultSort = $sort; |
||
110 | |||
111 | $this->addSortsToQuery($this->request->sorts($this->defaultSort)); |
||
112 | |||
113 | return $this; |
||
114 | } |
||
115 | |||
116 | public function allowedSorts($sorts) : self |
||
117 | { |
||
118 | $sorts = is_array($sorts) ? $sorts : func_get_args(); |
||
119 | if (! $this->request->sorts()) { |
||
120 | return $this; |
||
121 | } |
||
122 | |||
123 | $this->allowedSorts = collect($sorts)->map(function ($sort) { |
||
124 | if ($sort instanceof Sort) { |
||
125 | return $sort; |
||
126 | } |
||
127 | |||
128 | return Sort::field(ltrim($sort, '-')); |
||
129 | }); |
||
130 | |||
131 | $this->guardAgainstUnknownSorts(); |
||
132 | |||
133 | $this->addSortsToQuery($this->request->sorts($this->defaultSort)); |
||
134 | |||
135 | return $this; |
||
136 | } |
||
137 | |||
138 | public function allowedIncludes($includes) : self |
||
160 | |||
161 | public function allowedAppends($appends) : self |
||
173 | |||
174 | protected function parseSelectedFields() |
||
187 | |||
188 | protected function prependFieldsWithTableName(array $fields, string $tableName): array |
||
194 | |||
195 | protected function getFieldsForRelatedTable(string $relation): array |
||
205 | |||
206 | protected function addFiltersToQuery(Collection $filters) |
||
214 | |||
215 | protected function findFilter(string $property) : ? Filter |
||
222 | |||
223 | protected function addSortsToQuery(Collection $sorts) |
||
224 | { |
||
225 | $this->filterDuplicates($sorts) |
||
226 | ->each(function (string $property) { |
||
227 | $descending = $property[0] === '-'; |
||
228 | |||
236 | |||
237 | protected function filterDuplicates(Collection $sorts): Collection |
||
255 | |||
256 | protected function findSort(string $property) : ? Sort |
||
263 | |||
264 | protected function addDefaultSorts() |
||
276 | |||
277 | protected function addIncludesToQuery(Collection $includes) |
||
304 | |||
305 | public function setAppendsToResult($result) |
||
315 | |||
316 | protected function guardAgainstUnknownFilters() |
||
328 | |||
329 | protected function guardAgainstUnknownSorts() |
||
343 | |||
344 | protected function guardAgainstUnknownIncludes() |
||
354 | |||
355 | protected function guardAgainstUnknownAppends() |
||
365 | |||
366 | public function getQuery() |
||
374 | |||
375 | public function get($columns = ['*']) |
||
389 | |||
390 | public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null) |
||
398 | |||
399 | public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null) |
||
407 | } |
||
408 |