Skip to content

Commit e2fdc6d

Browse files
committed
refactor: cleanup params validator and drop RequestHelper
1 parent 6d50eae commit e2fdc6d

File tree

7 files changed

+160
-129
lines changed

7 files changed

+160
-129
lines changed

src/Drivers/Standard/ParamsValidator.php

Lines changed: 107 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
namespace Orion\Drivers\Standard;
44

55
use Illuminate\Support\Facades\Validator;
6-
use Illuminate\Validation\Rule;
76
use Orion\Exceptions\MaxNestedDepthExceededException;
87
use Orion\Helpers\ArrayHelper;
9-
use Orion\Helpers\RequestHelper;
108
use Orion\Http\Requests\Request;
119
use Orion\Http\Rules\WhitelistedField;
1210
use Orion\Http\Rules\WhitelistedQueryFields;
@@ -41,8 +39,13 @@ class ParamsValidator implements \Orion\Contracts\ParamsValidator
4139
/**
4240
* @inheritDoc
4341
*/
44-
public function __construct(array $exposedScopes = [], array $filterableBy = [], array $sortableBy = [], array $aggregatableBy = [], array $includableBy = [])
45-
{
42+
public function __construct(
43+
array $exposedScopes = [],
44+
array $filterableBy = [],
45+
array $sortableBy = [],
46+
array $aggregatableBy = [],
47+
array $includableBy = []
48+
) {
4649
$this->exposedScopes = $exposedScopes;
4750
$this->filterableBy = $filterableBy;
4851
$this->sortableBy = $sortableBy;
@@ -74,58 +77,6 @@ public function validateFilters(Request $request): void
7477
)->validate();
7578
}
7679

