Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/Api/V2/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected function readRelatedAwards(
'gameIfApplicable.system',
]);

if ($request->filter()?->value('gameAwards') === 'highest') {
if ($request->filter()?->value('gameAwardTier') === 'highest') {
$allAwardsQuery->highestGameAwardPerGame();
}

Expand Down
55 changes: 55 additions & 0 deletions app/Api/V2/PlayerGames/PlayerGameAwardKind.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace App\Api\V2\PlayerGames;

use App\Community\Enums\AwardType;
use App\Models\PlayerGame;
use App\Platform\Enums\UnlockMode;
use Illuminate\Database\Eloquent\Builder;

/**
* Beaten milestones a player can reach on a game, matched on the denormalized
* player_games timestamps rather than PlayerBadge award rows. The values
* intentionally mirror the user-awards filter[kind] vocabulary.
*
* Completion and mastery are deliberately absent: games are beaten, but
* achievement sets are completed or mastered. That filtering belongs on the
* player-achievement-sets resource.
*/
enum PlayerGameAwardKind: string
{
case BeatenCasual = 'beaten-casual';
case BeatenHardcore = 'beaten-hardcore';

/**
* @param Builder<PlayerGame> $query
* @return Builder<PlayerGame>
*/
public function apply(Builder $query): Builder
{
return match ($this) {
self::BeatenCasual => $this->applyBeaten($query, 'beaten_at', UnlockMode::Casual),
self::BeatenHardcore => $this->applyBeaten($query, 'beaten_hardcore_at', UnlockMode::Hardcore),
};
}

/**
* @param Builder<PlayerGame> $query
* @return Builder<PlayerGame>
*/
private function applyBeaten(Builder $query, string $timestampColumn, int $unlockMode): Builder
{
return $query->where(function (Builder $query) use ($timestampColumn, $unlockMode) {
$query
->whereNotNull($timestampColumn)
->orWhereHas('badges', function (Builder $query) use ($unlockMode) {
$query
->whereColumn('user_awards.user_id', 'player_games.user_id')
->where('award_type', AwardType::Mastery)
->where('award_tier', $unlockMode);
});
});
}
}
82 changes: 82 additions & 0 deletions app/Api/V2/PlayerGames/PlayerGameAwardKindFilter.php
Comment thread
Jamiras marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace App\Api\V2\PlayerGames;

use App\Models\PlayerGame;
use Illuminate\Database\Eloquent\Builder;
use LaravelJsonApi\Core\Exceptions\JsonApiException;
use LaravelJsonApi\Eloquent\Contracts\Filter;

final class PlayerGameAwardKindFilter implements Filter
{
public function key(): string
{
return 'awardKind';
}

public function isSingular(): bool
{
return false;
}

/**
* @param Builder<PlayerGame> $query
* @return Builder<PlayerGame>
*/
public function apply($query, $value)
{
$rawKinds = $this->parseRawKinds((string) $value);

if ($rawKinds === []) {
return $query;
}

$kinds = $this->parseKinds($rawKinds);

return $query->where(function (Builder $query) use ($kinds) {
foreach ($kinds as $kind) {
$query->orWhere(fn (Builder $query) => $kind->apply($query));
}
});
Comment thread
Jamiras marked this conversation as resolved.
}

/**
* @return list<string>
*/
private function parseRawKinds(string $value): array
{
return collect(explode(',', $value))
->map(fn (string $kind) => trim($kind))
->filter()
->unique()
->values()
->all();
}

/**
* @return list<PlayerGameAwardKind>
*/
private function parseKinds(array $kinds): array
{
return collect($kinds)
->map(function (string $kind) {
$parsedKind = PlayerGameAwardKind::tryFrom(trim($kind));

if (!$parsedKind) {
throw JsonApiException::error([
'status' => '400',
'code' => 'invalid_filter',
'title' => 'Invalid Filter',
'detail' => "Unknown award kind [{$kind}].",
]);
}

return $parsedKind;
})
->unique(fn (PlayerGameAwardKind $kind) => $kind->value)
->values()
->all();
}
}
5 changes: 3 additions & 2 deletions app/Api/V2/PlayerGames/PlayerGameSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
use LaravelJsonApi\Eloquent\Fields\Relations\BelongsTo;
use LaravelJsonApi\Eloquent\Fields\Relations\BelongsToMany;
use LaravelJsonApi\Eloquent\Fields\Relations\HasManyThrough;
use LaravelJsonApi\Eloquent\Filters\Where;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Filters\WhereIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;

Expand Down Expand Up @@ -91,7 +91,8 @@ public function filters(): array
{
return [
WhereIdIn::make($this)->delimiter(','),
Where::make('gameId', 'game_id'),
WhereIn::make('gameId', 'game_id')->delimiter(','),
new PlayerGameAwardKindFilter(),
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
use LaravelJsonApi\Core\Exceptions\JsonApiException;
use LaravelJsonApi\Eloquent\Contracts\Filter;

final class UserAwardGameAwardsFilter implements Filter
final class UserAwardGameAwardTierFilter implements Filter
{
public function key(): string
{
return 'gameAwards';
return 'gameAwardTier';
}
Comment thread
wescopeland marked this conversation as resolved.

public function isSingular(): bool
Expand All @@ -36,7 +36,7 @@ public function apply($query, $value)
'status' => '400',
'code' => 'invalid_filter',
'title' => 'Invalid Filter',
'detail' => "Unknown game awards filter [{$mode}].",
'detail' => "Unknown game award tier filter [{$mode}].",
]),
};
}
Expand Down
2 changes: 1 addition & 1 deletion app/Api/V2/UserAwards/UserAwardSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function filters(): array
Scope::make('awardedTo'),
Scope::make('eventId', 'forEventId'),
Scope::make('gameId', 'forGameId'),
new UserAwardGameAwardsFilter(),
new UserAwardGameAwardTierFilter(),
new UserAwardKindFilter(),
];
}
Expand Down
4 changes: 2 additions & 2 deletions app/Api/V2/Users/UserSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use App\Api\V2\Tickets\TicketStateFilter;
use App\Api\V2\Tickets\TicketTypeFilter;
use App\Api\V2\Tickets\UserUlidFilter;
use App\Api\V2\UserAwards\UserAwardGameAwardsFilter;
use App\Api\V2\UserAwards\UserAwardGameAwardTierFilter;
use App\Api\V2\UserAwards\UserAwardKindFilter;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
Expand Down Expand Up @@ -133,7 +133,7 @@ public function fields(): array
Scope::make('awardedTo'),
Scope::make('eventId', 'forEventId'),
Scope::make('gameId', 'forGameId'),
new UserAwardGameAwardsFilter(),
new UserAwardGameAwardTierFilter(),
new UserAwardKindFilter(),
)
->readOnly(),
Expand Down
Loading
Loading