refactor(graphql): remove resolver classes (#1176)

This commit is contained in:
Kyrch
2026-04-19 06:28:58 -03:00
committed by GitHub
parent b1d7e9ed30
commit 80d59f7e45
32 changed files with 281 additions and 613 deletions
-96
View File
@@ -1,96 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers;
use App\GraphQL\Schema\Mutations\BaseMutation;
use Illuminate\Pipeline\Pipeline;
use Illuminate\Routing\ControllerMiddlewareOptions;
use Illuminate\Routing\FiltersControllerMiddleware;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Validator;
abstract class BaseResolver
{
final public const MODEL = 'model';
/**
* HTTP middleware stack that should be run before the resolver action.
*
* Each element is an array with keys `middleware` and `options`. The
* `options` array is the same structure that the `ControllerMiddlewareOptions`
* helper understands (see `only` / `except`). This mirrors the behaviour of
* Laravel controllers so you can register middleware exactly the same way:
*
* $this->middleware('auth')->only('store');
* $this->middleware(['auth', 'log'])->except('destroy');
*
* The resolver will evaluate the options against the current action name
* when deciding which middleware to run.
*
* @var array<int, array{middleware:class-string|string,options:array}>
*/
protected array $middleware = [];
/**
* Get the attributes and values that were validated.
*
* @param class-string<BaseMutation> $mutation
* @return array<string, mixed>
*/
public function validated(array $args, string $mutation): array
{
$mutationInstance = App::make($mutation);
$validator = Validator::make($args, $mutationInstance->rulesForValidation($args));
return $validator->validated();
}
/**
* Run the middleware pipeline for the given action.
*/
protected function runMiddleware(): void
{
$action = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
$stack = [];
foreach ($this->middleware as $entry) {
$options = $entry['options'] ?? [];
if (FiltersControllerMiddleware::methodExcludedByOptions($action, $options)) {
continue;
}
$stack[] = $entry['middleware'];
}
if ($stack === []) {
return;
}
resolve(Pipeline::class)
->send(request())
->through($stack)
->thenReturn();
}
/**
* Register middleware on the controller.
*
* @param \Closure|array|string $middleware
* @return ControllerMiddlewareOptions
*/
public function middleware($middleware, array $options = [])
{
foreach ((array) $middleware as $m) {
$this->middleware[] = [
'middleware' => $m,
'options' => &$options,
];
}
return new ControllerMiddlewareOptions($options);
}
}
@@ -1,91 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers\List\Playlist;
use App\Actions\Http\Api\List\Playlist\Track\DestroyTrackAction;
use App\Actions\Http\Api\List\Playlist\Track\StoreTrackAction;
use App\Actions\Http\Api\List\Playlist\Track\UpdateTrackAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Resolvers\BaseResolver;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\CreatePlaylistTrackMutation;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\UpdatePlaylistTrackMutation;
use App\Http\Middleware\Models\List\PlaylistExceedsTrackLimit;
use App\Models\List\Playlist;
use App\Models\List\Playlist\PlaylistTrack;
use Illuminate\Support\Arr;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class PlaylistTrackResolver extends BaseResolver
{
final public const string ATTRIBUTE_ENTRY = 'entryId';
final public const string ATTRIBUTE_VIDEO = 'videoId';
public static Playlist $playlist;
public function __construct(Playlist $playlist)
{
static::$playlist = $playlist;
$this->middleware(EnsureFeaturesAreActive::using(AllowPlaylistManagement::class))->only(['store', 'update', 'destroy']);
$this->middleware(PlaylistExceedsTrackLimit::class)->only(['store']);
}
/**
* @param array<string, mixed> $args
*/
public function store(array $args, StoreTrackAction $action): PlaylistTrack
{
$this->runMiddleware();
/** @var Playlist $playlist */
$playlist = Arr::get($args, 'playlist');
$validated = $this->validated($args, CreatePlaylistTrackMutation::class);
$validated = [
PlaylistTrack::ATTRIBUTE_ENTRY => Arr::integer($validated, 'entryId'),
PlaylistTrack::ATTRIBUTE_VIDEO => Arr::integer($validated, 'videoId'),
];
return $action->store($playlist, PlaylistTrack::query(), $validated);
}
/**
* @param array<string, mixed> $args
*/
public function update(array $args, UpdateTrackAction $action): PlaylistTrack
{
$this->runMiddleware();
/** @var PlaylistTrack $track */
$track = Arr::get($args, self::MODEL);
$validated = $this->validated($args, UpdatePlaylistTrackMutation::class);
$validated = [
PlaylistTrack::ATTRIBUTE_ENTRY => Arr::integer($validated, 'entryId'),
PlaylistTrack::ATTRIBUTE_VIDEO => Arr::integer($validated, 'videoId'),
];
return $action->update($track->playlist, $track, $validated);
}
/**
* @param array<string, mixed> $args
*/
public function destroy(array $args, DestroyTrackAction $action): array
{
$this->runMiddleware();
/** @var PlaylistTrack $track */
$track = Arr::get($args, self::MODEL);
$message = $action->destroy($track->playlist, $track);
return [
'message' => $message,
];
}
}
@@ -1,79 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers\List;
use App\Actions\Http\Api\DestroyAction;
use App\Actions\Http\Api\StoreAction;
use App\Actions\Http\Api\UpdateAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Resolvers\BaseResolver;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\CreatePlaylistMutation;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\UpdatePlaylistMutation;
use App\Http\Middleware\Models\List\UserExceedsPlaylistLimit;
use App\Models\List\Playlist;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class PlaylistResolver extends BaseResolver
{
public function __construct()
{
$this->middleware(EnsureFeaturesAreActive::using(AllowPlaylistManagement::class))->only(['store', 'update', 'destroy']);
$this->middleware(UserExceedsPlaylistLimit::class)->only(['store']);
}
/**
* @param array<string, mixed> $args
* @param StoreAction<Playlist> $action
*/
public function store(array $args, StoreAction $action): Playlist
{
$this->runMiddleware();
$validated = $this->validated($args, CreatePlaylistMutation::class);
$parameters = [
...$validated,
Playlist::ATTRIBUTE_USER => Auth::id(),
];
return $action->store(Playlist::query(), $parameters);
}
/**
* @param array<string, mixed> $args
* @param UpdateAction<Playlist> $action
*/
public function update(array $args, UpdateAction $action): Playlist
{
$this->runMiddleware();
/** @var Playlist $playlist */
$playlist = Arr::pull($args, self::MODEL);
$validated = $this->validated($args, UpdatePlaylistMutation::class);
return $action->update($playlist, $validated);
}
/**
* @param array<string, mixed> $args
* @param DestroyAction<Playlist> $action
*/
public function destroy(array $args, DestroyAction $action): array
{
$this->runMiddleware();
/** @var Playlist $playlist */
$playlist = Arr::get($args, self::MODEL);
$message = $action->forceDelete($playlist);
return [
'message' => $message,
];
}
}
@@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers\List;
use App\Exceptions\GraphQL\ClientForbiddenException;
use App\Features\AllowExternalProfileManagement;
use App\GraphQL\Resolvers\BaseResolver;
use App\Http\Middleware\Api\EnabledOnlyOnLocalhost;
use App\Models\List\ExternalProfile;
use Illuminate\Support\Arr;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class SyncExternalProfileResolver extends BaseResolver
{
public function __construct()
{
$this->middleware(EnabledOnlyOnLocalhost::class);
$this->middleware(EnsureFeaturesAreActive::using(AllowExternalProfileManagement::class))->only(['update']);
}
/**
* @param array<string, mixed> $args
*/
public function update(array $args): array
{
$this->runMiddleware();
/** @var ExternalProfile $profile */
$profile = Arr::pull($args, self::MODEL);
throw_unless(
$profile->canBeSynced(),
ClientForbiddenException::class,
'This external profile cannot be synced at the moment.'
);
$profile->dispatchSyncJob();
return [
'message' => 'Job dispatched.',
];
}
}
@@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers\User;
use App\Contracts\Models\Likeable;
use App\GraphQL\Resolvers\BaseResolver;
use App\GraphQL\Schema\Mutations\Models\User\ToggleLikeMutation;
use App\Models\User\Like;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
class ToggleLikeResolver extends BaseResolver
{
final public const string ATTRIBUTE_ENTRY = 'entry';
final public const string ATTRIBUTE_PLAYLIST = 'playlist';
/**
* @param array<string, mixed> $args
*/
public function store(array $args): ?Like
{
$this->runMiddleware();
$validated = $this->validated($args, ToggleLikeMutation::class);
/** @var Model&Likeable $likeable */
$likeable = Arr::first($validated);
return $likeable->toggleLike(Auth::user());
}
}
@@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers\User;
use App\Actions\Http\Api\StoreAction;
use App\GraphQL\Resolvers\BaseResolver;
use App\GraphQL\Schema\Mutations\Models\User\WatchMutation;
use App\Models\User\WatchHistory;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
class WatchResolver extends BaseResolver
{
final public const string ATTRIBUTE_ENTRY = 'entryId';
final public const string ATTRIBUTE_VIDEO = 'videoId';
/**
* @param array<string, mixed> $args
* @param StoreAction<WatchHistory> $action
*/
public function store(array $args, StoreAction $action): WatchHistory
{
$this->runMiddleware();
$validated = $this->validated($args, WatchMutation::class);
$validated = [
WatchHistory::ATTRIBUTE_ENTRY => Arr::integer($validated, 'entryId'),
WatchHistory::ATTRIBUTE_VIDEO => Arr::integer($validated, 'videoId'),
WatchHistory::ATTRIBUTE_USER => Auth::id(),
];
return $action->store(WatchHistory::query(), $validated);
}
}
@@ -1,97 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Resolvers\Wiki\Anime;
use App\Actions\GraphQL\IndexAction;
use App\GraphQL\Resolvers\BaseResolver;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeason\AnimeYearSeasonSeasonField;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeasonField;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeasonsField;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearYearField;
use App\GraphQL\Schema\Queries\Wiki\AnimeYearsQuery;
use App\GraphQL\Schema\Types\Wiki\AnimeType;
use App\Models\Wiki\Anime;
use App\Rules\GraphQL\Resolver\AnimeYearRule;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
class AnimeYearsResolver extends BaseResolver
{
/**
* @param array<string, mixed> $args
*/
public function index(array $args, ResolveInfo $resolveInfo): mixed
{
$year = Arr::get($args, AnimeYearsQuery::ARGUMENT_YEAR);
$fieldSelection = $resolveInfo->getFieldSelection(1);
Validator::make(['year' => $year], ['year' => new AnimeYearRule($fieldSelection)])
->validate();
return Anime::query()
->distinct([Anime::ATTRIBUTE_YEAR, Anime::ATTRIBUTE_SEASON])
->orderBy(Anime::ATTRIBUTE_YEAR)
->when($year !== null, fn (Builder $query) => $query->whereIn(Anime::ATTRIBUTE_YEAR, $year))
->get([Anime::ATTRIBUTE_YEAR, Anime::ATTRIBUTE_SEASON])
->groupBy(Anime::ATTRIBUTE_YEAR)
->map(fn (Collection $items, int $year): array => [
AnimeYearYearField::FIELD => $year,
AnimeYearSeasonsField::FIELD => $items
->map(fn (Anime $anime): array => [
AnimeYearSeasonSeasonField::FIELD => $anime->season,
'seasonLocalized' => $anime->season->localize(),
'year' => $year, // Needed to query animes on the 'seasons' field.
])
->unique(Anime::ATTRIBUTE_SEASON)
->values()
->toArray(),
])->values();
}
/**
* Resolve the AnimeYearSeasonField.
*
* @param array<string, mixed> $args
*/
public function resolveSeasonField(array $root, array $args): mixed
{
$season = Arr::get($args, AnimeYearSeasonField::ARGUMENT_SEASON);
$year = Arr::get($root, AnimeYearsQuery::ARGUMENT_YEAR);
$seasons = collect(Arr::get($root, 'seasons'));
if ($seasons->doesntContain(fn ($item): bool => $item[AnimeYearSeasonSeasonField::FIELD] === $season)) {
return null;
}
return [
AnimeYearSeasonSeasonField::FIELD => $season,
'seasonLocalized' => $season->localize(),
'year' => $year, // Needed to query animes on the 'season' field.
];
}
/**
* Resolve the AnimeYearSeasonAnimeField.
*/
public function resolveAnimeField(array $root, array $args, ResolveInfo $resolveInfo, IndexAction $action): Paginator
{
$season = Arr::get($root, AnimeYearSeasonSeasonField::FIELD);
$year = Arr::get($root, 'year');
$builder = Anime::query()
// season filter applies only on the 'season' field.
->when($season !== null, fn (Builder $query) => $query->where(Anime::ATTRIBUTE_SEASON, $season->value))
->where(Anime::ATTRIBUTE_YEAR, $year);
return $action->index($builder, $args, new AnimeType(), $resolveInfo);
}
}
@@ -9,8 +9,9 @@ use App\Contracts\GraphQL\Fields\FilterableField;
use App\Contracts\GraphQL\Fields\RequiredOnCreation;
use App\Contracts\GraphQL\Fields\UpdatableField;
use App\GraphQL\Filter\IntFilter;
use App\GraphQL\Resolvers\List\Playlist\PlaylistTrackResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\CreatePlaylistTrackMutation;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\UpdatePlaylistTrackMutation;
use App\Models\List\Playlist\PlaylistTrack;
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
use App\Pivots\Wiki\AnimeThemeEntryVideo;
@@ -50,7 +51,7 @@ class PlaylistTrackEntryIdField extends Field implements CreatableField, Filtera
'integer',
Rule::exists(AnimeThemeEntry::class, AnimeThemeEntry::ATTRIBUTE_ID),
Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_ENTRY)
->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, Arr::get($args, PlaylistTrackResolver::ATTRIBUTE_VIDEO)),
->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, Arr::get($args, CreatePlaylistTrackMutation::ATTRIBUTE_VIDEO)),
];
}
@@ -59,7 +60,7 @@ class PlaylistTrackEntryIdField extends Field implements CreatableField, Filtera
*/
public function getUpdateRules(array $args): array
{
$videoId = Arr::get($args, PlaylistTrackResolver::ATTRIBUTE_VIDEO);
$videoId = Arr::get($args, UpdatePlaylistTrackMutation::ATTRIBUTE_VIDEO);
return [
'sometimes',
@@ -9,8 +9,9 @@ use App\Contracts\GraphQL\Fields\FilterableField;
use App\Contracts\GraphQL\Fields\RequiredOnCreation;
use App\Contracts\GraphQL\Fields\UpdatableField;
use App\GraphQL\Filter\IntFilter;
use App\GraphQL\Resolvers\List\Playlist\PlaylistTrackResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\CreatePlaylistTrackMutation;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\UpdatePlaylistTrackMutation;
use App\Models\List\Playlist\PlaylistTrack;
use App\Models\Wiki\Video;
use App\Pivots\Wiki\AnimeThemeEntryVideo;
@@ -50,7 +51,7 @@ class PlaylistTrackVideoIdField extends Field implements CreatableField, Filtera
'integer',
Rule::exists(Video::class, Video::ATTRIBUTE_ID),
Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_VIDEO)
->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, Arr::get($args, PlaylistTrackResolver::ATTRIBUTE_ENTRY)),
->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, Arr::get($args, CreatePlaylistTrackMutation::ATTRIBUTE_ENTRY)),
];
}
@@ -59,7 +60,7 @@ class PlaylistTrackVideoIdField extends Field implements CreatableField, Filtera
*/
public function getUpdateRules(array $args): array
{
$entryId = Arr::get($args, PlaylistTrackResolver::ATTRIBUTE_ENTRY);
$entryId = Arr::get($args, UpdatePlaylistTrackMutation::ATTRIBUTE_ENTRY);
return [
'sometimes',
@@ -6,8 +6,8 @@ namespace App\GraphQL\Schema\Fields\User\Like;
use App\Contracts\GraphQL\Fields\BindableField;
use App\Contracts\GraphQL\Fields\CreatableField;
use App\GraphQL\Resolvers\User\ToggleLikeResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\Models\User\ToggleLikeMutation;
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
@@ -36,7 +36,7 @@ class LikeAnimeThemeEntryField extends Field implements BindableField, Creatable
public function bindResolver(array $args): AnimeThemeEntry
{
return AnimeThemeEntry::query()
->where(AnimeThemeEntry::ATTRIBUTE_ID, Arr::get($args, ToggleLikeResolver::ATTRIBUTE_ENTRY))
->where(AnimeThemeEntry::ATTRIBUTE_ID, Arr::get($args, ToggleLikeMutation::ATTRIBUTE_ENTRY))
->firstOrFail();
}
@@ -46,9 +46,9 @@ class LikeAnimeThemeEntryField extends Field implements BindableField, Creatable
public function getCreationRules(array $args): array
{
return [
Str::of('prohibits:')->append(ToggleLikeResolver::ATTRIBUTE_PLAYLIST)->__toString(),
Str::of('prohibits:')->append(ToggleLikeMutation::ATTRIBUTE_PLAYLIST)->__toString(),
'required_without_all:'.implode(',', [
ToggleLikeResolver::ATTRIBUTE_PLAYLIST,
ToggleLikeMutation::ATTRIBUTE_PLAYLIST,
]),
];
}
@@ -6,8 +6,8 @@ namespace App\GraphQL\Schema\Fields\User\Like;
use App\Contracts\GraphQL\Fields\BindableField;
use App\Contracts\GraphQL\Fields\CreatableField;
use App\GraphQL\Resolvers\User\ToggleLikeResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\Models\User\ToggleLikeMutation;
use App\Models\List\Playlist;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
@@ -36,7 +36,7 @@ class LikePlaylistField extends Field implements BindableField, CreatableField
public function bindResolver(array $args): Playlist
{
return Playlist::query()
->where(Playlist::ATTRIBUTE_HASHID, Arr::get($args, ToggleLikeResolver::ATTRIBUTE_PLAYLIST))
->where(Playlist::ATTRIBUTE_HASHID, Arr::get($args, ToggleLikeMutation::ATTRIBUTE_PLAYLIST))
->firstOrFail();
}
@@ -46,9 +46,9 @@ class LikePlaylistField extends Field implements BindableField, CreatableField
public function getCreationRules(array $args): array
{
return [
Str::of('prohibits:')->append(ToggleLikeResolver::ATTRIBUTE_ENTRY)->__toString(),
Str::of('prohibits:')->append(ToggleLikeMutation::ATTRIBUTE_ENTRY)->__toString(),
'required_without_all:'.implode(',', [
ToggleLikeResolver::ATTRIBUTE_ENTRY,
ToggleLikeMutation::ATTRIBUTE_ENTRY,
]),
];
}
@@ -6,8 +6,8 @@ namespace App\GraphQL\Schema\Fields\User\WatchHistory;
use App\Contracts\GraphQL\Fields\CreatableField;
use App\Contracts\GraphQL\Fields\RequiredOnCreation;
use App\GraphQL\Resolvers\User\WatchResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\Models\User\WatchMutation;
use App\Models\User\WatchHistory;
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
use App\Pivots\Wiki\AnimeThemeEntryVideo;
@@ -42,7 +42,7 @@ class WatchHistoryEntryIdField extends Field implements CreatableField, Required
'integer',
Rule::exists(AnimeThemeEntry::class, AnimeThemeEntry::ATTRIBUTE_ID),
Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_ENTRY)
->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, Arr::get($args, WatchResolver::ATTRIBUTE_VIDEO)),
->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, Arr::get($args, WatchMutation::ATTRIBUTE_VIDEO)),
];
}
}
@@ -6,8 +6,8 @@ namespace App\GraphQL\Schema\Fields\User\WatchHistory;
use App\Contracts\GraphQL\Fields\CreatableField;
use App\Contracts\GraphQL\Fields\RequiredOnCreation;
use App\GraphQL\Resolvers\User\WatchResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\Models\User\WatchMutation;
use App\Models\User\WatchHistory;
use App\Models\Wiki\Video;
use App\Pivots\Wiki\AnimeThemeEntryVideo;
@@ -42,7 +42,7 @@ class WatchHistoryVideoIdField extends Field implements CreatableField, Required
'integer',
Rule::exists(Video::class, Video::ATTRIBUTE_ID),
Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_VIDEO)
->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, Arr::get($args, WatchResolver::ATTRIBUTE_ENTRY)),
->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, Arr::get($args, WatchMutation::ATTRIBUTE_ENTRY)),
];
}
}
@@ -4,16 +4,18 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeason;
use App\Actions\GraphQL\IndexAction;
use App\Contracts\GraphQL\Fields\DisplayableField;
use App\GraphQL\Argument\Argument;
use App\GraphQL\Resolvers\Wiki\Anime\AnimeYearsResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Queries\Models\Pagination\Wiki\AnimePaginationQuery;
use App\GraphQL\Schema\Types\Wiki\AnimeType;
use App\Models\Wiki\Anime;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Rebing\GraphQL\Support\Facades\GraphQL;
class AnimeYearSeasonAnimeField extends Field implements DisplayableField
@@ -77,9 +79,14 @@ class AnimeYearSeasonAnimeField extends Field implements DisplayableField
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
{
return App::call(
[App::make(AnimeYearsResolver::class), 'resolveAnimeField'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$season = Arr::get($root, AnimeYearSeasonSeasonField::FIELD);
$year = Arr::get($root, 'year');
$builder = Anime::query()
// season filter applies only on the 'season' field.
->when($season !== null, fn (Builder $query) => $query->where(Anime::ATTRIBUTE_SEASON, $season->value))
->where(Anime::ATTRIBUTE_YEAR, $year);
return new IndexAction()->index($builder, $args, new AnimeType(), $resolveInfo);
}
}
@@ -6,13 +6,13 @@ namespace App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear;
use App\Contracts\GraphQL\Fields\DisplayableField;
use App\Enums\Models\Wiki\AnimeSeason;
use App\GraphQL\Resolvers\Wiki\Anime\AnimeYearsResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeason\AnimeYearSeasonSeasonField;
use App\GraphQL\Schema\Queries\Wiki\AnimeYearsQuery;
use App\GraphQL\Schema\Types\Wiki\Anime\AnimeYear\AnimeYearSeasonType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Arr;
use Rebing\GraphQL\Support\Facades\GraphQL;
class AnimeYearSeasonField extends Field implements DisplayableField
@@ -53,13 +53,23 @@ class AnimeYearSeasonField extends Field implements DisplayableField
}
/**
* @return Collection
* @return array<string, mixed>|null
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
{
return App::call(
[App::make(AnimeYearsResolver::class), 'resolveSeasonField'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$season = Arr::get($args, self::ARGUMENT_SEASON);
$year = Arr::get($root, AnimeYearsQuery::ARGUMENT_YEAR);
$seasons = collect(Arr::get($root, 'seasons'));
if ($seasons->doesntContain(fn ($item): bool => $item[AnimeYearSeasonSeasonField::FIELD] === $season)) {
return null;
}
return [
AnimeYearSeasonSeasonField::FIELD => $season,
'seasonLocalized' => $season->localize(),
'year' => $year, // Needed to query animes on the 'season' field.
];
}
}
@@ -10,9 +10,9 @@ use App\GraphQL\Middleware\AuthMutation;
use App\GraphQL\Middleware\ResolveBindableArgs;
use App\GraphQL\Schema\Types\BaseType;
use App\GraphQL\Schema\Unions\BaseUnion;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Auth\Access\Response;
use Illuminate\Pipeline\Pipeline;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Mutation;
@@ -77,8 +77,11 @@ abstract class BaseMutation extends Mutation
return null;
}
/**
* @param array<string, mixed> $args
*/
abstract public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed;
protected function runHttpMiddlewares(array $middlewares): void
{
resolve(Pipeline::class)
->send(request())
->through($middlewares)
->thenReturn();
}
}
@@ -5,14 +5,18 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\ExternalProfile;
use App\Contracts\GraphQL\Fields\BindableField;
use App\Exceptions\GraphQL\ClientForbiddenException;
use App\Features\AllowExternalProfileManagement;
use App\GraphQL\Argument\Argument;
use App\GraphQL\Resolvers\List\SyncExternalProfileResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\BaseMutation;
use App\GraphQL\Schema\Types\List\ExternalProfileType;
use App\Http\Middleware\Api\EnabledOnlyOnLocalhost;
use App\Models\List\ExternalProfile;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Arr;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class SyncExternalProfileMutation extends BaseMutation
{
@@ -59,12 +63,28 @@ class SyncExternalProfileMutation extends BaseMutation
/**
* @param array<string, mixed> $args
* @return array<string, string>
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
{
return App::call(
[App::make(SyncExternalProfileResolver::class), 'update'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
$this->runHttpMiddlewares([
EnabledOnlyOnLocalhost::class,
EnsureFeaturesAreActive::using(AllowExternalProfileManagement::class),
]);
/** @var ExternalProfile $profile */
$profile = Arr::pull($args, 'model');
throw_unless(
$profile->canBeSynced(),
ClientForbiddenException::class,
'This external profile cannot be synced at the moment.'
);
$profile->dispatchSyncJob();
return [
'message' => 'Job dispatched.',
];
}
}
@@ -4,12 +4,16 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\Playlist;
use App\GraphQL\Resolvers\List\PlaylistResolver;
use App\Actions\Http\Api\StoreAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Schema\Mutations\Models\CreateMutation;
use App\GraphQL\Schema\Types\List\PlaylistType;
use App\Http\Middleware\Models\List\UserExceedsPlaylistLimit;
use App\Models\List\Playlist;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class CreatePlaylistMutation extends CreateMutation
{
@@ -28,12 +32,22 @@ class CreatePlaylistMutation extends CreateMutation
/**
* @param array<string, mixed> $args
* @param StoreAction<Playlist> $action
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, StoreAction $action): Playlist
{
return App::call(
[App::make(PlaylistResolver::class), 'store'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$this->runHttpMiddlewares([
EnsureFeaturesAreActive::using(AllowPlaylistManagement::class),
UserExceedsPlaylistLimit::class,
]);
$validated = Validator::make($args, $this->rulesForValidation($args))->validated();
$parameters = [
...$validated,
Playlist::ATTRIBUTE_USER => Auth::id(),
];
return $action->store(Playlist::query(), $parameters);
}
}
@@ -4,14 +4,16 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\Playlist;
use App\GraphQL\Resolvers\List\PlaylistResolver;
use App\Actions\Http\Api\DestroyAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Schema\Mutations\Models\DeleteMutation;
use App\GraphQL\Schema\Types\List\PlaylistType;
use App\GraphQL\Schema\Types\MessageResponseType;
use App\Models\List\Playlist;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Arr;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
use Rebing\GraphQL\Support\Facades\GraphQL;
class DeletePlaylistMutation extends DeleteMutation
@@ -36,12 +38,22 @@ class DeletePlaylistMutation extends DeleteMutation
/**
* @param array<string, mixed> $args
* @param DestroyAction<Playlist> $action
* @return array<string, string>
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, DestroyAction $action): array
{
return App::call(
[App::make(PlaylistResolver::class), 'destroy'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$this->runHttpMiddlewares([
EnsureFeaturesAreActive::using(AllowPlaylistManagement::class),
]);
/** @var Playlist $playlist */
$playlist = Arr::pull($args, 'model');
$message = $action->forceDelete($playlist);
return [
'message' => $message,
];
}
}
@@ -4,16 +4,25 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\Playlist\Track;
use App\GraphQL\Resolvers\List\Playlist\PlaylistTrackResolver;
use App\Actions\Http\Api\List\Playlist\Track\StoreTrackAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Schema\Mutations\Models\CreateMutation;
use App\GraphQL\Schema\Types\List\Playlist\PlaylistTrackType;
use App\Http\Middleware\Models\List\PlaylistExceedsTrackLimit;
use App\Models\List\Playlist;
use App\Models\List\Playlist\PlaylistTrack;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Validator;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class CreatePlaylistTrackMutation extends CreateMutation
{
public static Playlist $playlist;
final public const string ATTRIBUTE_ENTRY = 'entryId';
final public const string ATTRIBUTE_VIDEO = 'videoId';
public function __construct()
{
parent::__construct(PlaylistTrack::class);
@@ -30,11 +39,22 @@ class CreatePlaylistTrackMutation extends CreateMutation
/**
* @param array<string, mixed> $args
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, StoreTrackAction $action): PlaylistTrack
{
return App::call(
[App::make(PlaylistTrackResolver::class, ['playlist' => Arr::get($args, 'playlist')]), 'store'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
static::$playlist = Arr::get($args, 'playlist');
$this->runHttpMiddlewares([
EnsureFeaturesAreActive::using(AllowPlaylistManagement::class),
PlaylistExceedsTrackLimit::class,
]);
$validated = Validator::make($args, $this->rulesForValidation($args))->validated();
$validated = [
PlaylistTrack::ATTRIBUTE_ENTRY => Arr::integer($validated, 'entryId'),
PlaylistTrack::ATTRIBUTE_VIDEO => Arr::integer($validated, 'videoId'),
];
return $action->store(static::$playlist, PlaylistTrack::query(), $validated);
}
}
@@ -4,7 +4,8 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\Playlist\Track;
use App\GraphQL\Resolvers\List\Playlist\PlaylistTrackResolver;
use App\Actions\Http\Api\List\Playlist\Track\DestroyTrackAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Schema\Mutations\Models\DeleteMutation;
use App\GraphQL\Schema\Types\List\Playlist\PlaylistTrackType;
use App\GraphQL\Schema\Types\MessageResponseType;
@@ -12,7 +13,7 @@ use App\Models\List\Playlist\PlaylistTrack;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
use Rebing\GraphQL\Support\Facades\GraphQL;
class DeletePlaylistTrackMutation extends DeleteMutation
@@ -37,12 +38,21 @@ class DeletePlaylistTrackMutation extends DeleteMutation
/**
* @param array<string, mixed> $args
* @return array<string, string>
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, DestroyTrackAction $action): array
{
return App::call(
[App::make(PlaylistTrackResolver::class, ['playlist' => Arr::get($args, 'playlist')]), 'destroy'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$this->runHttpMiddlewares([
EnsureFeaturesAreActive::using(AllowPlaylistManagement::class),
]);
/** @var PlaylistTrack $track */
$track = Arr::pull($args, 'model');
$message = $action->destroy($track->playlist, $track);
return [
'message' => $message,
];
}
}
@@ -4,16 +4,21 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\Playlist\Track;
use App\GraphQL\Resolvers\List\Playlist\PlaylistTrackResolver;
use App\Actions\Http\Api\List\Playlist\Track\UpdateTrackAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Schema\Mutations\Models\UpdateMutation;
use App\GraphQL\Schema\Types\List\Playlist\PlaylistTrackType;
use App\Models\List\Playlist\PlaylistTrack;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Validator;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class UpdatePlaylistTrackMutation extends UpdateMutation
{
final public const string ATTRIBUTE_ENTRY = 'entryId';
final public const string ATTRIBUTE_VIDEO = 'videoId';
public function __construct()
{
parent::__construct(PlaylistTrack::class);
@@ -30,11 +35,22 @@ class UpdatePlaylistTrackMutation extends UpdateMutation
/**
* @param array<string, mixed> $args
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, UpdateTrackAction $action): PlaylistTrack
{
return App::call(
[App::make(PlaylistTrackResolver::class, ['playlist' => Arr::get($args, 'playlist')]), 'update'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$this->runHttpMiddlewares([
EnsureFeaturesAreActive::using(AllowPlaylistManagement::class),
]);
/** @var PlaylistTrack $track */
$track = Arr::get($args, 'model');
$validated = Validator::make($args, $this->rulesForValidation($args))->validated();
$validated = [
PlaylistTrack::ATTRIBUTE_ENTRY => Arr::integer($validated, 'entryId'),
PlaylistTrack::ATTRIBUTE_VIDEO => Arr::integer($validated, 'videoId'),
];
return $action->update($track->playlist, $track, $validated);
}
}
@@ -4,12 +4,15 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\List\Playlist;
use App\GraphQL\Resolvers\List\PlaylistResolver;
use App\Actions\Http\Api\UpdateAction;
use App\Features\AllowPlaylistManagement;
use App\GraphQL\Schema\Mutations\Models\UpdateMutation;
use App\GraphQL\Schema\Types\List\PlaylistType;
use App\Models\List\Playlist;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
class UpdatePlaylistMutation extends UpdateMutation
{
@@ -28,12 +31,19 @@ class UpdatePlaylistMutation extends UpdateMutation
/**
* @param array<string, mixed> $args
* @param UpdateAction<Playlist> $action
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, UpdateAction $action): Playlist
{
return App::call(
[App::make(PlaylistResolver::class), 'update'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$this->runHttpMiddlewares([
EnsureFeaturesAreActive::using(AllowPlaylistManagement::class),
]);
/** @var Playlist $playlist */
$playlist = Arr::pull($args, 'model');
$validated = Validator::make($args, $this->rulesForValidation($args))->validated();
return $action->update($playlist, $validated);
}
}
@@ -5,20 +5,26 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\User;
use App\Contracts\GraphQL\Fields\CreatableField;
use App\Contracts\Models\Likeable;
use App\GraphQL\Argument\Argument;
use App\GraphQL\Resolvers\User\ToggleLikeResolver;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Mutations\BaseMutation;
use App\GraphQL\Schema\Types\User\LikeType;
use App\Models\User\Like;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Rebing\GraphQL\Support\Facades\GraphQL;
class ToggleLikeMutation extends BaseMutation
{
final public const string ATTRIBUTE_ENTRY = 'entry';
final public const string ATTRIBUTE_PLAYLIST = 'playlist';
public function name(): string
{
return 'ToggleLike';
@@ -70,12 +76,15 @@ class ToggleLikeMutation extends BaseMutation
/**
* @param array<string, mixed> $args
* @return Like|null
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
{
return App::call(
[App::make(ToggleLikeResolver::class), 'store'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$validated = Validator::make($args, $this->rulesForValidation($args))->validated();
/** @var Model&Likeable $likeable */
$likeable = Arr::first($validated);
return $likeable->toggleLike(Auth::user());
}
}
@@ -4,17 +4,22 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Mutations\Models\User;
use App\GraphQL\Resolvers\User\WatchResolver;
use App\Actions\Http\Api\StoreAction;
use App\GraphQL\Schema\Mutations\Models\CreateMutation;
use App\GraphQL\Schema\Types\User\WatchHistoryType;
use App\Models\User\WatchHistory;
use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
class WatchMutation extends CreateMutation
{
final public const string ATTRIBUTE_ENTRY = 'entryId';
final public const string ATTRIBUTE_VIDEO = 'videoId';
public function __construct()
{
parent::__construct(WatchHistory::class);
@@ -45,12 +50,18 @@ class WatchMutation extends CreateMutation
/**
* @param array<string, mixed> $args
* @param StoreAction<WatchHistory> $action
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): mixed
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, StoreAction $action): WatchHistory
{
return App::call(
[App::make(WatchResolver::class), 'store'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$validated = Validator::make($args, $this->rulesForValidation($args))->validated();
$validated = [
WatchHistory::ATTRIBUTE_ENTRY => Arr::integer($validated, 'entryId'),
WatchHistory::ATTRIBUTE_VIDEO => Arr::integer($validated, 'videoId'),
WatchHistory::ATTRIBUTE_USER => Auth::id(),
];
return $action->store(WatchHistory::query(), $validated);
}
}
@@ -9,7 +9,6 @@ use App\GraphQL\Schema\Queries\BaseQuery;
use App\GraphQL\Schema\Types\Admin\FeaturedThemeType;
use App\Models\Admin\FeaturedTheme;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Date;
class CurrentFeaturedThemeQuery extends BaseQuery
@@ -43,12 +42,9 @@ class CurrentFeaturedThemeQuery extends BaseQuery
}
/**
* Resolve the query.
*
* @param array<string, mixed> $args
* @return FeaturedTheme|null
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): ?Model
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): ?FeaturedTheme
{
$builder = FeaturedTheme::query();
+1 -7
View File
@@ -8,7 +8,6 @@ use App\GraphQL\Argument\Argument;
use App\GraphQL\Schema\Queries\BaseQuery;
use App\GraphQL\Schema\Types\Auth\User\MeType;
use App\Models\Auth\User;
use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Support\Facades\Auth;
@@ -42,12 +41,7 @@ class MeQuery extends BaseQuery
return new MeType();
}
/**
* Resolve the query.
*
* @return User|null
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): ?User
{
$builder = User::query()->whereKey(Auth::id());
@@ -50,12 +50,7 @@ abstract class EloquentPaginationQuery extends EloquentQuery
];
}
/**
* Resolve the pagination query.
*
* @return Paginator
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, IndexAction $action)
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, IndexAction $action): Paginator
{
$builder = $this->model()::query();
@@ -54,10 +54,8 @@ abstract class EloquentSingularQuery extends EloquentQuery
/**
* Resolve the singular record with the binded argument.
*
* @return Model
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, ShowAction $action)
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, ShowAction $action): Model
{
/** @var Model $model */
$model = Arr::get($args, 'model');
@@ -5,18 +5,22 @@ declare(strict_types=1);
namespace App\GraphQL\Schema\Queries\Wiki;
use App\GraphQL\Argument\Argument;
use App\GraphQL\Resolvers\Wiki\Anime\AnimeYearsResolver;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeason\AnimeYearSeasonSeasonField;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeasonsField;
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearYearField;
use App\GraphQL\Schema\Queries\BaseQuery;
use App\GraphQL\Schema\Types\Wiki\Anime\AnimeYearType;
use App\Models\Wiki\Anime;
use App\Rules\GraphQL\Resolver\AnimeYearRule;
use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
class AnimeYearsQuery extends BaseQuery
{
@@ -69,14 +73,33 @@ class AnimeYearsQuery extends BaseQuery
return new AnimeYearType();
}
/**
* @return Collection
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): Collection
{
return App::call(
[App::make(AnimeYearsResolver::class), 'index'],
['root' => $root, 'args' => $args, 'context' => $context, 'resolveInfo' => $resolveInfo]
);
$year = Arr::get($args, self::ARGUMENT_YEAR);
$fieldSelection = $resolveInfo->getFieldSelection(1);
Validator::make(['year' => $year], ['year' => new AnimeYearRule($fieldSelection)])
->validate();
return Anime::query()
->distinct([Anime::ATTRIBUTE_YEAR, Anime::ATTRIBUTE_SEASON])
->orderBy(Anime::ATTRIBUTE_YEAR)
->when($year !== null, fn (Builder $query) => $query->whereIn(Anime::ATTRIBUTE_YEAR, $year))
->get([Anime::ATTRIBUTE_YEAR, Anime::ATTRIBUTE_SEASON])
->groupBy(Anime::ATTRIBUTE_YEAR)
->map(fn (Collection $items, int $year): array => [
AnimeYearYearField::FIELD => $year,
AnimeYearSeasonsField::FIELD => $items
->map(fn (Anime $anime): array => [
AnimeYearSeasonSeasonField::FIELD => $anime->season,
'seasonLocalized' => $anime->season->localize(),
'year' => $year, // Needed to query animes on the 'seasons' field.
])
->unique(Anime::ATTRIBUTE_SEASON)
->values()
->toArray(),
])->values();
}
}
@@ -72,10 +72,7 @@ class FindAnimeByExternalSiteQuery extends BaseQuery
return new AnimeType();
}
/**
* @return Collection
*/
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): Collection
{
Validator::make($args, [
self::ATTRIBUTE_SITE => ['required', new Enum(ResourceSite::class)],
@@ -6,7 +6,7 @@ namespace App\Http\Middleware\Models\List;
use App\Constants\Config\PlaylistConstants;
use App\Enums\Auth\SpecialPermission;
use App\GraphQL\Resolvers\List\Playlist\PlaylistTrackResolver;
use App\GraphQL\Schema\Mutations\Models\List\Playlist\Track\CreatePlaylistTrackMutation;
use App\Models\Auth\User;
use App\Models\List\Playlist;
use Closure;
@@ -23,7 +23,7 @@ class PlaylistExceedsTrackLimit
$trackLimit = intval(Config::get(PlaylistConstants::MAX_TRACKS_QUALIFIED));
/** @var Playlist|null $playlist */
$playlist = $request->route('playlist') ?? PlaylistTrackResolver::$playlist;
$playlist = $request->route('playlist') ?? CreatePlaylistTrackMutation::$playlist;
/** @var User|null $user */
$user = $request->user();