77-
/**
78-
* @throws MaxNestedDepthExceededException
79-
*/
80-
protected function nestedFiltersDepth($array, $modifier = 0) {
81-
$depth = ArrayHelper::depth($array);
82-
$configMaxNestedDepth = config('orion.search.max_nested_depth', 1);
83-
84-
// Here we calculate the real nested filters depth
85-
$depth = floor($depth / 2);
86-
87-
if ($depth + $modifier > $configMaxNestedDepth) {
88-
throw new MaxNestedDepthExceededException(422, __('Max nested depth :depth is exceeded', ['depth' => $configMaxNestedDepth]));
89-
}
90-
91-
return $depth;
92-
}
93-
94-
/**
95-
* @param string $prefix
96-
* @param int $maxDepth
97-
* @param array $filterableBy
98-
* @param array $rules
99-
* @param int $currentDepth
100-
* @return array
101-
*/
102-
protected function getNestedRules(string $prefix, int $maxDepth, array $rules = [], int $currentDepth = 1): array
103-
{
104-
$rules = array_merge($rules, [
105-
$prefix.'.*.type' => ['sometimes', 'in:and,or'],
106-
$prefix.'.*.field' => [
107-
"required_without:{$prefix}.*.nested",
108-
'regex:/^[\w.\_\-\>]+$/',
109-
new WhitelistedField($this->filterableBy),
110-
],
111-
$prefix.'.*.operator' => [
112-
'sometimes',
113-
'in:<,<=,>,>=,=,!=,like,not like,ilike,not ilike,in,not in,all in,any in',
114-
],
115-
$prefix.'.*.value' => ['nullable'],
116-
$prefix.'.*.nested' => ['sometimes', 'array',],
117-
]);
118-
119-
if ($maxDepth >= $currentDepth) {
120-
$rules = array_merge(
121-
$rules,
122-
$this->getNestedRules("{$prefix}.*.nested", $maxDepth, $rules, ++$currentDepth)
123-
);
124-
}
125-
126-
return $rules;
127-
}
128-
12980
public function validateSort(Request $request): void
13081
{
13182
Validator::make(
@@ -156,10 +107,10 @@ public function validateSearch(Request $request): void
156107

157108
public function validateAggregators(Request $request): void
158109
{
159-
$depth = $this->nestedFiltersDepth($request->post('aggregates', []), -1);
110+
$depth = $this->nestedFiltersDepth($request->input('aggregates', []), -1);
160111

161112
Validator::make(
162-
$request->post(),
113+
$request->all(),
163114
array_merge(
164115
[
165116
'aggregates' => ['sometimes', 'array'],
@@ -170,11 +121,11 @@ public function validateAggregators(Request $request): void
170121
'aggregates.*.field' => [
171122
(float) app()->version() >= 8.32 ? 'prohibited_if:aggregate.*.type,count' : '',
172123
(float) app()->version() >= 8.32 ? 'prohibited_if:aggregate.*.type,exists' : '',
173-
'required_if:aggregate.*.type,avg,sum,min,max'
124+
'required_if:aggregate.*.type,avg,sum,min,max',
174125
],
175126
'aggregates.*.type' => [
176127
'required',
177-
'in:count,min,max,avg,sum,exists'
128+
'in:count,min,max,avg,sum,exists',
178129
],
179130
'aggregates.*.filters' => ['sometimes', 'array'],
180131
],
@@ -186,35 +137,65 @@ public function validateAggregators(Request $request): void
186137
Validator::make(
187138
[
188139
'aggregates' =>
189-
collect($request->post('aggregates', []))
140+
collect($request->input('aggregates', []))
190141
->map(function ($aggregate) {
191142
return ['field' => isset($aggregate['field']) ? "{$aggregate['relation']}.{$aggregate['field']}" : $aggregate['relation']];
192-
})->all()
143+
})->all(),
193144
],
194145
[
195-
'aggregates.*.field' => new WhitelistedQueryFields($this->aggregatableBy)
146+
'aggregates.*.field' => new WhitelistedField($this->aggregatableBy),
196147
]
197148
)->validate();
198149

199150
Validator::make(
200151
$request->query(),
201152
[
202-
'with_count' => ['sometimes', 'string', 'not_regex:/\\./', new WhitelistedQueryFields($this->aggregatableBy)],
203-
'with_exists' => ['sometimes', 'string', 'not_regex:/\\./', new WhitelistedQueryFields($this->aggregatableBy)],
204-
'with_min' => ['sometimes', 'string', 'regex:/^[a-z\d,]*\.+[a-z\d,]*$/', new WhitelistedQueryFields($this->aggregatableBy)],
205-
'with_max' => ['sometimes', 'string', 'regex:/^[a-z\d,]*\.+[a-z\d,]*$/', new WhitelistedQueryFields($this->aggregatableBy)],
206-
'with_avg' => ['sometimes', 'string', 'regex:/^[a-z\d,]*\.+[a-z\d,]*$/', new WhitelistedQueryFields($this->aggregatableBy)],
207-
'with_sum' => ['sometimes', 'string', 'regex:/^[a-z\d,]*\.+[a-z\d,]*$/', new WhitelistedQueryFields($this->aggregatableBy)],
153+
'with_count' => [
154+
'sometimes',
155+
'string',
156+
'not_regex:/\\./',
157+
new WhitelistedQueryFields($this->aggregatableBy),
158+
],
159+
'with_exists' => [
160+
'sometimes',
161+
'string',
162+
'not_regex:/\\./',
163+
new WhitelistedQueryFields($this->aggregatableBy),
164+
],
165+
'with_min' => [
166+
'sometimes',
167+
'string',
168+
'regex:/^[a-z\d,]*\.+[a-z\d,]*$/',
169+
new WhitelistedQueryFields($this->aggregatableBy),
170+
],
171+
'with_max' => [
172+
'sometimes',
173+
'string',
174+
'regex:/^[a-z\d,]*\.+[a-z\d,]*$/',
175+
new WhitelistedQueryFields($this->aggregatableBy),
176+
],
177+
'with_avg' => [
178+
'sometimes',
179+
'string',
180+
'regex:/^[a-z\d,]*\.+[a-z\d,]*$/',
181+
new WhitelistedQueryFields($this->aggregatableBy),
182+
],
183+
'with_sum' => [
184+
'sometimes',
185+
'string',
186+
'regex:/^[a-z\d,]*\.+[a-z\d,]*$/',
187+
new WhitelistedQueryFields($this->aggregatableBy),
188+
],
208189
]
209190
)->validate();
210191
}
211192

212193
public function validateIncludes(Request $request): void
213194
{
214-
$depth = $this->nestedFiltersDepth($request->post('includes', []), -1);
195+
$depth = $this->nestedFiltersDepth($request->input('includes', []), -1);
215196

216197
Validator::make(
217-
$request->post(),
198+
$request->all(),
218199
array_merge(
219200
[
220201
'includes' => ['sometimes', 'array'],
@@ -236,4 +217,59 @@ public function validateIncludes(Request $request): void
236217
]
237218
)->validate();
238219
}
220+
221+
/**
222+
* @throws MaxNestedDepthExceededException
223+
*/
224+
protected function nestedFiltersDepth($array, $modifier = 0)
225+
{
226+
$depth = ArrayHelper::depth($array);
227+
$configMaxNestedDepth = config('orion.search.max_nested_depth', 1);
228+
229+
// Here we calculate the real nested filters depth
230+
$depth = floor($depth / 2);
231+
232+
if ($depth + $modifier > $configMaxNestedDepth) {
233+
throw new MaxNestedDepthExceededException(
234+
422,
235+
__('Max nested depth :depth is exceeded', ['depth' => $configMaxNestedDepth])
236+
);
237+
}
238+
239+
return $depth;
240+
}
241+
242+
/**
243+
* @param string $prefix
244+
* @param int $maxDepth
245+
* @param array $rules
246+
* @param int $currentDepth
247+
* @return array
248+
*/
249+
protected function getNestedRules(string $prefix, int $maxDepth, array $rules = [], int $currentDepth = 1): array
250+
{
251+
$rules = array_merge($rules, [
252+
$prefix.'.*.type' => ['sometimes', 'in:and,or'],
253+
$prefix.'.*.field' => [
254+
"required_without:{$prefix}.*.nested",
255+
'regex:/^[\w.\_\-\>]+$/',
256+
new WhitelistedField($this->filterableBy),
257+
],
258+
$prefix.'.*.operator' => [
259+
'sometimes',
260+
'in:<,<=,>,>=,=,!=,like,not like,ilike,not ilike,in,not in,all in,any in',
261+
],
262+
$prefix.'.*.value' => ['nullable'],
263+
$prefix.'.*.nested' => ['sometimes', 'array',],
264+
]);
265+
266+
if ($maxDepth >= $currentDepth) {
267+
$rules = array_merge(
268+
$rules,
269+
$this->getNestedRules("{$prefix}.*.nested", $maxDepth, $rules, ++$currentDepth)
270+
);
271+
}
272+
273+
return $rules;
274+
}
239275
}

src/Drivers/Standard/QueryBuilder.php

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use Illuminate\Support\Arr;
1212
use Illuminate\Support\Str;
1313
use JsonException;
14-
use Orion\Helpers\RequestHelper;
1514
use Orion\Http\Requests\Request;
1615
use RuntimeException;
1716

@@ -102,7 +101,8 @@ public function buildQuery($query, Request $request)
102101
* @param array $includeDescriptors
103102
* @return void
104103
*/
105-
protected function usingTable($table, $callback) {
104+
protected function usingTable($table, $callback)
105+
{
106106
$this->table = $table;
107107
$callback();
108108
unset($this->table);
@@ -143,7 +143,9 @@ public function applyFiltersToQuery($query, Request $request, array $filterDescr
143143
$or = Arr::get($filterDescriptor, 'type', 'and') === 'or';
144144

145145
if (is_array($childrenDescriptors = Arr::get($filterDescriptor, 'nested'))) {
146-
$query->{$or ? 'orWhere' : 'where'}(function ($query) use ($request, $childrenDescriptors) { $this->applyFiltersToQuery($query, $request, $childrenDescriptors); });
146+
$query->{$or ? 'orWhere' : 'where'}(function ($query) use ($request, $childrenDescriptors) {
147+
$this->applyFiltersToQuery($query, $request, $childrenDescriptors);
148+
});
147149
} elseif (strpos($filterDescriptor['field'], '.') !== false) {
148150
$relation = $this->relationsResolver->relationFromParamConstraint($filterDescriptor['field']);
149151
$relationField = $this->relationsResolver->relationFieldFromParamConstraint($filterDescriptor['field']);
@@ -351,7 +353,7 @@ public function getQualifiedFieldName(string $field): string
351353
* @param string $relation
352354
* @return string
353355
*/
354-
public function getTableNameFromRelation(string $relation):string
356+
public function getTableNameFromRelation(string $relation): string
355357
{
356358
return (new $this->resourceModelClass)->$relation()->getModel()->getTable();
357359
}
@@ -527,9 +529,13 @@ public function applyAggregatesToQuery($query, Request $request, array $aggregat
527529
$aggregateDescriptors = $aggregateDescriptors->merge(
528530
collect(explode(',', $request->query("with_$aggregateFunction", '')))
529531
->filter()
530-
->map(function($include) use ($aggregateFunction) {
532+
->map(function ($include) use ($aggregateFunction) {
531533
$explodedInclude = explode('.', $include);
532-
return ['relation' => $explodedInclude[0], 'field' => $explodedInclude[1] ?? '*', 'type' => $aggregateFunction];
534+
return [
535+
'relation' => $explodedInclude[0],
536+
'field' => $explodedInclude[1] ?? '*',
537+
'type' => $aggregateFunction,
538+
];
533539
})->all()
534540
);
535541
}
@@ -544,17 +550,30 @@ public function applyAggregatesToQuery($query, Request $request, array $aggregat
544550
);
545551
}
546552

547-
$query->withAggregate([$aggregateDescriptor['relation'] => function (Builder $aggregateQuery) use ($aggregateDescriptor, $request) {
548-
$this->usingTable($this->getTableNameFromRelation($aggregateDescriptor['relation']), function () use ($request, $aggregateQuery, $aggregateDescriptor) {
549-
$this->applyFiltersToQuery($aggregateQuery, $request, $this->removeFieldPrefixFromFields($aggregateDescriptor['filters'] ?? [], $aggregateDescriptor['relation'].'.'));
550-
});
551-
}], $aggregateDescriptor['field'] ?? '*', $aggregateDescriptor['type']);
553+
$query->withAggregate([
554+
$aggregateDescriptor['relation'] => function (Builder $aggregateQuery) use (
555+
$aggregateDescriptor,
556+
$request
557+
) {
558+
$this->usingTable(
559+
$this->getTableNameFromRelation($aggregateDescriptor['relation']),
560+
function () use ($request, $aggregateQuery, $aggregateDescriptor) {
561+
$this->applyFiltersToQuery(
562+
$aggregateQuery,
563+
$request,
564+
$this->removeFieldPrefixFromFields(
565+
$aggregateDescriptor['filters'] ?? [],
566+
$aggregateDescriptor['relation'].'.'
567+
)
568+
);
569+
}
570+
);
571+
},
572+
], $aggregateDescriptor['field'] ?? '*', $aggregateDescriptor['type']);
552573
}
553574
}
554575

555576

556-
557-
558577
/**
559578
* Apply eager loading relations to the query.
560579
*
@@ -571,7 +590,7 @@ public function applyIncludesToQuery($query, Request $request, array $includeDes
571590
$includeDescriptors =
572591
collect(explode(',', $request->query('include', '')))
573592
->filter()
574-
->map(function($include) {
593+
->map(function ($include) {
575594
return ['relation' => $include];
576595
})
577596
->merge($request->post('includes', []));
@@ -580,15 +599,26 @@ public function applyIncludesToQuery($query, Request $request, array $includeDes
580599
foreach ($includeDescriptors as $includeDescriptor) {
581600
$query->with([
582601
$includeDescriptor['relation'] => function (Relation $includeQuery) use ($includeDescriptor, $request) {
583-
$this->usingTable($this->getTableNameFromRelation($includeDescriptor['relation']), function () use ($request, $includeQuery, $includeDescriptor) {
584-
$this->applyFiltersToQuery($includeQuery, $request, $this->removeFieldPrefixFromFields($includeDescriptor['filters'] ?? [], $includeDescriptor['relation'].'.'));
585-
});
586-
}
602+
$this->usingTable(
603+
$this->getTableNameFromRelation($includeDescriptor['relation']),
604+
function () use ($request, $includeQuery, $includeDescriptor) {
605+
$this->applyFiltersToQuery(
606+
$includeQuery,
607+
$request,
608+
$this->removeFieldPrefixFromFields(
609+
$includeDescriptor['filters'] ?? [],
610+
$includeDescriptor['relation'].'.'
611+
)
612+
);
613+
}
614+
);
615+
},
587616
]);
588617
}
589618
}
590619

591-
protected function removeFieldPrefixFromFields(array $array, string $search) {
620+
protected function removeFieldPrefixFromFields(array $array, string $search)
621+
{
592622
return collect($array)
593623
->transform(function ($item) use ($search) {
594624
if (isset($item['nested'])) {
@@ -597,7 +627,7 @@ protected function removeFieldPrefixFromFields(array $array, string $search) {
597627
$item['field'] = Str::replaceFirst($search, '', $item['field']);
598628
}
599629

600-
return $item;
630+
return $item;
601631
})
602632
->all();
603633
}

0 commit comments

Comments
 (0)