Skip to content

Commit

Permalink
* move method encodeNextCursor() & decodeCursor() into a new clas…
Browse files Browse the repository at this point in the history
…s `CursorCodec` and add it as ctor param for DI @ `App\Http\PostsQuery\BaseQuery`

* move method `encodeNextCursor()` & `decodeCursor()` into a new Class `CursorCodecTest` @ `Tests\Feature\App\Http\PostsQuery\BaseQuery`
* fix not injecting the first param in the ctor of class `ParamsValidator` since 57363c5 @ `App\Http\Controllers\PostsQuery`
* renamed from class `Param` @ `App\Http\PostsQuery\QueryParam`
@ be
  • Loading branch information
n0099 committed Sep 21, 2024
1 parent 3c41563 commit 7b1131c
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 149 deletions.
10 changes: 7 additions & 3 deletions be/app/Http/Controllers/PostsQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,29 @@
use App\Eloquent\Model\Forum;
use App\Eloquent\Model\User;
use Barryvdh\Debugbar\LaravelDebugbar;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class PostsQuery extends Controller

Check warning on line 18 in be/app/Http/Controllers/PostsQuery.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpmd

PostsQuery.php: The class PostsQuery has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13. (CouplingBetweenObjects, Design Rules)
{
public function __construct(private readonly LaravelDebugbar $debugbar) {}
public function __construct(
private readonly LaravelDebugbar $debugbar,
protected Container $app,
) {}

public function query(\Illuminate\Http\Request $request): array
{
$validator = new ParamsValidator(Helper::jsonDecode(
$validator = $this->app->makeWith(ParamsValidator::class, ['params' => Helper::jsonDecode(
$request->validate([
'cursor' => [ // https://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data
// (,|$)|,){5,6} means allow at most 5~6 parts of base64 segment or empty string to exist
'regex:/^(([A-Za-z0-9-_]{4})*([A-Za-z0-9-_]{2,3})(,|$)|,){5,6}$/',
],
'query' => 'json|required',
])['query'],
));
)]);
$params = $validator->params;

$postIDParams = $params->pick(...Helper::POST_ID);
Expand Down
111 changes: 8 additions & 103 deletions be/app/Http/PostsQuery/BaseQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
use Closure;
use Barryvdh\Debugbar\LaravelDebugbar;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\Cursor;
use Illuminate\Pagination\CursorPaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

abstract class BaseQuery
{
Expand All @@ -37,6 +35,7 @@ abstract public function query(QueryParams $params, ?string $cursor): self;

public function __construct(
private readonly LaravelDebugbar $debugbar,
private readonly CursorCodec $cursorCodec,
private readonly int $perPageItems = 50,
) {}

Expand Down Expand Up @@ -76,7 +75,7 @@ protected function setResult(
$queriesWithOrderBy = $queries->map($addOrderByForBuilder);
$cursorsKeyByPostType = null;
if ($cursorParamValue !== null) {
$cursorsKeyByPostType = $this->decodeCursor($cursorParamValue);
$cursorsKeyByPostType = $this->cursorCodec->decodeCursor($cursorParamValue, $this->orderByField);
// remove queries for post types with encoded cursor ',,'
$queriesWithOrderBy = $queriesWithOrderBy->intersectByKeys($cursorsKeyByPostType);
}
Expand All @@ -103,112 +102,18 @@ protected function setResult(
$this->queryResultPages = [
'currentCursor' => $cursorParamValue ?? '',
'nextCursor' => $hasMore
? $this->encodeNextCursor($queryByPostIDParamName === null
? $postsKeyByTypePluralName
: $postsKeyByTypePluralName->except([Helper::POST_ID_TO_TYPE_PLURAL[$queryByPostIDParamName]]))
? $this->cursorCodec->encodeNextCursor(
$queryByPostIDParamName === null
? $postsKeyByTypePluralName
: $postsKeyByTypePluralName->except([Helper::POST_ID_TO_TYPE_PLURAL[$queryByPostIDParamName]]),
$this->orderByField,
)
: null,
];

$this->debugbar->stopMeasure('setResult');
}

/** @param Collection<string, Post> $postsKeyByTypePluralName */
public function encodeNextCursor(Collection $postsKeyByTypePluralName): string
{
$encodedCursorsKeyByPostType = $postsKeyByTypePluralName
->mapWithKeys(static fn(Collection $posts, string $type) => [
Helper::POST_TYPE_PLURAL_TO_TYPE[$type] => $posts->last(), // null when no posts
]) // [singularPostTypeName => lastPostInResult]
->filter() // remove post types that have no posts
->map(fn(Post $post, string $typePluralName) => [ // [postID, orderByField]
$post->getAttribute(Helper::POST_TYPE_TO_ID[$typePluralName]),
$post->getAttribute($this->orderByField),
])
->map(static fn(array $cursors) => collect($cursors)
->map(static function (int|string $cursor): string {
if ($cursor === 0) { // quick exit to keep 0 as is
// to prevent packed 0 with the default format 'P' after 0x00 trimming is an empty string
// that will be confused with post types without a cursor that is a blank encoded cursor ',,'
return '0';
}
$prefix = match (true) {
\is_int($cursor) && $cursor < 0 => '-',
\is_string($cursor) => 'S',
default => '',
};

$value = \is_int($cursor)
// remove trailing 0x00 for an unsigned int or 0xFF for a signed negative int
? rtrim(pack('P', $cursor), $cursor >= 0 ? "\x00" : "\xFF")
: ($prefix === 'S'
// keep string as is since encoded string will always longer than the original string
? $cursor
: throw new \RuntimeException('Invalid cursor value'));
if ($prefix !== 'S') {
// https://en.wikipedia.org/wiki/Base64#URL_applications
$value = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($value));
}

return $prefix . ($prefix === '' ? '' : ':') . $value;
})
->join(','));
return collect(Helper::POST_TYPES)
// merge cursors into flipped Helper::POST_TYPES with the same post type key
// value of keys that non exists in $encodedCursorsKeyByPostType will remain as int
->flip()->merge($encodedCursorsKeyByPostType)
// if the flipped value is a default int key there's no posts of this type
// (type key not exists in $postsKeyByTypePluralName)
// so we just return an empty ',' as placeholder
->map(static fn(string|int $cursor) => \is_int($cursor) ? ',' : $cursor)
->join(',');
}

/** @psalm-return Collection<'reply'|'subReply'|'thread', Cursor> */
public function decodeCursor(string $encodedCursors): Collection
{
return collect(Helper::POST_TYPES)
->combine(Str::of($encodedCursors)
->explode(',')
->map(static function (string $encodedCursor): int|string|null {
/**
* @var string $cursor
* @var string $prefix
*/
[$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 2, null);
if ($cursor === null) { // no prefix being provided means the value of cursor is a positive int
$cursor = $prefix;
$prefix = '';
}
return $cursor === '0' ? 0 : match ($prefix) { // keep 0 as is
'S' => $cursor, // string literal is not base64 encoded
default => ((array) (
unpack(
format: 'P',
string: str_pad( // re-add removed trailing 0x00 or 0xFF
base64_decode(
// https://en.wikipedia.org/wiki/Base64#URL_applications
str_replace(['-', '_'], ['+', '/'], $cursor),
),
length: 8,
pad_string: $prefix === '-' ? "\xFF" : "\x00",
),
)
))[1], // the returned array of unpack() will starts index from 1
};
})
->chunk(2) // split six values into three post type pairs
->map(static fn(Collection $i) => $i->values())) // reorder keys after chunk
->mapWithKeys(fn(Collection $cursors, string $postType) =>
[$postType =>
$cursors->mapWithKeys(fn(int|string|null $cursor, int $index) =>
[$index === 0 ? Helper::POST_TYPE_TO_ID[$postType] : $this->orderByField => $cursor]),
])
// filter out cursors with all fields value being null, their encoded cursor is ',,'
->reject(static fn(Collection $cursors) =>
$cursors->every(static fn(int|string|null $cursor) => $cursor === null))
->map(static fn(Collection $cursors) => new Cursor($cursors->toArray()));
}

/**
* Union builders pagination $unionMethodName data by $unionStatement
*
Expand Down
109 changes: 109 additions & 0 deletions be/app/Http/PostsQuery/CursorCodec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

namespace App\Http\PostsQuery;

use App\Eloquent\Model\Post\Post;
use App\Helper;
use Illuminate\Pagination\Cursor;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

class CursorCodec
{
/** @param Collection<string, Post> $postsKeyByTypePluralName */
public function encodeNextCursor(Collection $postsKeyByTypePluralName, string $orderByField): string
{
$encodedCursorsKeyByPostType = $postsKeyByTypePluralName
->mapWithKeys(static fn(Collection $posts, string $type) => [
Helper::POST_TYPE_PLURAL_TO_TYPE[$type] => $posts->last(), // null when no posts
]) // [singularPostTypeName => lastPostInResult]
->filter() // remove post types that have no posts
->map(fn(Post $post, string $typePluralName) => [ // [postID, orderByField]
$post->getAttribute(Helper::POST_TYPE_TO_ID[$typePluralName]),
$post->getAttribute($orderByField),
])
->map(static fn(array $cursors) => collect($cursors)
->map(static function (int|string $cursor): string {
if ($cursor === 0) { // quick exit to keep 0 as is
// to prevent packed 0 with the default format 'P' after 0x00 trimming is an empty string
// that will be confused with post types without a cursor that is a blank encoded cursor ',,'
return '0';
}
$prefix = match (true) {
\is_int($cursor) && $cursor < 0 => '-',

Check warning on line 33 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (ubuntu-latest) / phpunit-infection

Escaped Mutant for Mutator "LessThan": --- Original +++ New @@ @@ return '0'; } $prefix = match (true) { - \is_int($cursor) && $cursor < 0 => '-', + \is_int($cursor) && $cursor <= 0 => '-', \is_string($cursor) => 'S', default => '', };

Check warning on line 33 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpunit-infection

Escaped Mutant for Mutator "LessThan": --- Original +++ New @@ @@ return '0'; } $prefix = match (true) { - \is_int($cursor) && $cursor < 0 => '-', + \is_int($cursor) && $cursor <= 0 => '-', \is_string($cursor) => 'S', default => '', };

Check warning on line 33 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (macos-latest) / phpunit-infection

Escaped Mutant for Mutator "LessThan": --- Original +++ New @@ @@ return '0'; } $prefix = match (true) { - \is_int($cursor) && $cursor < 0 => '-', + \is_int($cursor) && $cursor <= 0 => '-', \is_string($cursor) => 'S', default => '', };
\is_string($cursor) => 'S',
default => '',
};

$value = \is_int($cursor)
// remove trailing 0x00 for an unsigned int or 0xFF for a signed negative int
? rtrim(pack('P', $cursor), $cursor >= 0 ? "\x00" : "\xFF")

Check warning on line 40 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (ubuntu-latest) / phpunit-infection

Escaped Mutant for Mutator "GreaterThanOrEqualTo": --- Original +++ New @@ @@ \is_string($cursor) => 'S', default => '', }; - $value = \is_int($cursor) ? rtrim(pack('P', $cursor), $cursor >= 0 ? "\x00" : "\xff") : ($prefix === 'S' ? $cursor : throw new \RuntimeException('Invalid cursor value')); + $value = \is_int($cursor) ? rtrim(pack('P', $cursor), $cursor > 0 ? "\x00" : "\xff") : ($prefix === 'S' ? $cursor : throw new \RuntimeException('Invalid cursor value')); if ($prefix !== 'S') { // https://en.wikipedia.org/wiki/Base64#URL_applications $value = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($value));

Check warning on line 40 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpunit-infection

Escaped Mutant for Mutator "GreaterThanOrEqualTo": --- Original +++ New @@ @@ \is_string($cursor) => 'S', default => '', }; - $value = \is_int($cursor) ? rtrim(pack('P', $cursor), $cursor >= 0 ? "\x00" : "\xff") : ($prefix === 'S' ? $cursor : throw new \RuntimeException('Invalid cursor value')); + $value = \is_int($cursor) ? rtrim(pack('P', $cursor), $cursor > 0 ? "\x00" : "\xff") : ($prefix === 'S' ? $cursor : throw new \RuntimeException('Invalid cursor value')); if ($prefix !== 'S') { // https://en.wikipedia.org/wiki/Base64#URL_applications $value = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($value));

Check warning on line 40 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (macos-latest) / phpunit-infection

Escaped Mutant for Mutator "GreaterThanOrEqualTo": --- Original +++ New @@ @@ \is_string($cursor) => 'S', default => '', }; - $value = \is_int($cursor) ? rtrim(pack('P', $cursor), $cursor >= 0 ? "\x00" : "\xff") : ($prefix === 'S' ? $cursor : throw new \RuntimeException('Invalid cursor value')); + $value = \is_int($cursor) ? rtrim(pack('P', $cursor), $cursor > 0 ? "\x00" : "\xff") : ($prefix === 'S' ? $cursor : throw new \RuntimeException('Invalid cursor value')); if ($prefix !== 'S') { // https://en.wikipedia.org/wiki/Base64#URL_applications $value = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($value));
: ($prefix === 'S'
// keep string as is since encoded string will always longer than the original string
? $cursor
: throw new \RuntimeException('Invalid cursor value'));
if ($prefix !== 'S') {
// https://en.wikipedia.org/wiki/Base64#URL_applications
$value = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($value));
}

return $prefix . ($prefix === '' ? '' : ':') . $value;
})
->join(','));
return collect(Helper::POST_TYPES)
// merge cursors into flipped Helper::POST_TYPES with the same post type key
// value of keys that non exists in $encodedCursorsKeyByPostType will remain as int
->flip()->merge($encodedCursorsKeyByPostType)
// if the flipped value is a default int key there's no posts of this type
// (type key not exists in $postsKeyByTypePluralName)
// so we just return an empty ',' as placeholder
->map(static fn(string|int $cursor) => \is_int($cursor) ? ',' : $cursor)
->join(',');
}

