diff --git a/app/Actions/Http/Api/DestroyAction.php b/app/Actions/Http/Api/DestroyAction.php index e9209769f..ad5e21f7d 100644 --- a/app/Actions/Http/Api/DestroyAction.php +++ b/app/Actions/Http/Api/DestroyAction.php @@ -10,14 +10,16 @@ use Illuminate\Support\Str; /** * Class DestroyAction. + * + * @template TModel of \Illuminate\Database\Eloquent\Model */ class DestroyAction { /** * Destroy model. * - * @param Model $model - * @return Model + * @param TModel $model + * @return TModel */ public function destroy(Model $model): Model { @@ -32,7 +34,7 @@ class DestroyAction /** * Force delete model that doesn't apply soft deletes. * - * @param Model&Nameable $model + * @param TModel&Nameable $model * @return string */ public function forceDelete(Model&Nameable $model): string diff --git a/app/Actions/Http/Api/StoreAction.php b/app/Actions/Http/Api/StoreAction.php index 1bc1578c2..a5a44cfe5 100644 --- a/app/Actions/Http/Api/StoreAction.php +++ b/app/Actions/Http/Api/StoreAction.php @@ -9,15 +9,17 @@ use Illuminate\Database\Eloquent\Model; /** * Class StoreAction. + * + * @template TModel of \Illuminate\Database\Eloquent\Model */ class StoreAction { /** * Store model. * - * @param Builder $builder + * @param Builder $builder * @param array $parameters - * @return Model + * @return TModel */ public function store(Builder $builder, array $parameters): Model { @@ -29,8 +31,8 @@ class StoreAction /** * Perform model cleanup for presentation. * - * @param Model $model - * @return Model + * @param TModel $model + * @return TModel */ public function cleanup(Model $model): Model { diff --git a/app/Actions/Http/Api/UpdateAction.php b/app/Actions/Http/Api/UpdateAction.php index 2f35ea9cf..80c59c3dc 100644 --- a/app/Actions/Http/Api/UpdateAction.php +++ b/app/Actions/Http/Api/UpdateAction.php @@ -8,15 +8,17 @@ use Illuminate\Database\Eloquent\Model; /** * Class UpdateAction. + * + * @template TModel of \Illuminate\Database\Eloquent\Model */ class UpdateAction { /** * Update model. * - * @param Model $model + * @param TModel $model * @param array $parameters - * @return Model + * @return TModel */ public function update(Model $model, array $parameters): Model { @@ -28,8 +30,8 @@ class UpdateAction /** * Perform model cleanup for presentation. * - * @param Model $model - * @return Model + * @param TModel $model + * @return TModel */ public function cleanup(Model $model): Model { diff --git a/app/Actions/Http/Api/Wiki/Image/StoreImageAction.php b/app/Actions/Http/Api/Wiki/Image/StoreImageAction.php index 302dbb439..e58eb013e 100644 --- a/app/Actions/Http/Api/Wiki/Image/StoreImageAction.php +++ b/app/Actions/Http/Api/Wiki/Image/StoreImageAction.php @@ -8,7 +8,6 @@ use App\Actions\Http\Api\StoreAction; use App\Http\Api\Field\Wiki\Image\ImageFileField; use App\Models\Wiki\Image; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Model; use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Http\UploadedFile; use Illuminate\Support\Arr; @@ -17,17 +16,19 @@ use Illuminate\Support\Facades\Storage; /** * Class StoreImageAction. + * + * @extends StoreAction */ class StoreImageAction extends StoreAction { /** * Store image. * - * @param Builder $builder + * @param Builder $builder * @param array $parameters - * @return Model + * @return Image */ - public function store(Builder $builder, array $parameters): Model + public function store(Builder $builder, array $parameters): Image { $imageParameters = Arr::except($parameters, ImageFileField::ATTRIBUTE_FILE); diff --git a/app/Actions/Models/List/External/StoreExternalProfileClaimedAction.php b/app/Actions/Models/List/External/StoreExternalProfileClaimedAction.php index 94cbc53a5..3ff40e063 100644 --- a/app/Actions/Models/List/External/StoreExternalProfileClaimedAction.php +++ b/app/Actions/Models/List/External/StoreExternalProfileClaimedAction.php @@ -90,6 +90,7 @@ class StoreExternalProfileClaimedAction return $unclaimedProfile; } + /** @var StoreAction */ $storeAction = new StoreAction(); /** @var ExternalProfile $profile */ diff --git a/app/Concerns/GraphQL/ResolvesArguments.php b/app/Concerns/GraphQL/ResolvesArguments.php index 48ca24293..4eeff3763 100644 --- a/app/Concerns/GraphQL/ResolvesArguments.php +++ b/app/Concerns/GraphQL/ResolvesArguments.php @@ -4,6 +4,11 @@ declare(strict_types=1); namespace App\Concerns\GraphQL; +use App\Contracts\GraphQL\Fields\BindableField; +use App\Contracts\GraphQL\Fields\CreatableField; +use App\Contracts\GraphQL\Fields\RequiredOnCreation; +use App\Contracts\GraphQL\Fields\RequiredOnUpdate; +use App\Contracts\GraphQL\Fields\UpdatableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\FilterDirective; use App\GraphQL\Definition\Fields\Field; @@ -15,6 +20,8 @@ use Illuminate\Support\Str; */ trait ResolvesArguments { + use ResolvesDirectives; + /** * Build the arguments array into string. * @@ -52,4 +59,77 @@ trait ResolvesArguments ->flatten() ->toArray(); } + + /** + * Resolve the fields into arguments that are used for mutations of type create. + * + * @param Field[] $fields + * @return string[] + */ + public function resolveCreateMutationArguments(array $fields): array + { + return collect($fields) + ->filter(fn (Field $field) => $field instanceof CreatableField) + ->map(function (Field&CreatableField $field) { + return Str::of($field->getColumn()) + ->append(': ') + ->append($field->type()->__toString()) + ->append($field instanceof RequiredOnCreation ? '!' : '') + ->__toString(); + }) + ->flatten() + ->toArray(); + } + + /** + * Resolve the fields into arguments that are used for mutations of type update. + * + * @param Field[] $fields + * @return string[] + */ + public function resolveUpdateMutationArguments(array $fields): array + { + return collect($fields) + ->filter(fn (Field $field) => $field instanceof UpdatableField) + ->map(function (Field&UpdatableField $field) { + return Str::of($field->getColumn()) + ->append(': ') + ->append($field->type()->__toString()) + ->append($field instanceof RequiredOnUpdate ? '!' : '') + ->__toString(); + }) + ->flatten() + ->toArray(); + } + + /** + * Resolve the bind argument. + * + * @param array $fields + * @return string[] + */ + public function resolveBindArgument(array $fields): array + { + return collect($fields) + ->filter(fn (Field $field) => $field instanceof BindableField) + ->map(function (Field $field) { + return Str::of($field->getName()) + ->append(': ') + ->append($field->type()->__toString()) + ->append('! ') + ->append($this->getBindDirective($field)) + ->__toString(); + }) + ->toArray(); + } + + private function getBindDirective(Field&BindableField $field): string + { + return $this->resolveDirectives([ + 'bind' => [ + 'class' => $field->bindTo(), + 'column' => $field->bindUsingColumn(), + ], + ]); + } } diff --git a/app/Concerns/GraphQL/ResolvesAttributes.php b/app/Concerns/GraphQL/ResolvesAttributes.php index a65a40d91..5146a03b1 100644 --- a/app/Concerns/GraphQL/ResolvesAttributes.php +++ b/app/Concerns/GraphQL/ResolvesAttributes.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Concerns\GraphQL; use App\GraphQL\Attributes\UseBuilder; +use App\GraphQL\Attributes\UseField; use Exception; use ReflectionClass; @@ -36,4 +37,28 @@ trait ResolvesAttributes return null; } + + /** + * Resolve the field directive as an attribute. + * + * @return string|null + */ + protected function resolveFieldAttribute(): ?string + { + $reflection = new ReflectionClass($this); + + $attributes = $reflection->getAttributes(UseField::class); + + if (count($attributes) === 1) { + $instance = $attributes[0]->newInstance(); + + return $instance->fieldClass.'@'.$instance->method; + } + + if (count($attributes) > 1) { + throw new Exception('Multiple fields disallowed.'); + } + + return null; + } } diff --git a/app/Contracts/GraphQL/Fields/BindableField.php b/app/Contracts/GraphQL/Fields/BindableField.php new file mode 100644 index 000000000..30bc9cc37 --- /dev/null +++ b/app/Contracts/GraphQL/Fields/BindableField.php @@ -0,0 +1,27 @@ + + */ + public function bindTo(): string; + + /** + * Get the column that the field should use to bind. + * + * @return string + */ + public function bindUsingColumn(): string; +} diff --git a/app/Contracts/GraphQL/Fields/CreatableField.php b/app/Contracts/GraphQL/Fields/CreatableField.php new file mode 100644 index 000000000..af5ff47a0 --- /dev/null +++ b/app/Contracts/GraphQL/Fields/CreatableField.php @@ -0,0 +1,19 @@ + $args + * @return array + */ + public function getCreationRules(array $args): array; +} diff --git a/app/Contracts/GraphQL/Fields/DisplayableField.php b/app/Contracts/GraphQL/Fields/DisplayableField.php new file mode 100644 index 000000000..00dd245b0 --- /dev/null +++ b/app/Contracts/GraphQL/Fields/DisplayableField.php @@ -0,0 +1,18 @@ + $args + * @return array + */ + public function getUpdateRules(array $args): array; +} diff --git a/app/GraphQL/Attributes/Hidden.php b/app/GraphQL/Attributes/Hidden.php index 6d74aab69..afc7f016a 100644 --- a/app/GraphQL/Attributes/Hidden.php +++ b/app/GraphQL/Attributes/Hidden.php @@ -6,9 +6,15 @@ namespace App\GraphQL\Attributes; use Attribute; +/** + * Class Hidden. + */ #[Attribute(Attribute::TARGET_ALL)] class Hidden { + /** + * @param bool $hidden + */ public function __construct( public bool $hidden = true, ) {} diff --git a/app/GraphQL/Attributes/UseField.php b/app/GraphQL/Attributes/UseField.php new file mode 100644 index 000000000..d0580eaf1 --- /dev/null +++ b/app/GraphQL/Attributes/UseField.php @@ -0,0 +1,23 @@ + $storeAction + * @param UpdateAction $updateAction + * @param DestroyAction $destroyAction + */ + public function __construct( + protected StoreAction $storeAction, + protected UpdateAction $updateAction, + protected DestroyAction $destroyAction, + ) {} + + /** + * Get the attributes and values that were validated. + * + * @param array $args + * @param class-string $mutation + * @return array + * + * @throws ValidationException + */ + public function validated(array $args, string $mutation): array + { + $mutationInstance = App::make($mutation); + + return Validator::make($args, $mutationInstance->rules($args))->validated(); + } +} diff --git a/app/GraphQL/Mutations/List/ExternalProfileMutator.php b/app/GraphQL/Controllers/List/ExternalProfileController.php similarity index 82% rename from app/GraphQL/Mutations/List/ExternalProfileMutator.php rename to app/GraphQL/Controllers/List/ExternalProfileController.php index 091c128e7..8e160ba3f 100644 --- a/app/GraphQL/Mutations/List/ExternalProfileMutator.php +++ b/app/GraphQL/Controllers/List/ExternalProfileController.php @@ -2,16 +2,17 @@ declare(strict_types=1); -namespace App\GraphQL\Mutations\List; +namespace App\GraphQL\Controllers\List; +use App\GraphQL\Controllers\BaseController; use App\Models\List\ExternalProfile; use Illuminate\Http\JsonResponse; use Illuminate\Support\Arr; /** - * Class ExternalProfileMutator. + * Class ExternalProfileController. */ -class ExternalProfileMutator +class ExternalProfileController extends BaseController { final public const ROUTE_SLUG = 'id'; diff --git a/app/GraphQL/Mutations/List/Playlist/PlaylistTrackMutator.php b/app/GraphQL/Controllers/List/Playlist/PlaylistTrackController.php similarity index 89% rename from app/GraphQL/Mutations/List/Playlist/PlaylistTrackMutator.php rename to app/GraphQL/Controllers/List/Playlist/PlaylistTrackController.php index 42d2092fc..16bb7b4a6 100644 --- a/app/GraphQL/Mutations/List/Playlist/PlaylistTrackMutator.php +++ b/app/GraphQL/Controllers/List/Playlist/PlaylistTrackController.php @@ -2,19 +2,22 @@ declare(strict_types=1); -namespace App\GraphQL\Mutations\List\Playlist; +namespace App\GraphQL\Controllers\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\GraphQL\Controllers\BaseController; use App\Models\List\Playlist; use App\Models\List\Playlist\PlaylistTrack; use Illuminate\Support\Arr; /** - * Class PlaylistTrackMutator. + * Class PlaylistTrackController. + * + * @extends BaseController */ -class PlaylistTrackMutator +class PlaylistTrackController extends BaseController { final public const ROUTE_SLUG = 'id'; diff --git a/app/GraphQL/Mutations/List/PlaylistMutator.php b/app/GraphQL/Controllers/List/PlaylistController.php similarity index 55% rename from app/GraphQL/Mutations/List/PlaylistMutator.php rename to app/GraphQL/Controllers/List/PlaylistController.php index 92304ef88..e4ee08f4e 100644 --- a/app/GraphQL/Mutations/List/PlaylistMutator.php +++ b/app/GraphQL/Controllers/List/PlaylistController.php @@ -2,19 +2,21 @@ declare(strict_types=1); -namespace App\GraphQL\Mutations\List; +namespace App\GraphQL\Controllers\List; -use App\Actions\Http\Api\DestroyAction; -use App\Actions\Http\Api\StoreAction; -use App\Actions\Http\Api\UpdateAction; +use App\GraphQL\Controllers\BaseController; +use App\GraphQL\Definition\Mutations\Rest\List\Playlist\CreatePlaylistMutation; +use App\GraphQL\Definition\Mutations\Rest\List\Playlist\UpdatePlaylistMutation; use App\Models\List\Playlist; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; /** - * Class PlaylistMutator. + * Class PlaylistController. + * + * @extends BaseController */ -class PlaylistMutator +class PlaylistController extends BaseController { final public const ROUTE_SLUG = 'id'; @@ -27,15 +29,14 @@ class PlaylistMutator */ public function store($_, array $args): Playlist { + $validated = $this->validated($args, CreatePlaylistMutation::class); + $parameters = [ - ...$args, + ...$validated, Playlist::ATTRIBUTE_USER => Auth::id(), ]; - $action = new StoreAction(); - - /** @var Playlist $stored */ - $stored = $action->store(Playlist::query(), $parameters); + $stored = $this->storeAction->store(Playlist::query(), $parameters); return $stored; } @@ -52,10 +53,9 @@ class PlaylistMutator /** @var Playlist $playlist */ $playlist = Arr::pull($args, self::ROUTE_SLUG); - $action = new UpdateAction(); + $validated = $this->validated($args, UpdatePlaylistMutation::class); - /** @var Playlist $updated */ - $updated = $action->update($playlist, $args); + $updated = $this->updateAction->update($playlist, $validated); return $updated; } @@ -65,18 +65,15 @@ class PlaylistMutator * * @param null $_ * @param array $args - * @return Playlist + * @return string */ - public function destroy($_, array $args): Playlist + public function destroy($_, array $args): string { /** @var Playlist $playlist */ $playlist = Arr::get($args, self::ROUTE_SLUG); - $action = new DestroyAction(); + $message = $this->destroyAction->forceDelete($playlist); - /** @var Playlist $destroyed */ - $destroyed = $action->destroy($playlist); - - return $destroyed; + return $message; } } diff --git a/app/GraphQL/Mutations/User/LikeMutator.php b/app/GraphQL/Controllers/User/LikeController.php similarity index 94% rename from app/GraphQL/Mutations/User/LikeMutator.php rename to app/GraphQL/Controllers/User/LikeController.php index 1a082c10f..64e6a4768 100644 --- a/app/GraphQL/Mutations/User/LikeMutator.php +++ b/app/GraphQL/Controllers/User/LikeController.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace App\GraphQL\Mutations\User; +namespace App\GraphQL\Controllers\User; use App\Models\List\Playlist; use App\Models\Wiki\Video; @@ -11,9 +11,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; /** - * Class LikeMutator. + * Class LikeController. */ -class LikeMutator +class LikeController { final public const ATTRIBUTE_PLAYLIST = 'playlist'; final public const ATTRIBUTE_VIDEO = 'video'; diff --git a/app/GraphQL/Definition/Fields/Base/CountAggregateField.php b/app/GraphQL/Definition/Fields/Base/CountAggregateField.php index b462ee1ac..15fd6ed2c 100644 --- a/app/GraphQL/Definition/Fields/Base/CountAggregateField.php +++ b/app/GraphQL/Definition/Fields/Base/CountAggregateField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Base; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Resolvers\CountAggregateResolver; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class CountAggregateField. */ -class CountAggregateField extends Field +class CountAggregateField extends Field implements DisplayableField { /** * Create a new Field instance. @@ -52,8 +53,18 @@ class CountAggregateField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::int(); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Base/CountField.php b/app/GraphQL/Definition/Fields/Base/CountField.php index 050af9044..58bea07b1 100644 --- a/app/GraphQL/Definition/Fields/Base/CountField.php +++ b/app/GraphQL/Definition/Fields/Base/CountField.php @@ -4,13 +4,14 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Base; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use GraphQL\Type\Definition\Type; /** * Class CountField. */ -class CountField extends Field +class CountField extends Field implements DisplayableField { /** * Create a new Field instance. @@ -48,8 +49,18 @@ class CountField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::int(); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Base/ExistsField.php b/app/GraphQL/Definition/Fields/Base/ExistsField.php index c45b3f2c5..38b08c02e 100644 --- a/app/GraphQL/Definition/Fields/Base/ExistsField.php +++ b/app/GraphQL/Definition/Fields/Base/ExistsField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Base; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Resolvers\ExistsResolver; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class ExistsField. */ -class ExistsField extends Field +class ExistsField extends Field implements DisplayableField { /** * Create a new Field instance. @@ -52,8 +53,18 @@ class ExistsField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::boolean(); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/BooleanField.php b/app/GraphQL/Definition/Fields/BooleanField.php index 6778bfac0..de6b9a025 100644 --- a/app/GraphQL/Definition/Fields/BooleanField.php +++ b/app/GraphQL/Definition/Fields/BooleanField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\EqFilterDirective; use App\GraphQL\Definition\Directives\Filters\FilterDirective; @@ -12,18 +13,28 @@ use GraphQL\Type\Definition\Type; /** * Class BooleanField. */ -abstract class BooleanField extends Field implements FilterableField +abstract class BooleanField extends Field implements DisplayableField, FilterableField { /** * The type returned by the field. * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::boolean(); } + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } + /** * The directives available for this filter. * diff --git a/app/GraphQL/Definition/Fields/DateTimeTzField.php b/app/GraphQL/Definition/Fields/DateTimeTzField.php index 6e0ff08bb..7c3aef9b0 100644 --- a/app/GraphQL/Definition/Fields/DateTimeTzField.php +++ b/app/GraphQL/Definition/Fields/DateTimeTzField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\FilterDirective; use App\GraphQL\Definition\Directives\Filters\GreaterFilterDirective; @@ -14,18 +15,28 @@ use Nuwave\Lighthouse\Schema\TypeRegistry; /** * Class DateTimeTzField. */ -abstract class DateTimeTzField extends Field implements FilterableField +abstract class DateTimeTzField extends Field implements DisplayableField, FilterableField { /** * The type returned by the field. * * @return Type */ - protected function type(): Type + public function type(): Type { return app(TypeRegistry::class)->get('DateTimeTz'); } + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } + /** * The directives available for this filter. * diff --git a/app/GraphQL/Definition/Fields/EnumField.php b/app/GraphQL/Definition/Fields/EnumField.php index 7156b38b9..8be31c37e 100644 --- a/app/GraphQL/Definition/Fields/EnumField.php +++ b/app/GraphQL/Definition/Fields/EnumField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\FilterDirective; use App\GraphQL\Definition\Directives\Filters\InFilterDirective; @@ -15,7 +16,7 @@ use Nuwave\Lighthouse\Schema\TypeRegistry; /** * Class EnumField. */ -abstract class EnumField extends Field implements FilterableField +abstract class EnumField extends Field implements DisplayableField, FilterableField { /** * Create a new field instance. @@ -34,12 +35,22 @@ abstract class EnumField extends Field implements FilterableField parent::__construct($column, $name, $nullable); } + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } + /** * The type returned by the field. * * @return Type */ - protected function type(): Type + public function type(): Type { return app(TypeRegistry::class)->get(class_basename($this->enum)); } diff --git a/app/GraphQL/Definition/Fields/Field.php b/app/GraphQL/Definition/Fields/Field.php index 63de35f92..b6502ca89 100644 --- a/app/GraphQL/Definition/Fields/Field.php +++ b/app/GraphQL/Definition/Fields/Field.php @@ -82,7 +82,7 @@ abstract class Field implements Stringable * * @return Type */ - abstract protected function type(): Type; + abstract public function type(): Type; /** * Resolve the field. diff --git a/app/GraphQL/Definition/Fields/FloatField.php b/app/GraphQL/Definition/Fields/FloatField.php index 70b9c008f..9698d9dbc 100644 --- a/app/GraphQL/Definition/Fields/FloatField.php +++ b/app/GraphQL/Definition/Fields/FloatField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\FilterDirective; use App\GraphQL\Definition\Directives\Filters\GreaterFilterDirective; @@ -15,18 +16,28 @@ use GraphQL\Type\Definition\Type; /** * Class FloatField. */ -abstract class FloatField extends Field implements FilterableField +abstract class FloatField extends Field implements DisplayableField, FilterableField { /** * The type returned by the field. * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::float(); } + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } + /** * The directives available for this filter. * diff --git a/app/GraphQL/Definition/Fields/IntField.php b/app/GraphQL/Definition/Fields/IntField.php index 81f0d776d..88f1fd1b0 100644 --- a/app/GraphQL/Definition/Fields/IntField.php +++ b/app/GraphQL/Definition/Fields/IntField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\FilterDirective; use App\GraphQL\Definition\Directives\Filters\GreaterFilterDirective; @@ -15,18 +16,28 @@ use GraphQL\Type\Definition\Type; /** * Class IntField. */ -abstract class IntField extends Field implements FilterableField +abstract class IntField extends Field implements DisplayableField, FilterableField { /** * The type returned by the field. * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::int(); } + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } + /** * The directives available for this filter. * diff --git a/app/GraphQL/Definition/Fields/JsonField.php b/app/GraphQL/Definition/Fields/JsonField.php index b5d50e506..d7e7f59f4 100644 --- a/app/GraphQL/Definition/Fields/JsonField.php +++ b/app/GraphQL/Definition/Fields/JsonField.php @@ -4,7 +4,20 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; + /** * Class JsonField. */ -abstract class JsonField extends Field {} +abstract class JsonField extends Field implements DisplayableField +{ + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } +} diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistDescriptionField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistDescriptionField.php index c86343303..2d524dce9 100644 --- a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistDescriptionField.php +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistDescriptionField.php @@ -4,13 +4,16 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\List\Playlist; +use App\Contracts\GraphQL\Fields\CreatableField; +use App\Contracts\GraphQL\Fields\UpdatableField; use App\GraphQL\Definition\Fields\StringField; use App\Models\List\Playlist; +use App\Rules\ModerationRule; /** * Class PlaylistDescriptionField. */ -class PlaylistDescriptionField extends StringField +class PlaylistDescriptionField extends StringField implements CreatableField, UpdatableField { /** * Create a new field instance. @@ -29,4 +32,36 @@ class PlaylistDescriptionField extends StringField { return 'The description of the playlist'; } + + /** + * Set the creation validation rules for the field. + * + * @param array $args + * @return array + */ + public function getCreationRules(array $args): array + { + return [ + 'nullable', + 'string', + 'max:1000', + new ModerationRule(), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + return [ + 'nullable', + 'string', + 'max:1000', + new ModerationRule(), + ]; + } } diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistIdField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistIdField.php index 68f935f31..ddc5eb6fa 100644 --- a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistIdField.php +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistIdField.php @@ -4,13 +4,14 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\List\Playlist; +use App\Contracts\GraphQL\Fields\BindableField; use App\GraphQL\Definition\Fields\StringField; use App\Models\List\Playlist; /** * Class PlaylistIdField. */ -class PlaylistIdField extends StringField +class PlaylistIdField extends StringField implements BindableField { /** * Create a new field instance. @@ -29,4 +30,24 @@ class PlaylistIdField extends StringField { return 'The primary key of the resource'; } + + /** + * Get the model that the field should bind to. + * + * @return class-string + */ + public function bindTo(): string + { + return Playlist::class; + } + + /** + * Get the column that the field should use to bind. + * + * @return string + */ + public function bindUsingColumn(): string + { + return Playlist::ATTRIBUTE_HASHID; + } } diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistNameField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistNameField.php index 552241586..fac6148e4 100644 --- a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistNameField.php +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistNameField.php @@ -4,13 +4,17 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\List\Playlist; +use App\Contracts\GraphQL\Fields\CreatableField; +use App\Contracts\GraphQL\Fields\RequiredOnCreation; +use App\Contracts\GraphQL\Fields\UpdatableField; use App\GraphQL\Definition\Fields\StringField; use App\Models\List\Playlist; +use App\Rules\ModerationRule; /** * Class PlaylistNameField. */ -class PlaylistNameField extends StringField +class PlaylistNameField extends StringField implements CreatableField, RequiredOnCreation, UpdatableField { /** * Create a new field instance. @@ -29,4 +33,37 @@ class PlaylistNameField extends StringField { return 'The title of the playlist'; } + + /** + * Set the creation validation rules for the field. + * + * @param array $args + * @return array + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + 'string', + 'max:192', + new ModerationRule(), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + return [ + 'sometimes', + 'required', + 'string', + 'max:192', + new ModerationRule(), + ]; + } } diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackEntryIdField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackEntryIdField.php new file mode 100644 index 000000000..fe323e380 --- /dev/null +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackEntryIdField.php @@ -0,0 +1,92 @@ + $args + * @return array + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + 'integer', + Rule::exists(AnimeThemeEntry::class, AnimeThemeEntry::ATTRIBUTE_ID), + Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_ENTRY) + ->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, Arr::get($args, PlaylistTrack::ATTRIBUTE_VIDEO)), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + $videoId = Arr::get($args, PlaylistTrack::ATTRIBUTE_VIDEO); + + return [ + 'sometimes', + 'required', + 'integer', + Rule::exists(AnimeThemeEntry::class, AnimeThemeEntry::ATTRIBUTE_ID), + Rule::when( + ! empty($videoId), + [ + Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_ENTRY) + ->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, $videoId), + ] + ), + ]; + } +} diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackIdField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackIdField.php index f5084e38e..171a7016d 100644 --- a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackIdField.php +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackIdField.php @@ -4,13 +4,14 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack; +use App\Contracts\GraphQL\Fields\BindableField; use App\GraphQL\Definition\Fields\StringField; use App\Models\List\Playlist\PlaylistTrack; /** * Class PlaylistTrackIdField. */ -class PlaylistTrackIdField extends StringField +class PlaylistTrackIdField extends StringField implements BindableField { /** * Create a new field instance. @@ -29,4 +30,24 @@ class PlaylistTrackIdField extends StringField { return 'The primary key of the resource'; } + + /** + * Get the model that the field should bind to. + * + * @return class-string + */ + public function bindTo(): string + { + return PlaylistTrack::class; + } + + /** + * Get the column that the field should use to bind. + * + * @return string + */ + public function bindUsingColumn(): string + { + return PlaylistTrack::ATTRIBUTE_HASHID; + } } diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackNextField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackNextField.php new file mode 100644 index 000000000..8f2c0cc1b --- /dev/null +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackNextField.php @@ -0,0 +1,96 @@ + $args + * @return array + */ + public function getCreationRules(array $args): array + { + $playlistHashid = Arr::get($args, 'playlist'); + $playlist = Playlist::query()->firstWhere(Playlist::ATTRIBUTE_HASHID, $playlistHashid); + + return [ + 'sometimes', + 'required', + 'string', + Str::of('prohibits:')->append(PlaylistTrack::RELATION_PREVIOUS)->__toString(), + Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) + ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + $playlistHashid = Arr::get($args, 'playlist'); + $hashid = Arr::get($args, PlaylistTrack::ATTRIBUTE_HASHID); + + $playlist = Playlist::query()->firstWhere(Playlist::ATTRIBUTE_HASHID, $playlistHashid); + $track = PlaylistTrack::query()->firstWhere(PlaylistTrack::ATTRIBUTE_HASHID, $hashid); + + return [ + 'sometimes', + 'required', + 'string', + Str::of('prohibits:')->append(PlaylistTrack::RELATION_PREVIOUS)->__toString(), + Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) + ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()) + ->whereNot(PlaylistTrack::ATTRIBUTE_ID, $track?->getKey()), + ]; + } +} diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackPlaylistField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackPlaylistField.php new file mode 100644 index 000000000..c9b4aceeb --- /dev/null +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackPlaylistField.php @@ -0,0 +1,95 @@ + + */ + public function bindTo(): string + { + return Playlist::class; + } + + /** + * Get the column that the field should use to bind. + * + * @return string + */ + public function bindUsingColumn(): string + { + return Playlist::ATTRIBUTE_HASHID; + } + + /** + * Set the creation validation rules for the field. + * + * @param array $args + * @return array + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + return [ + 'required', + ]; + } +} diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackPreviousField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackPreviousField.php new file mode 100644 index 000000000..2b4063df0 --- /dev/null +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackPreviousField.php @@ -0,0 +1,96 @@ + $args + * @return array + */ + public function getCreationRules(array $args): array + { + $playlistHashid = Arr::get($args, 'playlist'); + $playlist = Playlist::query()->firstWhere(Playlist::ATTRIBUTE_HASHID, $playlistHashid); + + return [ + 'sometimes', + 'required', + 'string', + Str::of('prohibits:')->append(PlaylistTrack::RELATION_NEXT)->__toString(), + Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) + ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + $playlistHashid = Arr::get($args, 'playlist'); + $hashid = Arr::get($args, PlaylistTrack::ATTRIBUTE_HASHID); + + $playlist = Playlist::query()->firstWhere(Playlist::ATTRIBUTE_HASHID, $playlistHashid); + $track = PlaylistTrack::query()->firstWhere(PlaylistTrack::ATTRIBUTE_HASHID, $hashid); + + return [ + 'sometimes', + 'required', + 'string', + Str::of('prohibits:')->append(PlaylistTrack::RELATION_NEXT)->__toString(), + Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) + ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()) + ->whereNot(PlaylistTrack::ATTRIBUTE_ID, $track?->getKey()), + ]; + } +} diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackVideoIdField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackVideoIdField.php new file mode 100644 index 000000000..716da6184 --- /dev/null +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistTrack/PlaylistTrackVideoIdField.php @@ -0,0 +1,92 @@ + $args + * @return array + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + 'integer', + Rule::exists(Video::class, Video::ATTRIBUTE_ID), + Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_VIDEO) + ->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, Arr::get($args, PlaylistTrack::ATTRIBUTE_ENTRY)), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + $entryId = Arr::get($args, PlaylistTrack::ATTRIBUTE_ENTRY); + + return [ + 'sometimes', + 'required', + 'integer', + Rule::exists(Video::class, Video::ATTRIBUTE_ID), + Rule::when( + ! empty($entryId), + [ + Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_VIDEO) + ->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, $entryId), + ] + ), + ]; + } +} diff --git a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistVisibilityField.php b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistVisibilityField.php index 3a5ff939e..32806019f 100644 --- a/app/GraphQL/Definition/Fields/List/Playlist/PlaylistVisibilityField.php +++ b/app/GraphQL/Definition/Fields/List/Playlist/PlaylistVisibilityField.php @@ -4,14 +4,18 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\List\Playlist; +use App\Contracts\GraphQL\Fields\CreatableField; +use App\Contracts\GraphQL\Fields\RequiredOnCreation; +use App\Contracts\GraphQL\Fields\UpdatableField; use App\Enums\Models\List\PlaylistVisibility; use App\GraphQL\Definition\Fields\EnumField; use App\Models\List\Playlist; +use Illuminate\Validation\Rules\Enum; /** * Class PlaylistVisibilityField. */ -class PlaylistVisibilityField extends EnumField +class PlaylistVisibilityField extends EnumField implements CreatableField, RequiredOnCreation, UpdatableField { /** * Create a new field instance. @@ -30,4 +34,33 @@ class PlaylistVisibilityField extends EnumField { return 'The state of who can see the playlist'; } + + /** + * Set the creation validation rules for the field. + * + * @param array $args + * @return array + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + new Enum(PlaylistVisibility::class), + ]; + } + + /** + * Set the update validation rules for the field. + * + * @param array $args + * @return array + */ + public function getUpdateRules(array $args): array + { + return [ + 'sometimes', + 'required', + new Enum(PlaylistVisibility::class), + ]; + } } diff --git a/app/GraphQL/Definition/Fields/LocalizedEnumField.php b/app/GraphQL/Definition/Fields/LocalizedEnumField.php index 2c996977d..4047d2a49 100644 --- a/app/GraphQL/Definition/Fields/LocalizedEnumField.php +++ b/app/GraphQL/Definition/Fields/LocalizedEnumField.php @@ -4,13 +4,14 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use GraphQL\Type\Definition\Type; use Illuminate\Support\Arr; /** * Class LocalizedEnumField. */ -class LocalizedEnumField extends Field +class LocalizedEnumField extends Field implements DisplayableField { /** * Create a new field instance. @@ -38,7 +39,7 @@ class LocalizedEnumField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::string(); } @@ -67,4 +68,14 @@ class LocalizedEnumField extends Field { return Arr::get($root, $this->column)?->localize(); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchAnimeField.php b/app/GraphQL/Definition/Fields/Search/SearchAnimeField.php index 1263c5ea6..73f250604 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchAnimeField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchAnimeField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\AnimeType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchAnimeField. */ -class SearchAnimeField extends Field +class SearchAnimeField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchAnimeField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new AnimeType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchAnimeThemesField.php b/app/GraphQL/Definition/Fields/Search/SearchAnimeThemesField.php index 9fdf54303..789762dff 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchAnimeThemesField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchAnimeThemesField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\Anime\AnimeThemeType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchAnimeThemesField. */ -class SearchAnimeThemesField extends Field +class SearchAnimeThemesField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchAnimeThemesField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new AnimeThemeType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchArtistsField.php b/app/GraphQL/Definition/Fields/Search/SearchArtistsField.php index 886fa9af4..7e9a4d108 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchArtistsField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchArtistsField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\ArtistType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchArtistsField. */ -class SearchArtistsField extends Field +class SearchArtistsField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchArtistsField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new ArtistType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchPlaylistsField.php b/app/GraphQL/Definition/Fields/Search/SearchPlaylistsField.php index 0bdc1f4d0..6b022318f 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchPlaylistsField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchPlaylistsField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\List\PlaylistType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchPlaylistsField. */ -class SearchPlaylistsField extends Field +class SearchPlaylistsField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchPlaylistsField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new PlaylistType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchSeriesField.php b/app/GraphQL/Definition/Fields/Search/SearchSeriesField.php index b57556bf4..b645405d6 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchSeriesField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchSeriesField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\SeriesType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchSeriesField. */ -class SearchSeriesField extends Field +class SearchSeriesField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchSeriesField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new SeriesType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchSongsField.php b/app/GraphQL/Definition/Fields/Search/SearchSongsField.php index cabd0d2b5..58903d758 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchSongsField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchSongsField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\SongType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchSongsField. */ -class SearchSongsField extends Field +class SearchSongsField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchSongsField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new SongType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchStudiosField.php b/app/GraphQL/Definition/Fields/Search/SearchStudiosField.php index 6ddc08398..33721f5bb 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchStudiosField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchStudiosField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\StudioType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchStudiosField. */ -class SearchStudiosField extends Field +class SearchStudiosField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchStudiosField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new StudioType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Search/SearchVideosField.php b/app/GraphQL/Definition/Fields/Search/SearchVideosField.php index 5948a7309..b6f7b4313 100644 --- a/app/GraphQL/Definition/Fields/Search/SearchVideosField.php +++ b/app/GraphQL/Definition/Fields/Search/SearchVideosField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Search; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\VideoType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class SearchVideosField. */ -class SearchVideosField extends Field +class SearchVideosField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class SearchVideosField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(Type::nonNull(new VideoType())); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/StringField.php b/app/GraphQL/Definition/Fields/StringField.php index 5d1178e11..8d5221658 100644 --- a/app/GraphQL/Definition/Fields/StringField.php +++ b/app/GraphQL/Definition/Fields/StringField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\FilterableField; use App\GraphQL\Definition\Directives\Filters\EqFilterDirective; use App\GraphQL\Definition\Directives\Filters\FilterDirective; @@ -13,18 +14,28 @@ use GraphQL\Type\Definition\Type; /** * Class StringField. */ -abstract class StringField extends Field implements FilterableField +abstract class StringField extends Field implements DisplayableField, FilterableField { /** * The type returned by the field. * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::string(); } + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } + /** * The directives available for this filter. * diff --git a/app/GraphQL/Definition/Fields/User/Notification/NotificationDataField.php b/app/GraphQL/Definition/Fields/User/Notification/NotificationDataField.php index b4a88d30a..d564bc497 100644 --- a/app/GraphQL/Definition/Fields/User/Notification/NotificationDataField.php +++ b/app/GraphQL/Definition/Fields/User/Notification/NotificationDataField.php @@ -37,7 +37,7 @@ class NotificationDataField extends JsonField * * @return Type */ - protected function type(): Type + public function type(): Type { return new NotificationDataType(); } diff --git a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearFallField.php b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearFallField.php index 8d0be5a27..74e896ca1 100644 --- a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearFallField.php +++ b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearFallField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Wiki\Anime\AnimeYear; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\AnimeType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class AnimeYearFallField. */ -class AnimeYearFallField extends Field +class AnimeYearFallField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class AnimeYearFallField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(new AnimeType()); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSpringField.php b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSpringField.php index 79c7f389b..ac6201876 100644 --- a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSpringField.php +++ b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSpringField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Wiki\Anime\AnimeYear; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\AnimeType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class AnimeYearSpringField. */ -class AnimeYearSpringField extends Field +class AnimeYearSpringField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class AnimeYearSpringField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(new AnimeType()); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSummerField.php b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSummerField.php index aa6865735..689ed305a 100644 --- a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSummerField.php +++ b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearSummerField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Wiki\Anime\AnimeYear; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\AnimeType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class AnimeYearSummerField. */ -class AnimeYearSummerField extends Field +class AnimeYearSummerField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class AnimeYearSummerField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(new AnimeType()); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearWinterField.php b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearWinterField.php index 0c39ccf5e..9755cb5ad 100644 --- a/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearWinterField.php +++ b/app/GraphQL/Definition/Fields/Wiki/Anime/AnimeYear/AnimeYearWinterField.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Fields\Wiki\Anime\AnimeYear; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\GraphQL\Definition\Fields\Field; use App\GraphQL\Definition\Types\Wiki\AnimeType; use GraphQL\Type\Definition\Type; @@ -11,7 +12,7 @@ use GraphQL\Type\Definition\Type; /** * Class AnimeYearWinterField. */ -class AnimeYearWinterField extends Field +class AnimeYearWinterField extends Field implements DisplayableField { /** * Create a new field instance. @@ -36,8 +37,18 @@ class AnimeYearWinterField extends Field * * @return Type */ - protected function type(): Type + public function type(): Type { return Type::listOf(new AnimeType()); } + + /** + * Determine if the field should be displayed to the user. + * + * @return bool + */ + public function canBeDisplayed(): bool + { + return true; + } } diff --git a/app/GraphQL/Definition/Mutations/BaseMutation.php b/app/GraphQL/Definition/Mutations/BaseMutation.php new file mode 100644 index 000000000..30a2cd650 --- /dev/null +++ b/app/GraphQL/Definition/Mutations/BaseMutation.php @@ -0,0 +1,97 @@ +resolveDirectives($this->directives()); + + $arguments = $this->buildArguments($this->arguments()); + + return " + \"\"\"{$this->description()}\"\"\" + {$this->name}{$arguments}: {$this->getType()->__toString()} {$directives} + "; + } + + /** + * The arguments of the mutation. + * + * @return string[] + */ + abstract public function arguments(): array; + + /** + * The directives of the mutation. + * + * @return array + * + * @throws Exception + */ + public function directives(): array + { + $field = $this->resolveFieldAttribute(); + + if ($field === null) { + throw new Exception("The mutation {$this->name} must implement an UseField attribute."); + } + + return [ + 'field' => [ + 'resolver' => $field, + ], + ]; + } + + /** + * The type returned by the mutation. + * + * @return Type + */ + abstract public function getType(): Type; + + /** + * The base return type of the mutation. + * + * @return Type + */ + abstract public function baseType(): Type; + + /** + * The description of the mutation. + * + * @return string + */ + abstract public function description(): string; +} diff --git a/app/GraphQL/Definition/Mutations/Rest/CreateMutation.php b/app/GraphQL/Definition/Mutations/Rest/CreateMutation.php new file mode 100644 index 000000000..1bc81c503 --- /dev/null +++ b/app/GraphQL/Definition/Mutations/Rest/CreateMutation.php @@ -0,0 +1,99 @@ + $model + */ + public function __construct(protected string $model) + { + parent::__construct('create'.ucfirst(class_basename($model))); + } + + /** + * Get the arguments for the create mutation. + * + * @return array + */ + public function arguments(): array + { + $arguments = []; + + $baseType = $this->baseType(); + + if ($baseType instanceof HasFields) { + $bindableFields = Arr::where($baseType->fields(), fn (Field $field) => $field instanceof BindableField && $field instanceof CreatableField); + $notBindableFields = Arr::where($baseType->fields(), fn (Field $field) => ! $field instanceof BindableField); + $arguments[] = $this->resolveBindArgument($bindableFields); + $arguments[] = $this->resolveCreateMutationArguments($notBindableFields); + } + + return $arguments; + } + + /** + * The directives of the mutation. + * + * @return array + */ + public function directives(): array + { + return [ + 'canModel' => [ + 'ability' => 'create', + 'injectArgs' => true, + 'model' => $this->model, + ], + + ...parent::directives(), + ]; + } + + /** + * Get the rules for the create mutation. + * + * @param array $args + * @return array + */ + public function rules(array $args): array + { + $baseType = $this->baseType(); + + if ($baseType instanceof HasFields) { + return collect($baseType->fields()) + ->filter(fn (Field $field) => $field instanceof CreatableField) + ->mapWithKeys(fn (Field&CreatableField $field) => [$field->getColumn() => $field->getCreationRules($args)]) + ->toArray(); + } + + return []; + } + + /** + * The type returned by the field. + * + * @return Type + */ + public function getType(): Type + { + return Type::nonNull($this->baseType()); + } +} diff --git a/app/GraphQL/Definition/Mutations/Rest/DeleteMutation.php b/app/GraphQL/Definition/Mutations/Rest/DeleteMutation.php new file mode 100644 index 000000000..d062d80d3 --- /dev/null +++ b/app/GraphQL/Definition/Mutations/Rest/DeleteMutation.php @@ -0,0 +1,74 @@ + $model + */ + public function __construct(protected string $model) + { + parent::__construct('delete'.ucfirst(class_basename($model))); + } + + /** + * Get the arguments for the create mutation. + * + * @return array + */ + public function arguments(): array + { + $arguments = []; + + $baseType = $this->baseType(); + + if ($baseType instanceof HasFields) { + $arguments[] = $this->resolveBindArgument($baseType->fields()); + } + + return $arguments; + } + + /** + * The directives of the mutation. + * + * @return array + */ + public function directives(): array + { + return [ + 'canModel' => [ + 'ability' => 'delete', + 'injectArgs' => true, + 'model' => $this->model, + ], + + ...parent::directives(), + ]; + } + + /** + * The type returned by the field. + * + * @return Type + */ + public function getType(): Type + { + return Type::nonNull($this->baseType()); + } +} diff --git a/app/GraphQL/Definition/Mutations/Rest/List/Playlist/CreatePlaylistMutation.php b/app/GraphQL/Definition/Mutations/Rest/List/Playlist/CreatePlaylistMutation.php new file mode 100644 index 000000000..6571c9fcc --- /dev/null +++ b/app/GraphQL/Definition/Mutations/Rest/List/Playlist/CreatePlaylistMutation.php @@ -0,0 +1,46 @@ + $model + */ + public function __construct(protected string $model) + { + parent::__construct('update'.ucfirst(class_basename($model))); + } + + /** + * Get the arguments for the create mutation. + * + * @return array + */ + public function arguments(): array + { + $arguments = []; + + $baseType = $this->baseType(); + + if ($baseType instanceof HasFields) { + $arguments[] = $this->resolveBindArgument($baseType->fields()); + $arguments[] = $this->resolveUpdateMutationArguments($baseType->fields()); + } + + return $arguments; + } + + /** + * The directives of the mutation. + * + * @return array + */ + public function directives(): array + { + return [ + 'canModel' => [ + 'ability' => 'update', + 'injectArgs' => true, + 'model' => $this->model, + ], + + ...parent::directives(), + ]; + } + + /** + * Get the rules for the create mutation. + * + * @param array $args + * @return array + */ + public function rules(array $args): array + { + $baseType = $this->baseType(); + + if ($baseType instanceof HasFields) { + return collect($baseType->fields()) + ->filter(fn (Field $field) => $field instanceof UpdatableField) + ->mapWithKeys(fn (Field&UpdatableField $field) => [$field->getColumn() => $field->getUpdateRules($args)]) + ->toArray(); + } + + return []; + } + + /** + * The type returned by the field. + * + * @return Type + */ + public function getType(): Type + { + return Type::nonNull($this->baseType()); + } +} diff --git a/app/GraphQL/Definition/Queries/BaseQuery.php b/app/GraphQL/Definition/Queries/BaseQuery.php index 647d3789f..f136d5b4f 100644 --- a/app/GraphQL/Definition/Queries/BaseQuery.php +++ b/app/GraphQL/Definition/Queries/BaseQuery.php @@ -49,7 +49,7 @@ abstract class BaseQuery } /** - * The description of the type. + * The description of the query. * * @return string */ @@ -64,9 +64,13 @@ abstract class BaseQuery { $builder = $this->resolveBuilderAttribute(); + $field = $this->resolveFieldAttribute(); + return [ ...(is_string($builder) ? ['builder' => ['method' => $builder]] : []), + ...(is_string($field) ? ['field' => ['resolver' => $field]] : []), + ...($this->paginated ? ['paginate' => []] : []), ]; } diff --git a/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearQuery.php b/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearQuery.php index 3b3e8bfe7..eeeff9b13 100644 --- a/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearQuery.php +++ b/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearQuery.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Queries\Wiki\Anime; +use App\GraphQL\Attributes\UseField; use App\GraphQL\Definition\Queries\BaseQuery; use App\GraphQL\Definition\Types\Wiki\Anime\AnimeYearType; use App\GraphQL\Queries\AnimeYear; @@ -12,6 +13,7 @@ use App\Models\Wiki\Anime; /** * Class AnimeYearQuery. */ +#[UseField(AnimeYear::class, 'year')] class AnimeYearQuery extends BaseQuery { public function __construct() @@ -44,10 +46,6 @@ class AnimeYearQuery extends BaseQuery 'injectArgs' => 'true', 'model' => Anime::class, ], - - 'field' => [ - 'resolver' => AnimeYear::class.'@year', - ], ]; } diff --git a/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearsQuery.php b/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearsQuery.php index 64fda6286..b88e7fc09 100644 --- a/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearsQuery.php +++ b/app/GraphQL/Definition/Queries/Wiki/Anime/AnimeYearsQuery.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Queries\Wiki\Anime; +use App\GraphQL\Attributes\UseField; use App\GraphQL\Definition\Queries\BaseQuery; use App\GraphQL\Queries\AnimeYear; use App\Models\Wiki\Anime; @@ -12,6 +13,7 @@ use GraphQL\Type\Definition\Type; /** * Class AnimeYearsQuery. */ +#[UseField(AnimeYear::class, 'years')] class AnimeYearsQuery extends BaseQuery { public function __construct() @@ -44,10 +46,6 @@ class AnimeYearsQuery extends BaseQuery 'injectArgs' => 'true', 'model' => Anime::class, ], - - 'field' => [ - 'resolver' => AnimeYear::class.'@years', - ], ]; } diff --git a/app/GraphQL/Definition/Relations/BelongsToManyRelation.php b/app/GraphQL/Definition/Relations/BelongsToManyRelation.php index 84c2aa668..f373a8fd7 100644 --- a/app/GraphQL/Definition/Relations/BelongsToManyRelation.php +++ b/app/GraphQL/Definition/Relations/BelongsToManyRelation.php @@ -17,7 +17,7 @@ class BelongsToManyRelation extends Relation * * @return Type */ - protected function type(): Type + public function type(): Type { if (! $this->nullable) { return Type::nonNull(Type::listOf($this->type)); diff --git a/app/GraphQL/Definition/Relations/BelongsToRelation.php b/app/GraphQL/Definition/Relations/BelongsToRelation.php index 2b91ce593..ccc02570c 100644 --- a/app/GraphQL/Definition/Relations/BelongsToRelation.php +++ b/app/GraphQL/Definition/Relations/BelongsToRelation.php @@ -17,7 +17,7 @@ class BelongsToRelation extends Relation * * @return Type */ - protected function type(): Type + public function type(): Type { if (! $this->nullable) { return Type::nonNull($this->type); diff --git a/app/GraphQL/Definition/Relations/HasManyRelation.php b/app/GraphQL/Definition/Relations/HasManyRelation.php index a12bf40de..0a52c35b3 100644 --- a/app/GraphQL/Definition/Relations/HasManyRelation.php +++ b/app/GraphQL/Definition/Relations/HasManyRelation.php @@ -17,7 +17,7 @@ class HasManyRelation extends Relation * * @return Type */ - protected function type(): Type + public function type(): Type { if (! $this->nullable) { return Type::nonNull(Type::listOf($this->type)); diff --git a/app/GraphQL/Definition/Relations/HasOneRelation.php b/app/GraphQL/Definition/Relations/HasOneRelation.php index ae93c3efc..ac680f149 100644 --- a/app/GraphQL/Definition/Relations/HasOneRelation.php +++ b/app/GraphQL/Definition/Relations/HasOneRelation.php @@ -17,7 +17,7 @@ class HasOneRelation extends Relation * * @return Type */ - protected function type(): Type + public function type(): Type { if (! $this->nullable) { return Type::nonNull($this->type); diff --git a/app/GraphQL/Definition/Relations/MorphManyRelation.php b/app/GraphQL/Definition/Relations/MorphManyRelation.php index a53bb8a77..97fccf6c0 100644 --- a/app/GraphQL/Definition/Relations/MorphManyRelation.php +++ b/app/GraphQL/Definition/Relations/MorphManyRelation.php @@ -17,7 +17,7 @@ class MorphManyRelation extends Relation * * @return Type */ - protected function type(): Type + public function type(): Type { if (! $this->nullable) { return Type::nonNull(Type::listOf($this->type)); diff --git a/app/GraphQL/Definition/Relations/MorphToRelation.php b/app/GraphQL/Definition/Relations/MorphToRelation.php index 37368f895..57820f561 100644 --- a/app/GraphQL/Definition/Relations/MorphToRelation.php +++ b/app/GraphQL/Definition/Relations/MorphToRelation.php @@ -17,7 +17,7 @@ class MorphToRelation extends Relation * * @return Type */ - protected function type(): Type + public function type(): Type { if (! $this->nullable) { return Type::nonNull($this->type); diff --git a/app/GraphQL/Definition/Types/BaseType.php b/app/GraphQL/Definition/Types/BaseType.php index 7c7071b25..5448ee954 100644 --- a/app/GraphQL/Definition/Types/BaseType.php +++ b/app/GraphQL/Definition/Types/BaseType.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\GraphQL\Definition\Types; use App\Concerns\GraphQL\ResolvesDirectives; +use App\Contracts\GraphQL\Fields\DisplayableField; use App\Contracts\GraphQL\HasDirectives; use App\Contracts\GraphQL\HasFields; use App\Contracts\GraphQL\HasRelations; @@ -27,6 +28,7 @@ abstract class BaseType extends ObjectType $fields = []; if ($this instanceof HasFields) { $fields[] = collect($this->fields()) + ->filter(fn (Field $field) => $field instanceof DisplayableField && $field->canBeDisplayed()) ->mapWithKeys(fn (Field $field) => [ $field->getName() => [ 'description' => $field->description(), @@ -55,14 +57,17 @@ abstract class BaseType extends ObjectType { $fields = []; if ($this instanceof HasFields) { - $fields[] = Arr::map($this->fields(), function (Field $field) { - return Str::of('"') - ->append($field->description()) - ->append('"') - ->newLine() - ->append($field->__toString()) - ->__toString(); - }); + $fields[] = collect($this->fields()) + ->filter(fn (Field $field) => $field instanceof DisplayableField && $field->canBeDisplayed()) + ->map(function (Field $field) { + return Str::of('"') + ->append($field->description()) + ->append('"') + ->newLine() + ->append($field->__toString()) + ->__toString(); + }) + ->toArray(); } if ($this instanceof HasRelations) { diff --git a/app/GraphQL/Definition/Types/List/Playlist/PlaylistTrackType.php b/app/GraphQL/Definition/Types/List/Playlist/PlaylistTrackType.php index 3cd880f9a..1b070ce97 100644 --- a/app/GraphQL/Definition/Types/List/Playlist/PlaylistTrackType.php +++ b/app/GraphQL/Definition/Types/List/Playlist/PlaylistTrackType.php @@ -9,7 +9,12 @@ use App\Contracts\GraphQL\HasRelations; use App\GraphQL\Definition\Fields\Base\CreatedAtField; use App\GraphQL\Definition\Fields\Base\UpdatedAtField; use App\GraphQL\Definition\Fields\Field; +use App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack\PlaylistTrackEntryIdField; use App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack\PlaylistTrackIdField; +use App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack\PlaylistTrackNextField; +use App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack\PlaylistTrackPlaylistField; +use App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack\PlaylistTrackPreviousField; +use App\GraphQL\Definition\Fields\List\Playlist\PlaylistTrack\PlaylistTrackVideoIdField; use App\GraphQL\Definition\Relations\BelongsToRelation; use App\GraphQL\Definition\Relations\Relation; use App\GraphQL\Definition\Types\EloquentType; @@ -58,6 +63,11 @@ class PlaylistTrackType extends EloquentType implements HasFields, HasRelations { return [ new PlaylistTrackIdField(), + new PlaylistTrackEntryIdField(), + new PlaylistTrackVideoIdField(), + new PlaylistTrackNextField(), + new PlaylistTrackPreviousField(), + new PlaylistTrackPlaylistField(), new CreatedAtField(), new UpdatedAtField(), ]; diff --git a/app/GraphQL/Policies/List/ExternalProfilePolicy.php b/app/GraphQL/Policies/List/ExternalProfilePolicy.php index 6240666b8..7e891ca7e 100644 --- a/app/GraphQL/Policies/List/ExternalProfilePolicy.php +++ b/app/GraphQL/Policies/List/ExternalProfilePolicy.php @@ -6,7 +6,7 @@ namespace App\GraphQL\Policies\List; use App\Enums\Auth\CrudPermission; use App\Enums\Models\List\ExternalProfileVisibility; -use App\GraphQL\Mutations\List\ExternalProfileMutator; +use App\GraphQL\Controllers\List\ExternalProfileController; use App\GraphQL\Policies\BasePolicy; use App\Models\Auth\User; use App\Models\List\ExternalProfile; @@ -25,7 +25,7 @@ class ExternalProfilePolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function view(?User $user, ?array $injected = null, ?string $keyName = ExternalProfileMutator::ROUTE_SLUG): bool + public function view(?User $user, ?array $injected = null, ?string $keyName = ExternalProfileController::ROUTE_SLUG): bool { /** @var ExternalProfile $profile */ $profile = Arr::get($injected, $keyName); @@ -46,7 +46,7 @@ class ExternalProfilePolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function update(User $user, array $injected, ?string $keyName = ExternalProfileMutator::ROUTE_SLUG): bool + public function update(User $user, array $injected, ?string $keyName = ExternalProfileController::ROUTE_SLUG): bool { /** @var ExternalProfile $profile */ $profile = Arr::get($injected, $keyName); @@ -62,7 +62,7 @@ class ExternalProfilePolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function delete(User $user, array $injected, ?string $keyName = ExternalProfileMutator::ROUTE_SLUG): bool + public function delete(User $user, array $injected, ?string $keyName = ExternalProfileController::ROUTE_SLUG): bool { /** @var ExternalProfile $profile */ $profile = Arr::get($injected, $keyName); @@ -78,7 +78,7 @@ class ExternalProfilePolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function restore(User $user, array $injected, ?string $keyName = ExternalProfileMutator::ROUTE_SLUG): bool + public function restore(User $user, array $injected, ?string $keyName = ExternalProfileController::ROUTE_SLUG): bool { /** @var ExternalProfile $profile */ $profile = Arr::get($injected, $keyName); diff --git a/app/GraphQL/Policies/List/Playlist/PlaylistTrackPolicy.php b/app/GraphQL/Policies/List/Playlist/PlaylistTrackPolicy.php index e040a6acc..1e184b61f 100644 --- a/app/GraphQL/Policies/List/Playlist/PlaylistTrackPolicy.php +++ b/app/GraphQL/Policies/List/Playlist/PlaylistTrackPolicy.php @@ -6,7 +6,7 @@ namespace App\GraphQL\Policies\List\Playlist; use App\Enums\Auth\CrudPermission; use App\Enums\Models\List\PlaylistVisibility; -use App\GraphQL\Mutations\List\Playlist\PlaylistTrackMutator; +use App\GraphQL\Controllers\List\Playlist\PlaylistTrackController; use App\GraphQL\Policies\BasePolicy; use App\Models\Auth\User; use App\Models\List\Playlist; @@ -87,7 +87,7 @@ class PlaylistTrackPolicy extends BasePolicy /** @var Playlist|null $playlist */ $playlist = Arr::get($injected, 'playlist'); /** @var PlaylistTrack $track */ - $track = Arr::get($injected, PlaylistTrackMutator::ROUTE_SLUG); + $track = Arr::get($injected, PlaylistTrackController::ROUTE_SLUG); return $playlist?->user()->is($user) && parent::update($user, $injected, $keyName); } @@ -105,7 +105,7 @@ class PlaylistTrackPolicy extends BasePolicy /** @var Playlist|null $playlist */ $playlist = Arr::get($injected, 'playlist'); /** @var PlaylistTrack $track */ - $track = Arr::get($injected, PlaylistTrackMutator::ROUTE_SLUG); + $track = Arr::get($injected, PlaylistTrackController::ROUTE_SLUG); return $playlist?->user()->is($user) && parent::delete($user, $injected, $keyName); } @@ -123,7 +123,7 @@ class PlaylistTrackPolicy extends BasePolicy /** @var Playlist|null $playlist */ $playlist = Arr::get($injected, 'playlist'); /** @var PlaylistTrack $track */ - $track = Arr::get($injected, PlaylistTrackMutator::ROUTE_SLUG); + $track = Arr::get($injected, PlaylistTrackController::ROUTE_SLUG); return $playlist?->user()->is($user) && parent::restore($user, $injected, $keyName); } diff --git a/app/GraphQL/Policies/List/PlaylistPolicy.php b/app/GraphQL/Policies/List/PlaylistPolicy.php index 57d280bf7..0e1c99195 100644 --- a/app/GraphQL/Policies/List/PlaylistPolicy.php +++ b/app/GraphQL/Policies/List/PlaylistPolicy.php @@ -6,7 +6,7 @@ namespace App\GraphQL\Policies\List; use App\Enums\Auth\CrudPermission; use App\Enums\Models\List\PlaylistVisibility; -use App\GraphQL\Mutations\List\PlaylistMutator; +use App\GraphQL\Controllers\List\PlaylistController; use App\GraphQL\Policies\BasePolicy; use App\Models\Auth\User; use App\Models\List\Playlist; @@ -25,7 +25,7 @@ class PlaylistPolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function view(?User $user, ?array $injected = null, ?string $keyName = PlaylistMutator::ROUTE_SLUG): bool + public function view(?User $user, ?array $injected = null, ?string $keyName = PlaylistController::ROUTE_SLUG): bool { /** @var Playlist $playlist */ $playlist = Arr::get($injected, $keyName); @@ -46,7 +46,7 @@ class PlaylistPolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function update(User $user, array $injected, ?string $keyName = PlaylistMutator::ROUTE_SLUG): bool + public function update(User $user, array $injected, ?string $keyName = PlaylistController::ROUTE_SLUG): bool { /** @var Playlist $playlist */ $playlist = Arr::get($injected, $keyName); @@ -62,7 +62,7 @@ class PlaylistPolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function delete(User $user, array $injected, ?string $keyName = PlaylistMutator::ROUTE_SLUG): bool + public function delete(User $user, array $injected, ?string $keyName = PlaylistController::ROUTE_SLUG): bool { /** @var Playlist $playlist */ $playlist = Arr::get($injected, $keyName); @@ -78,7 +78,7 @@ class PlaylistPolicy extends BasePolicy * @param string|null $keyName * @return bool */ - public function restore(User $user, array $injected, ?string $keyName = PlaylistMutator::ROUTE_SLUG): bool + public function restore(User $user, array $injected, ?string $keyName = PlaylistController::ROUTE_SLUG): bool { /** @var Playlist $playlist */ $playlist = Arr::get($injected, $keyName); diff --git a/app/GraphQL/Schemas/List/Playlist/playlisttrack.graphql b/app/GraphQL/Schemas/List/Playlist/playlisttrack.graphql index 0ca876fab..0353e18d8 100644 --- a/app/GraphQL/Schemas/List/Playlist/playlisttrack.graphql +++ b/app/GraphQL/Schemas/List/Playlist/playlisttrack.graphql @@ -28,36 +28,36 @@ extend type Query { @paginate } -extend type Mutation @guard { - createPlaylistTrack( - entry_id: Int! - video_id: Int! - next: String - previous: String - playlist: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") - ): PlaylistTrack! - @canModel(ability: "create", injectArgs: true) - @validator(class: "App\\GraphQL\\Validators\\Mutation\\List\\Playlist\\Track\\CreatePlaylistTrackValidator") - @field(resolver: "App\\GraphQL\\Mutations\\List\\Playlist\\PlaylistTrackMutator@store") +# extend type Mutation @guard { +# createPlaylistTrack( +# entry_id: Int! +# video_id: Int! +# next: String +# previous: String +# playlist: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") +# ): PlaylistTrack! +# @canModel(ability: "create", injectArgs: true) +# @validator(class: "App\\GraphQL\\Validators\\Mutation\\List\\Playlist\\Track\\CreatePlaylistTrackValidator") +# @field(resolver: "App\\GraphQL\\Controllers\\List\\Playlist\\PlaylistTrackController@store") - updatePlaylistTrack( - id: String! @bind(class: "App\\Models\\List\\Playlist\\PlaylistTrack", column: "hashid") - entry_id: Int - video_id: Int - next: String - previous: String - playlist: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") - ): PlaylistTrack! - @canModel(ability: "update", injectArgs: true) - @validator(class: "App\\GraphQL\\Validators\\Mutation\\List\\Playlist\\Track\\UpdatePlaylistTrackValidator") - @field(resolver: "App\\GraphQL\\Mutations\\List\\Playlist\\PlaylistTrackMutator@update") +# updatePlaylistTrack( +# id: String! @bind(class: "App\\Models\\List\\Playlist\\PlaylistTrack", column: "hashid") +# entry_id: Int +# video_id: Int +# next: String +# previous: String +# playlist: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") +# ): PlaylistTrack! +# @canModel(ability: "update", injectArgs: true) +# @validator(class: "App\\GraphQL\\Validators\\Mutation\\List\\Playlist\\Track\\UpdatePlaylistTrackValidator") +# @field(resolver: "App\\GraphQL\\Controllers\\List\\Playlist\\PlaylistTrackController@update") - deletePlaylistTrack( - id: String! @bind(class: "App\\Models\\List\\Playlist\\PlaylistTrack", column: "hashid") - playlist: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") - ): PlaylistTrack! - @canModel(ability: "delete", injectArgs: true) - @field(resolver: "App\\GraphQL\\Mutations\\List\\Playlist\\PlaylistTrackMutator@destroy") -} \ No newline at end of file +# deletePlaylistTrack( +# id: String! @bind(class: "App\\Models\\List\\Playlist\\PlaylistTrack", column: "hashid") +# playlist: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") +# ): PlaylistTrack! +# @canModel(ability: "delete", injectArgs: true) +# @field(resolver: "App\\GraphQL\\Controllers\\List\\Playlist\\PlaylistTrackController@destroy") +# } \ No newline at end of file diff --git a/app/GraphQL/Schemas/List/externalprofile.graphql b/app/GraphQL/Schemas/List/externalprofile.graphql index 0c4f43d2c..5acff6ce7 100644 --- a/app/GraphQL/Schemas/List/externalprofile.graphql +++ b/app/GraphQL/Schemas/List/externalprofile.graphql @@ -24,5 +24,5 @@ extend type Mutation @guard { @middleware(class: "App\\Http\\Middleware\\Api\\EnabledOnlyOnLocalhost") @featureEnabled(class: "App\\Features\\AllowExternalProfileManagement") @canModel(ability: "update", injectArgs: true, model: "App\\Models\\List\\ExternalProfile") - @field(resolver: "App\\GraphQL\\Mutations\\List\\ExternalProfileMutator@sync") + @field(resolver: "App\\GraphQL\\Controllers\\List\\ExternalProfileController@sync") } \ No newline at end of file diff --git a/app/GraphQL/Schemas/List/playlist.graphql b/app/GraphQL/Schemas/List/playlist.graphql index f2789591c..962023505 100644 --- a/app/GraphQL/Schemas/List/playlist.graphql +++ b/app/GraphQL/Schemas/List/playlist.graphql @@ -22,33 +22,4 @@ extend type Query { id: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") ): Playlist @first @canModel(ability: "view", injectArgs: true) -} - -extend type Mutation @guard { - createPlaylist( - name: String! - description: String - visibility: PlaylistVisibility! - ): Playlist! - @canModel(ability: "create") - @validator(class: "App\\GraphQL\\Validators\\Mutation\\List\\Playlist\\CreatePlaylistValidator") - @field(resolver: "App\\GraphQL\\Mutations\\List\\PlaylistMutator@store") - - - updatePlaylist( - id: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") - name: String - description: String - visibility: PlaylistVisibility - ): Playlist! - @canModel(ability: "update", injectArgs: true) - @validator(class: "App\\GraphQL\\Validators\\Mutation\\List\\Playlist\\UpdatePlaylistValidator") - @field(resolver: "App\\GraphQL\\Mutations\\List\\PlaylistMutator@update") - - - deletePlaylist( - id: String! @bind(class: "App\\Models\\List\\Playlist", column: "hashid") - ): Playlist! - @canModel(ability: "delete", injectArgs: true) - @field(resolver: "App\\GraphQL\\Mutations\\List\\PlaylistMutator@destroy") } \ No newline at end of file diff --git a/app/GraphQL/Schemas/User/like.graphql b/app/GraphQL/Schemas/User/like.graphql index 16cb98ba0..202190c63 100644 --- a/app/GraphQL/Schemas/User/like.graphql +++ b/app/GraphQL/Schemas/User/like.graphql @@ -5,7 +5,7 @@ extend type Mutation @guard { ): Liked @canModel(ability: "create", model: "App\\Models\\User\\Like") @validator(class: "App\\GraphQL\\Validators\\Mutation\\User\\Like\\CreateLikeValidator") - @field(resolver: "App\\GraphQL\\Mutations\\User\\LikeMutator@store") + @field(resolver: "App\\GraphQL\\Controllers\\User\\LikeController@store") unlike( playlist: String @bind(class: "App\\Models\\List\\Playlist", column: "hashid") @@ -13,5 +13,5 @@ extend type Mutation @guard { ): Liked @canModel(ability: "delete", injectArgs: true, model: "App\\Models\\User\\Like") @validator(class: "App\\GraphQL\\Validators\\Mutation\\User\\Like\\DeleteLikeValidator") - @field(resolver: "App\\GraphQL\\Mutations\\User\\LikeMutator@destroy") + @field(resolver: "App\\GraphQL\\Controllers\\User\\LikeController@destroy") } \ No newline at end of file diff --git a/app/GraphQL/Validators/Mutation/List/Playlist/CreatePlaylistValidator.php b/app/GraphQL/Validators/Mutation/List/Playlist/CreatePlaylistValidator.php deleted file mode 100644 index e4cb2613f..000000000 --- a/app/GraphQL/Validators/Mutation/List/Playlist/CreatePlaylistValidator.php +++ /dev/null @@ -1,44 +0,0 @@ -> - */ - public function rules(): array - { - return [ - Playlist::ATTRIBUTE_NAME => [ - 'required', - 'string', - 'max:192', - new ModerationRule(), - ], - Playlist::ATTRIBUTE_DESCRIPTION => [ - 'nullable', - 'string', - 'max:1000', - new ModerationRule(), - ], - Playlist::ATTRIBUTE_VISIBILITY => [ - 'required', - new Enum(PlaylistVisibility::class), - ], - ]; - } -} diff --git a/app/GraphQL/Validators/Mutation/List/Playlist/Track/CreatePlaylistTrackValidator.php b/app/GraphQL/Validators/Mutation/List/Playlist/Track/CreatePlaylistTrackValidator.php deleted file mode 100644 index 86126a08f..000000000 --- a/app/GraphQL/Validators/Mutation/List/Playlist/Track/CreatePlaylistTrackValidator.php +++ /dev/null @@ -1,65 +0,0 @@ -> - */ - public function rules(): array - { - $playlistHashid = $this->arg('playlist'); - $playlist = Playlist::query()->firstWhere(Playlist::ATTRIBUTE_HASHID, $playlistHashid); - - return [ - PlaylistTrack::ATTRIBUTE_ENTRY => [ - 'required', - 'integer', - Rule::exists(AnimeThemeEntry::class, AnimeThemeEntry::ATTRIBUTE_ID), - Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_ENTRY) - ->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, $this->arg(PlaylistTrack::ATTRIBUTE_VIDEO)), - ], - PlaylistTrack::RELATION_NEXT => [ - 'sometimes', - 'required', - 'string', - Str::of('prohibits:')->append(PlaylistTrack::RELATION_PREVIOUS)->__toString(), - Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) - ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()), - ], - PlaylistTrack::RELATION_PREVIOUS => [ - 'sometimes', - 'required', - 'string', - Str::of('prohibits:')->append(PlaylistTrack::RELATION_NEXT)->__toString(), - Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) - ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()), - ], - PlaylistTrack::ATTRIBUTE_VIDEO => [ - 'required', - 'integer', - Rule::exists(Video::class, Video::ATTRIBUTE_ID), - Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_VIDEO) - ->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, $this->arg(PlaylistTrack::ATTRIBUTE_ENTRY)), - ], - ]; - } -} diff --git a/app/GraphQL/Validators/Mutation/List/Playlist/Track/UpdatePlaylistTrackValidator.php b/app/GraphQL/Validators/Mutation/List/Playlist/Track/UpdatePlaylistTrackValidator.php deleted file mode 100644 index 97fbe3365..000000000 --- a/app/GraphQL/Validators/Mutation/List/Playlist/Track/UpdatePlaylistTrackValidator.php +++ /dev/null @@ -1,84 +0,0 @@ -> - */ - public function rules(): array - { - $playlistHashid = $this->arg('playlist'); - $hashid = $this->arg(PlaylistTrack::ATTRIBUTE_HASHID); - $entryId = $this->arg(PlaylistTrack::ATTRIBUTE_ENTRY); - $videoId = $this->arg(PlaylistTrack::ATTRIBUTE_VIDEO); - - $playlist = Playlist::query()->firstWhere(Playlist::ATTRIBUTE_HASHID, $playlistHashid); - $track = PlaylistTrack::query()->firstWhere(PlaylistTrack::ATTRIBUTE_HASHID, $hashid); - - return [ - PlaylistTrack::ATTRIBUTE_ENTRY => [ - 'sometimes', - 'required', - 'integer', - Rule::exists(AnimeThemeEntry::class, AnimeThemeEntry::ATTRIBUTE_ID), - Rule::when( - ! empty($videoId), - [ - Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_ENTRY) - ->where(AnimeThemeEntryVideo::ATTRIBUTE_VIDEO, $videoId), - ] - ), - ], - PlaylistTrack::RELATION_NEXT => [ - 'sometimes', - 'required', - 'string', - Str::of('prohibits:')->append(PlaylistTrack::RELATION_PREVIOUS)->__toString(), - Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) - ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()) - ->whereNot(PlaylistTrack::ATTRIBUTE_ID, $track?->getKey()), - ], - PlaylistTrack::RELATION_PREVIOUS => [ - 'sometimes', - 'required', - 'string', - Str::of('prohibits:')->append(PlaylistTrack::RELATION_NEXT)->__toString(), - Rule::exists(PlaylistTrack::class, HasHashids::ATTRIBUTE_HASHID) - ->where(PlaylistTrack::ATTRIBUTE_PLAYLIST, $playlist?->getKey()) - ->whereNot(PlaylistTrack::ATTRIBUTE_ID, $track?->getKey()), - ], - PlaylistTrack::ATTRIBUTE_VIDEO => [ - 'sometimes', - 'required', - 'integer', - Rule::exists(Video::class, Video::ATTRIBUTE_ID), - Rule::when( - ! empty($entryId), - [ - Rule::exists(AnimeThemeEntryVideo::class, AnimeThemeEntryVideo::ATTRIBUTE_VIDEO) - ->where(AnimeThemeEntryVideo::ATTRIBUTE_ENTRY, $entryId), - ] - ), - ], - ]; - } -} diff --git a/app/GraphQL/Validators/Mutation/List/Playlist/UpdatePlaylistValidator.php b/app/GraphQL/Validators/Mutation/List/Playlist/UpdatePlaylistValidator.php deleted file mode 100644 index 2963a2e6e..000000000 --- a/app/GraphQL/Validators/Mutation/List/Playlist/UpdatePlaylistValidator.php +++ /dev/null @@ -1,46 +0,0 @@ -> - */ - public function rules(): array - { - return [ - Playlist::ATTRIBUTE_NAME => [ - 'sometimes', - 'required', - 'string', - 'max:192', - new ModerationRule(), - ], - Playlist::ATTRIBUTE_DESCRIPTION => [ - 'nullable', - 'string', - 'max:1000', - new ModerationRule(), - ], - Playlist::ATTRIBUTE_VISIBILITY => [ - 'sometimes', - 'required', - new Enum(PlaylistVisibility::class), - ], - ]; - } -} diff --git a/app/GraphQL/Validators/Mutation/User/Like/CreateLikeValidator.php b/app/GraphQL/Validators/Mutation/User/Like/CreateLikeValidator.php index 95801d5da..634df32c4 100644 --- a/app/GraphQL/Validators/Mutation/User/Like/CreateLikeValidator.php +++ b/app/GraphQL/Validators/Mutation/User/Like/CreateLikeValidator.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Validators\Mutation\User\Like; -use App\GraphQL\Mutations\User\LikeMutator; +use App\GraphQL\Controllers\User\LikeController; use Illuminate\Support\Str; use Nuwave\Lighthouse\Validation\Validator; @@ -21,11 +21,11 @@ class CreateLikeValidator extends Validator public function rules(): array { return [ - LikeMutator::ATTRIBUTE_PLAYLIST => [ - Str::of('prohibits:')->append(LikeMutator::ATTRIBUTE_VIDEO)->__toString(), + LikeController::ATTRIBUTE_PLAYLIST => [ + Str::of('prohibits:')->append(LikeController::ATTRIBUTE_VIDEO)->__toString(), ], - LikeMutator::ATTRIBUTE_VIDEO => [ - Str::of('prohibits:')->append(LikeMutator::ATTRIBUTE_PLAYLIST)->__toString(), + LikeController::ATTRIBUTE_VIDEO => [ + Str::of('prohibits:')->append(LikeController::ATTRIBUTE_PLAYLIST)->__toString(), ], ]; } diff --git a/app/GraphQL/Validators/Mutation/User/Like/DeleteLikeValidator.php b/app/GraphQL/Validators/Mutation/User/Like/DeleteLikeValidator.php index 29b315087..b1e3711c0 100644 --- a/app/GraphQL/Validators/Mutation/User/Like/DeleteLikeValidator.php +++ b/app/GraphQL/Validators/Mutation/User/Like/DeleteLikeValidator.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\GraphQL\Validators\Mutation\User\Like; -use App\GraphQL\Mutations\User\LikeMutator; +use App\GraphQL\Controllers\User\LikeController; use Illuminate\Support\Str; use Nuwave\Lighthouse\Validation\Validator; @@ -21,11 +21,11 @@ class DeleteLikeValidator extends Validator public function rules(): array { return [ - LikeMutator::ATTRIBUTE_PLAYLIST => [ - Str::of('prohibits:')->append(LikeMutator::ATTRIBUTE_VIDEO)->__toString(), + LikeController::ATTRIBUTE_PLAYLIST => [ + Str::of('prohibits:')->append(LikeController::ATTRIBUTE_VIDEO)->__toString(), ], - LikeMutator::ATTRIBUTE_VIDEO => [ - Str::of('prohibits:')->append(LikeMutator::ATTRIBUTE_PLAYLIST)->__toString(), + LikeController::ATTRIBUTE_VIDEO => [ + Str::of('prohibits:')->append(LikeController::ATTRIBUTE_PLAYLIST)->__toString(), ], ]; } diff --git a/app/Http/Controllers/Api/Admin/AnnouncementController.php b/app/Http/Controllers/Api/Admin/AnnouncementController.php index 205c98565..f3dc1d785 100644 --- a/app/Http/Controllers/Api/Admin/AnnouncementController.php +++ b/app/Http/Controllers/Api/Admin/AnnouncementController.php @@ -54,7 +54,7 @@ class AnnouncementController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return AnnouncementResource */ public function store(StoreRequest $request, StoreAction $action): AnnouncementResource diff --git a/app/Http/Controllers/Api/Admin/DumpController.php b/app/Http/Controllers/Api/Admin/DumpController.php index c95915287..9f9590799 100644 --- a/app/Http/Controllers/Api/Admin/DumpController.php +++ b/app/Http/Controllers/Api/Admin/DumpController.php @@ -54,7 +54,7 @@ class DumpController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return DumpResource */ public function store(StoreRequest $request, StoreAction $action): DumpResource diff --git a/app/Http/Controllers/Api/Admin/FeaturedThemeController.php b/app/Http/Controllers/Api/Admin/FeaturedThemeController.php index 39614ca75..6baf8ca7a 100644 --- a/app/Http/Controllers/Api/Admin/FeaturedThemeController.php +++ b/app/Http/Controllers/Api/Admin/FeaturedThemeController.php @@ -59,7 +59,7 @@ class FeaturedThemeController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return FeaturedThemeResource */ public function store(StoreRequest $request, StoreAction $action): FeaturedThemeResource diff --git a/app/Http/Controllers/Api/Document/PageController.php b/app/Http/Controllers/Api/Document/PageController.php index 460fe8702..073188963 100644 --- a/app/Http/Controllers/Api/Document/PageController.php +++ b/app/Http/Controllers/Api/Document/PageController.php @@ -55,7 +55,7 @@ class PageController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return PageResource */ public function store(StoreRequest $request, StoreAction $action): PageResource diff --git a/app/Http/Controllers/Api/List/ExternalProfileController.php b/app/Http/Controllers/Api/List/ExternalProfileController.php index b8e66d951..1647cbd60 100644 --- a/app/Http/Controllers/Api/List/ExternalProfileController.php +++ b/app/Http/Controllers/Api/List/ExternalProfileController.php @@ -95,7 +95,7 @@ class ExternalProfileController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return ExternalProfileResource */ public function store(StoreRequest $request, StoreAction $action): ExternalProfileResource diff --git a/app/Http/Controllers/Api/List/PlaylistController.php b/app/Http/Controllers/Api/List/PlaylistController.php index f22d38a2e..488681bb1 100644 --- a/app/Http/Controllers/Api/List/PlaylistController.php +++ b/app/Http/Controllers/Api/List/PlaylistController.php @@ -71,7 +71,7 @@ class PlaylistController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return PlaylistResource */ public function store(StoreRequest $request, StoreAction $action): PlaylistResource diff --git a/app/Http/Controllers/Api/Pivot/List/PlaylistImageController.php b/app/Http/Controllers/Api/Pivot/List/PlaylistImageController.php index e9482b87b..b530bb5fe 100644 --- a/app/Http/Controllers/Api/Pivot/List/PlaylistImageController.php +++ b/app/Http/Controllers/Api/Pivot/List/PlaylistImageController.php @@ -72,7 +72,7 @@ class PlaylistImageController extends PivotController * @param StoreRequest $request * @param Playlist $playlist * @param Image $image - * @param StoreAction $action + * @param StoreAction $action * @return PlaylistImageResource */ public function store(StoreRequest $request, Playlist $playlist, Image $image, StoreAction $action): PlaylistImageResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/AnimeImageController.php b/app/Http/Controllers/Api/Pivot/Wiki/AnimeImageController.php index 99a2c799e..b810c0cb8 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/AnimeImageController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/AnimeImageController.php @@ -55,7 +55,7 @@ class AnimeImageController extends PivotController * @param StoreRequest $request * @param Anime $anime * @param Image $image - * @param StoreAction $action + * @param StoreAction $action * @return AnimeImageResource */ public function store(StoreRequest $request, Anime $anime, Image $image, StoreAction $action): AnimeImageResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/AnimeResourceController.php b/app/Http/Controllers/Api/Pivot/Wiki/AnimeResourceController.php index 9d8856e88..a6469b781 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/AnimeResourceController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/AnimeResourceController.php @@ -57,7 +57,7 @@ class AnimeResourceController extends PivotController * @param StoreRequest $request * @param Anime $anime * @param ExternalResource $resource - * @param StoreAction $action + * @param StoreAction $action * @return AnimeResourceResource */ public function store(StoreRequest $request, Anime $anime, ExternalResource $resource, StoreAction $action): AnimeResourceResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/AnimeSeriesController.php b/app/Http/Controllers/Api/Pivot/Wiki/AnimeSeriesController.php index 814c6828a..075fb1fa0 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/AnimeSeriesController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/AnimeSeriesController.php @@ -55,7 +55,7 @@ class AnimeSeriesController extends PivotController * @param StoreRequest $request * @param Anime $anime * @param Series $series - * @param StoreAction $action + * @param StoreAction $action * @return AnimeSeriesResource */ public function store(StoreRequest $request, Anime $anime, Series $series, StoreAction $action): AnimeSeriesResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/AnimeStudioController.php b/app/Http/Controllers/Api/Pivot/Wiki/AnimeStudioController.php index 1ab2a1c96..65eeddf05 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/AnimeStudioController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/AnimeStudioController.php @@ -55,7 +55,7 @@ class AnimeStudioController extends PivotController * @param StoreRequest $request * @param Anime $anime * @param Studio $studio - * @param StoreAction $action + * @param StoreAction $action * @return AnimeStudioResource */ public function store(StoreRequest $request, Anime $anime, Studio $studio, StoreAction $action): AnimeStudioResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/AnimeThemeEntryVideoController.php b/app/Http/Controllers/Api/Pivot/Wiki/AnimeThemeEntryVideoController.php index 0c8aafbbe..b71562e5c 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/AnimeThemeEntryVideoController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/AnimeThemeEntryVideoController.php @@ -55,7 +55,7 @@ class AnimeThemeEntryVideoController extends PivotController * @param StoreRequest $request * @param AnimeThemeEntry $animethemeentry * @param Video $video - * @param StoreAction $action + * @param StoreAction $action * @return AnimeThemeEntryVideoResource */ public function store(StoreRequest $request, AnimeThemeEntry $animethemeentry, Video $video, StoreAction $action): AnimeThemeEntryVideoResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/ArtistImageController.php b/app/Http/Controllers/Api/Pivot/Wiki/ArtistImageController.php index 4441b85f7..002225d15 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/ArtistImageController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/ArtistImageController.php @@ -55,7 +55,7 @@ class ArtistImageController extends PivotController * @param StoreRequest $request * @param Artist $artist * @param Image $image - * @param StoreAction $action + * @param StoreAction $action * @return ArtistImageResource */ public function store(StoreRequest $request, Artist $artist, Image $image, StoreAction $action): ArtistImageResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/ArtistMemberController.php b/app/Http/Controllers/Api/Pivot/Wiki/ArtistMemberController.php index be4525ddb..3a595b0dd 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/ArtistMemberController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/ArtistMemberController.php @@ -56,7 +56,7 @@ class ArtistMemberController extends PivotController * @param StoreRequest $request * @param Artist $artist * @param Artist $member - * @param StoreAction $action + * @param StoreAction $action * @return ArtistMemberResource */ public function store(StoreRequest $request, Artist $artist, Artist $member, StoreAction $action): ArtistMemberResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/ArtistResourceController.php b/app/Http/Controllers/Api/Pivot/Wiki/ArtistResourceController.php index 5f74f1393..0494de063 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/ArtistResourceController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/ArtistResourceController.php @@ -57,7 +57,7 @@ class ArtistResourceController extends PivotController * @param StoreRequest $request * @param Artist $artist * @param ExternalResource $resource - * @param StoreAction $action + * @param StoreAction $action * @return ArtistResourceResource */ public function store(StoreRequest $request, Artist $artist, ExternalResource $resource, StoreAction $action): ArtistResourceResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/ArtistSongController.php b/app/Http/Controllers/Api/Pivot/Wiki/ArtistSongController.php index f529fccd0..c1dea2a0a 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/ArtistSongController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/ArtistSongController.php @@ -57,7 +57,7 @@ class ArtistSongController extends PivotController * @param StoreRequest $request * @param Artist $artist * @param Song $song - * @param StoreAction $action + * @param StoreAction $action * @return ArtistSongResource */ public function store(StoreRequest $request, Artist $artist, Song $song, StoreAction $action): ArtistSongResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/SongResourceController.php b/app/Http/Controllers/Api/Pivot/Wiki/SongResourceController.php index d57bc686a..943ce2325 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/SongResourceController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/SongResourceController.php @@ -57,7 +57,7 @@ class SongResourceController extends PivotController * @param StoreRequest $request * @param Song $song * @param ExternalResource $resource - * @param StoreAction $action + * @param StoreAction $action * @return SongResourceResource */ public function store(StoreRequest $request, Song $song, ExternalResource $resource, StoreAction $action): SongResourceResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/StudioImageController.php b/app/Http/Controllers/Api/Pivot/Wiki/StudioImageController.php index aecc3c628..72eead63d 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/StudioImageController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/StudioImageController.php @@ -55,7 +55,7 @@ class StudioImageController extends PivotController * @param StoreRequest $request * @param Studio $studio * @param Image $image - * @param StoreAction $action + * @param StoreAction $action * @return StudioImageResource */ public function store(StoreRequest $request, Studio $studio, Image $image, StoreAction $action): StudioImageResource diff --git a/app/Http/Controllers/Api/Pivot/Wiki/StudioResourceController.php b/app/Http/Controllers/Api/Pivot/Wiki/StudioResourceController.php index 0f27b43d7..42c4d263f 100644 --- a/app/Http/Controllers/Api/Pivot/Wiki/StudioResourceController.php +++ b/app/Http/Controllers/Api/Pivot/Wiki/StudioResourceController.php @@ -57,7 +57,7 @@ class StudioResourceController extends PivotController * @param StoreRequest $request * @param Studio $studio * @param ExternalResource $resource - * @param StoreAction $action + * @param StoreAction $action * @return StudioResourceResource */ public function store(StoreRequest $request, Studio $studio, ExternalResource $resource, StoreAction $action): StudioResourceResource diff --git a/app/Http/Controllers/Api/Wiki/Anime/SynonymController.php b/app/Http/Controllers/Api/Wiki/Anime/SynonymController.php index c1e7fd401..51bd5471a 100644 --- a/app/Http/Controllers/Api/Wiki/Anime/SynonymController.php +++ b/app/Http/Controllers/Api/Wiki/Anime/SynonymController.php @@ -57,7 +57,7 @@ class SynonymController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return SynonymResource */ public function store(StoreRequest $request, StoreAction $action): SynonymResource diff --git a/app/Http/Controllers/Api/Wiki/Anime/Theme/EntryController.php b/app/Http/Controllers/Api/Wiki/Anime/Theme/EntryController.php index 656468419..697a4ee5c 100644 --- a/app/Http/Controllers/Api/Wiki/Anime/Theme/EntryController.php +++ b/app/Http/Controllers/Api/Wiki/Anime/Theme/EntryController.php @@ -57,7 +57,7 @@ class EntryController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return EntryResource */ public function store(StoreRequest $request, StoreAction $action): EntryResource diff --git a/app/Http/Controllers/Api/Wiki/Anime/ThemeController.php b/app/Http/Controllers/Api/Wiki/Anime/ThemeController.php index c4755e1c6..f970964aa 100644 --- a/app/Http/Controllers/Api/Wiki/Anime/ThemeController.php +++ b/app/Http/Controllers/Api/Wiki/Anime/ThemeController.php @@ -57,7 +57,7 @@ class ThemeController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return ThemeResource */ public function store(StoreRequest $request, StoreAction $action): ThemeResource diff --git a/app/Http/Controllers/Api/Wiki/AnimeController.php b/app/Http/Controllers/Api/Wiki/AnimeController.php index fb60ccff3..6a26aa07a 100644 --- a/app/Http/Controllers/Api/Wiki/AnimeController.php +++ b/app/Http/Controllers/Api/Wiki/AnimeController.php @@ -57,7 +57,7 @@ class AnimeController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return AnimeResource */ public function store(StoreRequest $request, StoreAction $action): AnimeResource diff --git a/app/Http/Controllers/Api/Wiki/ArtistController.php b/app/Http/Controllers/Api/Wiki/ArtistController.php index 918e3ca49..b43e29d77 100644 --- a/app/Http/Controllers/Api/Wiki/ArtistController.php +++ b/app/Http/Controllers/Api/Wiki/ArtistController.php @@ -57,7 +57,7 @@ class ArtistController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction $action * @return ArtistResource */ public function store(StoreRequest $request, StoreAction $action): ArtistResource diff --git a/app/Http/Controllers/Api/Wiki/AudioController.php b/app/Http/Controllers/Api/Wiki/AudioController.php index 60d239826..7a81c147e 100644 --- a/app/Http/Controllers/Api/Wiki/AudioController.php +++ b/app/Http/Controllers/Api/Wiki/AudioController.php @@ -55,7 +55,7 @@ class AudioController extends BaseController * Store a newly created resource. * * @param StoreRequest $request - * @param StoreAction $action + * @param StoreAction