/** @psalm-return Collection<'reply'|'subReply'|'thread', Cursor> */
public function decodeCursor(string $encodedCursors, string $orderByField): Collection
{
return collect(Helper::POST_TYPES)
->combine(Str::of($encodedCursors)
->explode(',')
->map(static function (string $encodedCursor): int|string|null {
/**
* @var string $cursor
* @var string $prefix
*/
[$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 2, null);

Check warning on line 75 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (ubuntu-latest) / phpunit-infection

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * @var string $cursor * @var string $prefix */ - [$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 2, null); + [$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 3, null); if ($cursor === null) { // no prefix being provided means the value of cursor is a positive int $cursor = $prefix;

Check warning on line 75 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpunit-infection

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * @var string $cursor * @var string $prefix */ - [$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 2, null); + [$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 3, null); if ($cursor === null) { // no prefix being provided means the value of cursor is a positive int $cursor = $prefix;

Check warning on line 75 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (macos-latest) / phpunit-infection

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * @var string $cursor * @var string $prefix */ - [$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 2, null); + [$prefix, $cursor] = array_pad(explode(':', $encodedCursor), 3, null); if ($cursor === null) { // no prefix being provided means the value of cursor is a positive int $cursor = $prefix;
if ($cursor === null) { // no prefix being provided means the value of cursor is a positive int
$cursor = $prefix;
$prefix = '';
}
return $cursor === '0' ? 0 : match ($prefix) { // keep 0 as is
'S' => $cursor, // string literal is not base64 encoded
default => ((array) (

Check warning on line 82 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (ubuntu-latest) / phpunit-infection

Escaped Mutant for Mutator "CastArray": --- Original +++ New @@ @@ // keep 0 as is 'S' => $cursor, // string literal is not base64 encoded - default => ((array) unpack(format: 'P', string: str_pad( + default => unpack(format: 'P', string: str_pad( // re-add removed trailing 0x00 or 0xFF base64_decode( // https://en.wikipedia.org/wiki/Base64#URL_applications @@ @@ ), length: 8, pad_string: $prefix === '-' ? "\xff" : "\x00" - )))[1], + ))[1], }; })->chunk(2)->map(static fn(Collection $i) => $i->values()))->mapWithKeys(fn(Collection $cursors, string $postType) => [$postType => $cursors->mapWithKeys(fn(int|string|null $cursor, int $index) => [$index === 0 ? Helper::POST_TYPE_TO_ID[$postType] : $orderByField => $cursor])])->reject(static fn(Collection $cursors) => $cursors->every(static fn(int|string|null $cursor) => $cursor === null))->map(static fn(Collection $cursors) => new Cursor($cursors->toArray())); } }

Check warning on line 82 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpunit-infection

Escaped Mutant for Mutator "CastArray": --- Original +++ New @@ @@ // keep 0 as is 'S' => $cursor, // string literal is not base64 encoded - default => ((array) unpack(format: 'P', string: str_pad( + default => unpack(format: 'P', string: str_pad( // re-add removed trailing 0x00 or 0xFF base64_decode( // https://en.wikipedia.org/wiki/Base64#URL_applications @@ @@ ), length: 8, pad_string: $prefix === '-' ? "\xff" : "\x00" - )))[1], + ))[1], }; })->chunk(2)->map(static fn(Collection $i) => $i->values()))->mapWithKeys(fn(Collection $cursors, string $postType) => [$postType => $cursors->mapWithKeys(fn(int|string|null $cursor, int $index) => [$index === 0 ? Helper::POST_TYPE_TO_ID[$postType] : $orderByField => $cursor])])->reject(static fn(Collection $cursors) => $cursors->every(static fn(int|string|null $cursor) => $cursor === null))->map(static fn(Collection $cursors) => new Cursor($cursors->toArray())); } }

Check warning on line 82 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (macos-latest) / phpunit-infection

Escaped Mutant for Mutator "CastArray": --- Original +++ New @@ @@ // keep 0 as is 'S' => $cursor, // string literal is not base64 encoded - default => ((array) unpack(format: 'P', string: str_pad( + default => unpack(format: 'P', string: str_pad( // re-add removed trailing 0x00 or 0xFF base64_decode( // https://en.wikipedia.org/wiki/Base64#URL_applications @@ @@ ), length: 8, pad_string: $prefix === '-' ? "\xff" : "\x00" - )))[1], + ))[1], }; })->chunk(2)->map(static fn(Collection $i) => $i->values()))->mapWithKeys(fn(Collection $cursors, string $postType) => [$postType => $cursors->mapWithKeys(fn(int|string|null $cursor, int $index) => [$index === 0 ? Helper::POST_TYPE_TO_ID[$postType] : $orderByField => $cursor])])->reject(static fn(Collection $cursors) => $cursors->every(static fn(int|string|null $cursor) => $cursor === null))->map(static fn(Collection $cursors) => new Cursor($cursors->toArray())); } }
unpack(
format: 'P',
string: str_pad( // re-add removed trailing 0x00 or 0xFF
base64_decode(
// https://en.wikipedia.org/wiki/Base64#URL_applications
str_replace(['-', '_'], ['+', '/'], $cursor),
),
length: 8,

Check warning on line 90 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (ubuntu-latest) / phpunit-infection

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ // https://en.wikipedia.org/wiki/Base64#URL_applications str_replace(['-', '_'], ['+', '/'], $cursor) ), - length: 8, + length: 9, pad_string: $prefix === '-' ? "\xff" : "\x00" )))[1], };

Check warning on line 90 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpunit-infection

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ // https://en.wikipedia.org/wiki/Base64#URL_applications str_replace(['-', '_'], ['+', '/'], $cursor) ), - length: 8, + length: 9, pad_string: $prefix === '-' ? "\xff" : "\x00" )))[1], };

Check warning on line 90 in be/app/Http/PostsQuery/CursorCodec.php

View workflow job for this annotation

GitHub Actions / runs-on (macos-latest) / phpunit-infection

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ // https://en.wikipedia.org/wiki/Base64#URL_applications str_replace(['-', '_'], ['+', '/'], $cursor) ), - length: 8, + length: 9, pad_string: $prefix === '-' ? "\xff" : "\x00" )))[1], };
pad_string: $prefix === '-' ? "\xFF" : "\x00",
),
)
))[1], // the returned array of unpack() will starts index from 1
};
})
->chunk(2) // split six values into three post type pairs
->map(static fn(Collection $i) => $i->values())) // reorder keys after chunk
->mapWithKeys(fn(Collection $cursors, string $postType) =>
[$postType =>
$cursors->mapWithKeys(fn(int|string|null $cursor, int $index) =>
[$index === 0 ? Helper::POST_TYPE_TO_ID[$postType] : $orderByField => $cursor]),
])
// filter out cursors with all fields value being null, their encoded cursor is ',,'
->reject(static fn(Collection $cursors) =>
$cursors->every(static fn(int|string|null $cursor) => $cursor === null))
->map(static fn(Collection $cursors) => new Cursor($cursors->toArray()));
}
}
2 changes: 1 addition & 1 deletion be/app/Http/PostsQuery/IndexQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function query(QueryParams $params, ?string $cursor): self
/** @var array<string, mixed> $flatParams key by param name */
$flatParams = array_reduce(
$params->pick(...ParamsValidator::UNIQUE_PARAMS_NAME, ...Helper::POST_ID),
static fn(array $accParams, Param $param) =>
static fn(array $accParams, QueryParam $param) =>
[...$accParams, $param->name => $param->value, ...$param->getAllSub()],
[],
); // flatten unique query params
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace App\Http\PostsQuery;

class Param
class QueryParam
{
public string $name;

Expand Down
14 changes: 7 additions & 7 deletions be/app/Http/PostsQuery/QueryParams.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

class QueryParams
{
/** @var Param[] */
/** @var QueryParam[] */
protected array $params;

/** @param array[] $params */
public function __construct(array $params)
{
$this->params = array_map(static fn($p) => new Param($p), $params);
$this->params = array_map(static fn($p) => new QueryParam($p), $params);
}

/** @psalm-return int<0, max> */
Expand All @@ -22,8 +22,8 @@ public function count(): int
}

/**
* @return Param[]
* @psalm-return list<Param>
* @return QueryParam[]
* @psalm-return list<QueryParam>
*/
public function pick(string ...$names): array
{
Expand All @@ -35,8 +35,8 @@ public function pick(string ...$names): array
}

/**
* @return Param[]
* @psalm-return list<Param>
* @return QueryParam[]
* @psalm-return list<QueryParam>
*/
public function omit(string ...$names): array
{
Expand Down Expand Up @@ -71,7 +71,7 @@ public function addDefaultValueOnUniqueParams(): void
];
foreach ($uniqueParamsDefaultValue as $name => $value) {
// add unique params with default value when it's not presented in $this->params
$paramFilledWithDefaults = new Param([
$paramFilledWithDefaults = new QueryParam([
$name => $this->getUniqueParamValue($name) ?? $value['value'],
...($this->pick($name)[0]->subParam ?? $value['subParam'] ?? []),
]);
Expand Down
2 changes: 1 addition & 1 deletion be/app/Http/PostsQuery/SearchQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function query(QueryParams $params, ?string $cursor): self
*/
private static function applyQueryParamsOnQuery(

Check warning on line 53 in be/app/Http/PostsQuery/SearchQuery.php

View workflow job for this annotation

GitHub Actions / runs-on (windows-latest) / phpmd

SearchQuery.php: The method applyQueryParamsOnQuery() has a Cyclomatic Complexity of 12. The configured cyclomatic complexity threshold is 10. (CyclomaticComplexity, Code Size Rules)
Builder $query,
Param $param,
QueryParam $param,
?Collection &$outCachedUserQueryResult,
): Builder {
$name = $param->name;
Expand Down
Loading

0 comments on commit 7b1131c

Please sign in to comment.