diff --git a/app/Actions/Models/User/Submission/Anime/ManageAnimeSubmissionAction.php b/app/Actions/Models/User/Submission/Anime/ManageAnimeSubmissionAction.php index 91e371a5f..2b0c045a7 100644 --- a/app/Actions/Models/User/Submission/Anime/ManageAnimeSubmissionAction.php +++ b/app/Actions/Models/User/Submission/Anime/ManageAnimeSubmissionAction.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\Actions\Models\User\Submission\Anime; use App\Actions\Models\User\Submission\SubmissionAction; -use App\Concerns\Models\CanCreateAnimeSynonym; +use App\Concerns\Models\CanCreateSynonym; use App\Filament\Resources\Wiki\Song\RelationManagers\PerformanceSongRelationManager; use App\Models\User\Submission\SubmissionStage; use App\Models\Wiki\Anime; @@ -16,7 +16,7 @@ use Illuminate\Support\Facades\DB; class ManageAnimeSubmissionAction extends SubmissionAction { - use CanCreateAnimeSynonym; + use CanCreateSynonym; /** * Approve the submission stage. @@ -47,7 +47,7 @@ class ManageAnimeSubmissionAction extends SubmissionAction protected function createSynonyms(array $synonyms, Anime $anime): void { foreach ($synonyms as $synonym) { - $this->createAnimeSynonym( + $this->createSynonym( Arr::get($synonym, 'text'), Arr::get($synonym, 'type'), $anime diff --git a/app/Actions/Models/Wiki/BackfillAnimeAction.php b/app/Actions/Models/Wiki/BackfillAnimeAction.php index 51c5368a5..526dc876a 100644 --- a/app/Actions/Models/Wiki/BackfillAnimeAction.php +++ b/app/Actions/Models/Wiki/BackfillAnimeAction.php @@ -11,14 +11,15 @@ use App\Actions\Models\Wiki\Anime\ExternalApi\AniZipAnimeExternalApiAction; use App\Actions\Models\Wiki\Anime\ExternalApi\JikanAnimeExternalApiAction; use App\Actions\Models\Wiki\Anime\ExternalApi\LivechartAnimeExternalApiAction; use App\Actions\Models\Wiki\Anime\ExternalApi\MalAnimeExternalApiAction; -use App\Concerns\Models\CanCreateAnimeSynonym; use App\Concerns\Models\CanCreateStudio; +use App\Concerns\Models\CanCreateSynonym; +use App\Concerns\Models\HasLabel; use App\Contracts\Actions\Models\Wiki\BackfillImages; use App\Contracts\Actions\Models\Wiki\BackfillResources; use App\Contracts\Actions\Models\Wiki\BackfillStudios; use App\Contracts\Actions\Models\Wiki\BackfillSynonyms; use App\Enums\Actions\ActionStatus; -use App\Enums\Models\Wiki\AnimeSynonymType; +use App\Enums\Models\Wiki\SynonymType; use App\Models\Wiki\Anime; use Exception; use Illuminate\Support\Arr; @@ -27,8 +28,9 @@ use Illuminate\Support\Facades\Log; class BackfillAnimeAction extends BackfillWikiAction { - use CanCreateAnimeSynonym; use CanCreateStudio; + use CanCreateSynonym; + use HasLabel; final public const string STUDIOS = 'studios'; final public const string SYNONYMS = 'synonyms'; @@ -147,16 +149,16 @@ class BackfillAnimeAction extends BackfillWikiAction $texts = []; foreach ($api->getSynonyms() as $type => $text) { - if ($type === AnimeSynonymType::OTHER->value && in_array($text, $texts)) { - Log::info("Skipping duplicate synonym '$text' for Anime {$this->getModel()->getName()}"); + if ($type === SynonymType::OTHER->value && in_array($text, $texts)) { + Log::info("Skipping duplicate synonym '$text' for {$this->privateLabel($this->getModel())} {$this->getModel()->getName()}"); continue; } - $this->createAnimeSynonym($text, $type, $this->getModel()); + $this->createSynonym($text, $type, $this->getModel()); $texts[] = $text; } - if ($this->getModel()->animesynonyms()->exists()) { + if ($this->getModel()->synonyms()->exists()) { $this->toBackfill[self::SYNONYMS] = false; } } diff --git a/app/Concerns/Models/CanCreateAnimeSynonym.php b/app/Concerns/Models/CanCreateAnimeSynonym.php deleted file mode 100644 index 987bc319b..000000000 --- a/app/Concerns/Models/CanCreateAnimeSynonym.php +++ /dev/null @@ -1,31 +0,0 @@ -value && Str::is($text, $anime->getName(), true)) - ) { - return; - } - - Log::info("Creating {$text} for Anime {$anime->getName()}"); - - $anime->animesynonyms()->create([ - AnimeSynonym::ATTRIBUTE_TEXT => $text, - AnimeSynonym::ATTRIBUTE_TYPE => $type, - ]); - } -} diff --git a/app/Concerns/Models/CanCreateSynonym.php b/app/Concerns/Models/CanCreateSynonym.php new file mode 100644 index 000000000..f070aabe3 --- /dev/null +++ b/app/Concerns/Models/CanCreateSynonym.php @@ -0,0 +1,35 @@ +value && Str::is($text, $synonymable->getName(), true)) + ) { + return; + } + + Log::info("Creating {$text} for {$this->privateLabel($synonymable)} {$synonymable->getName()}"); + + $synonymable->synonyms()->create([ + Synonym::ATTRIBUTE_TEXT => $text, + Synonym::ATTRIBUTE_TYPE => $type, + ]); + } +} diff --git a/app/Contracts/Events/UpdateAnimeSynonymsEvent.php b/app/Contracts/Events/UpdateAnimeSynonymsEvent.php new file mode 100644 index 000000000..e59ce9203 --- /dev/null +++ b/app/Contracts/Events/UpdateAnimeSynonymsEvent.php @@ -0,0 +1,12 @@ + + */ + public function synonyms(): MorphMany; +} diff --git a/app/Enums/Models/Wiki/AnimeSynonymType.php b/app/Enums/Models/Wiki/AnimeSynonymType.php index f3c4abdac..d420d1665 100644 --- a/app/Enums/Models/Wiki/AnimeSynonymType.php +++ b/app/Enums/Models/Wiki/AnimeSynonymType.php @@ -6,7 +6,12 @@ namespace App\Enums\Models\Wiki; use App\Concerns\Enums\LocalizesName; use Filament\Support\Contracts\HasLabel; +use GraphQL\Type\Definition\Deprecated as GraphQLDeprecated; +/** + * @deprecated Use SynonymType instead. + */ +#[GraphQLDeprecated('Use SynonymType instead.')] enum AnimeSynonymType: int implements HasLabel { use LocalizesName; diff --git a/app/Enums/Models/Wiki/SynonymType.php b/app/Enums/Models/Wiki/SynonymType.php new file mode 100644 index 000000000..48e0db715 --- /dev/null +++ b/app/Enums/Models/Wiki/SynonymType.php @@ -0,0 +1,18 @@ +discordthread?->delete(); - $anime->animesynonyms->each(function (AnimeSynonym $synonym): void { - AnimeSynonym::withoutEvents(function () use ($synonym): void { + $anime->synonyms->each(function (Synonym $synonym): void { + Synonym::withoutEvents(function () use ($synonym): void { $synonym->unsearchable(); $synonym->delete(); }); diff --git a/app/Events/Wiki/Anime/AnimeRestored.php b/app/Events/Wiki/Anime/AnimeRestored.php index c4390b676..5b7b2f5fd 100644 --- a/app/Events/Wiki/Anime/AnimeRestored.php +++ b/app/Events/Wiki/Anime/AnimeRestored.php @@ -7,9 +7,9 @@ namespace App\Events\Wiki\Anime; use App\Contracts\Events\CascadesRestoresEvent; use App\Events\Base\Wiki\WikiRestoredEvent; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; use App\Models\Wiki\Anime\AnimeTheme; use App\Models\Wiki\Anime\Theme\AnimeThemeEntry; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -27,8 +27,8 @@ class AnimeRestored extends WikiRestoredEvent implements CascadesRestoresEvent { $anime = $this->getModel(); - $anime->animesynonyms()->withoutGlobalScope(SoftDeletingScope::class)->get()->each(function (AnimeSynonym $synonym): void { - AnimeSynonym::withoutEvents(function () use ($synonym): void { + $anime->synonyms()->withoutGlobalScope(SoftDeletingScope::class)->get()->each(function (Synonym $synonym): void { + Synonym::withoutEvents(function () use ($synonym): void { $synonym->restore(); $synonym->searchable(); }); diff --git a/app/Events/Wiki/Anime/Synonym/SynonymCreated.php b/app/Events/Wiki/Anime/Synonym/SynonymCreated.php deleted file mode 100644 index 7a2c0f26a..000000000 --- a/app/Events/Wiki/Anime/Synonym/SynonymCreated.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class SynonymCreated extends WikiCreatedEvent implements UpdateRelatedIndicesEvent -{ - protected function getDiscordMessageDescription(): string - { - return "Synonym '**{$this->getModel()->getName()}**' has been created for Anime '**{$this->getModel()->anime->getName()}**'."; - } - - public function updateRelatedIndices(): void - { - $synonym = $this->getModel()->load([ - AnimeSynonym::RELATION_SERIES, - AnimeSynonym::RELATION_VIDEOS, - ]); - - $synonym->anime->searchable(); - $synonym->anime->series->each(fn (Series $series) => $series->searchable()); - $synonym->anime->animethemes->each(function (AnimeTheme $theme): void { - $theme->searchable(); - $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { - $entry->searchable(); - $entry->videos->each(fn (Video $video) => $video->searchable()); - }); - }); - } -} diff --git a/app/Events/Wiki/Anime/Synonym/SynonymDeleted.php b/app/Events/Wiki/Anime/Synonym/SynonymDeleted.php deleted file mode 100644 index 9c400d493..000000000 --- a/app/Events/Wiki/Anime/Synonym/SynonymDeleted.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ -class SynonymDeleted extends WikiDeletedEvent implements UpdateRelatedIndicesEvent -{ - protected function getDiscordMessageDescription(): string - { - return "Synonym '**{$this->getModel()->getName()}**' has been deleted for Anime '**{$this->getModel()->anime->getName()}**'."; - } - - protected function getNotificationMessage(): string - { - return "Synonym '{$this->getModel()->getName()}' has been deleted for Anime '{$this->getModel()->anime->getName()}'. It will be automatically pruned in one week. Please review."; - } - - protected function getFilamentNotificationUrl(): string - { - return SynonymFilament::getUrl('view', ['record' => $this->getModel()]); - } - - public function updateRelatedIndices(): void - { - $anime = $this->getModel()->anime->load([ - Anime::RELATION_SERIES, - Anime::RELATION_VIDEOS, - ]); - - $anime->searchable(); - $anime->series->each(fn (Series $series) => $series->searchable()); - $anime->animethemes->each(function (AnimeTheme $theme): void { - $theme->searchable(); - $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { - $entry->searchable(); - $entry->videos->each(fn (Video $video) => $video->searchable()); - }); - }); - } -} diff --git a/app/Events/Wiki/Anime/Synonym/SynonymRestored.php b/app/Events/Wiki/Anime/Synonym/SynonymRestored.php deleted file mode 100644 index 7c8e1feae..000000000 --- a/app/Events/Wiki/Anime/Synonym/SynonymRestored.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class SynonymRestored extends WikiRestoredEvent implements UpdateRelatedIndicesEvent -{ - protected function getDiscordMessageDescription(): string - { - return "Synonym '**{$this->getModel()->getName()}**' has been restored for Anime '**{$this->getModel()->anime->getName()}**'."; - } - - public function updateRelatedIndices(): void - { - $synonym = $this->getModel()->load([ - AnimeSynonym::RELATION_SERIES, - AnimeSynonym::RELATION_VIDEOS, - ]); - - $synonym->anime->searchable(); - $synonym->anime->series->each(fn (Series $series) => $series->searchable()); - $synonym->anime->animethemes->each(function (AnimeTheme $theme): void { - $theme->searchable(); - $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { - $entry->searchable(); - $entry->videos->each(fn (Video $video) => $video->searchable()); - }); - }); - } -} diff --git a/app/Events/Wiki/Anime/Synonym/SynonymUpdated.php b/app/Events/Wiki/Anime/Synonym/SynonymUpdated.php deleted file mode 100644 index 13564e2db..000000000 --- a/app/Events/Wiki/Anime/Synonym/SynonymUpdated.php +++ /dev/null @@ -1,48 +0,0 @@ - - */ -class SynonymUpdated extends WikiUpdatedEvent implements UpdateRelatedIndicesEvent -{ - public function __construct(AnimeSynonym $synonym) - { - parent::__construct($synonym); - $this->initializeEmbedFields($synonym); - } - - protected function getDiscordMessageDescription(): string - { - return "Synonym '**{$this->getModel()->getName()}**' has been updated for Anime '**{$this->getModel()->anime->getName()}**'."; - } - - public function updateRelatedIndices(): void - { - $synonym = $this->getModel()->load([ - AnimeSynonym::RELATION_SERIES, - AnimeSynonym::RELATION_VIDEOS, - ]); - - $synonym->anime->searchable(); - $synonym->anime->series->each(fn (Series $series) => $series->searchable()); - $synonym->anime->animethemes->each(function (AnimeTheme $theme): void { - $theme->searchable(); - $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { - $entry->searchable(); - $entry->videos->each(fn (Video $video) => $video->searchable()); - }); - }); - } -} diff --git a/app/Events/Wiki/Synonym/SynonymCreated.php b/app/Events/Wiki/Synonym/SynonymCreated.php new file mode 100644 index 000000000..13ed6b066 --- /dev/null +++ b/app/Events/Wiki/Synonym/SynonymCreated.php @@ -0,0 +1,66 @@ + + */ +class SynonymCreated extends WikiCreatedEvent implements UpdateAnimeSynonymsEvent, UpdateRelatedIndicesEvent +{ + use HasLabel; + + protected function getDiscordMessageDescription(): string + { + return "Synonym '**{$this->getModel()->getName()}**' has been created for {$this->privateLabel($this->getModel()->synonymable)} '**{$this->getModel()->synonymable->getName()}**'."; + } + + public function updateRelatedIndices(): void + { + $synonym = $this->getModel()->load([ + Synonym::RELATION_SYNONYMABLE => fn (MorphTo $morphTo): MorphTo => $morphTo->morphWith([ + Anime::class => [ + Anime::RELATION_SERIES, + Anime::RELATION_VIDEOS, + ], + ]), + ]); + + if ($synonym->synonymable instanceof Anime) { + $synonym->synonymable->searchable(); + $synonym->synonymable->series->each(fn (Series $series) => $series->searchable()); + $synonym->synonymable->animethemes->each(function (AnimeTheme $theme): void { + $theme->searchable(); + $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { + $entry->searchable(); + $entry->videos->each(fn (Video $video) => $video->searchable()); + }); + }); + } + } + + public function updateAnimeSynonyms(): void + { + if ($this->getModel()->synonymable instanceof Anime) { + AnimeSynonym::query()->create([ + AnimeSynonym::ATTRIBUTE_ANIME => $this->getModel()->synonymable_id, + AnimeSynonym::ATTRIBUTE_TEXT => $this->getModel()->text, + AnimeSynonym::ATTRIBUTE_TYPE => $this->getModel()->type->value, + ]); + } + } +} diff --git a/app/Events/Wiki/Synonym/SynonymDeleted.php b/app/Events/Wiki/Synonym/SynonymDeleted.php new file mode 100644 index 000000000..c32804500 --- /dev/null +++ b/app/Events/Wiki/Synonym/SynonymDeleted.php @@ -0,0 +1,76 @@ + + */ +class SynonymDeleted extends WikiDeletedEvent implements UpdateAnimeSynonymsEvent, UpdateRelatedIndicesEvent +{ + use HasLabel; + + protected function getDiscordMessageDescription(): string + { + return "Synonym '**{$this->getModel()->getName()}**' has been deleted for {$this->privateLabel($this->getModel()->synonymable)} '**{$this->getModel()->synonymable->getName()}**'."; + } + + protected function getNotificationMessage(): string + { + return "Synonym '{$this->getModel()->getName()}' has been deleted for {$this->privateLabel($this->getModel()->synonymable)} '{$this->getModel()->synonymable->getName()}'. It will be automatically pruned in one week. Please review."; + } + + protected function getFilamentNotificationUrl(): string + { + return SynonymFilament::getUrl('view', ['record' => $this->getModel()]); + } + + public function updateRelatedIndices(): void + { + $synonym = $this->getModel()->load([ + Synonym::RELATION_SYNONYMABLE => fn (MorphTo $morphTo): MorphTo => $morphTo->morphWith([ + Anime::class => [ + Anime::RELATION_SERIES, + Anime::RELATION_VIDEOS, + ], + ]), + ]); + + if ($synonym->synonymable instanceof Anime) { + $synonym->synonymable->searchable(); + $synonym->synonymable->series->each(fn (Series $series) => $series->searchable()); + $synonym->synonymable->animethemes->each(function (AnimeTheme $theme): void { + $theme->searchable(); + $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { + $entry->searchable(); + $entry->videos->each(fn (Video $video) => $video->searchable()); + }); + }); + } + } + + public function updateAnimeSynonyms(): void + { + if ($this->getModel()->synonymable instanceof Anime) { + AnimeSynonym::query() + ->where(AnimeSynonym::ATTRIBUTE_ANIME, $this->getModel()->synonymable_id) + ->where(AnimeSynonym::ATTRIBUTE_TEXT, $this->getModel()->text) + ->{$this->getModel()->isForceDeleting() ? 'forceDelete' : 'delete'}(); + } + } +} diff --git a/app/Events/Wiki/Synonym/SynonymRestored.php b/app/Events/Wiki/Synonym/SynonymRestored.php new file mode 100644 index 000000000..aa259da51 --- /dev/null +++ b/app/Events/Wiki/Synonym/SynonymRestored.php @@ -0,0 +1,67 @@ + + */ +class SynonymRestored extends WikiRestoredEvent implements UpdateAnimeSynonymsEvent, UpdateRelatedIndicesEvent +{ + use HasLabel; + + protected function getDiscordMessageDescription(): string + { + return "Synonym '**{$this->getModel()->getName()}**' has been restored for {$this->privateLabel($this->getModel()->synonymable)} '**{$this->getModel()->synonymable->getName()}**'."; + } + + public function updateRelatedIndices(): void + { + $synonym = $this->getModel()->load([ + Synonym::RELATION_SYNONYMABLE => fn (MorphTo $morphTo): MorphTo => $morphTo->morphWith([ + Anime::class => [ + Anime::RELATION_SERIES, + Anime::RELATION_VIDEOS, + ], + ]), + ]); + + if ($synonym->synonymable instanceof Anime) { + $synonym->synonymable->searchable(); + $synonym->synonymable->series->each(fn (Series $series) => $series->searchable()); + $synonym->synonymable->animethemes->each(function (AnimeTheme $theme): void { + $theme->searchable(); + $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { + $entry->searchable(); + $entry->videos->each(fn (Video $video) => $video->searchable()); + }); + }); + } + } + + public function updateAnimeSynonyms(): void + { + if ($this->getModel()->synonymable instanceof Anime) { + AnimeSynonym::query() + ->withTrashed() + ->where(AnimeSynonym::ATTRIBUTE_ANIME, $this->getModel()->synonymable_id) + ->where(AnimeSynonym::ATTRIBUTE_TEXT, $this->getModel()->text) + ->first() + ?->restore(); + } + } +} diff --git a/app/Events/Wiki/Synonym/SynonymUpdated.php b/app/Events/Wiki/Synonym/SynonymUpdated.php new file mode 100644 index 000000000..45670861d --- /dev/null +++ b/app/Events/Wiki/Synonym/SynonymUpdated.php @@ -0,0 +1,74 @@ + + */ +class SynonymUpdated extends WikiUpdatedEvent implements UpdateAnimeSynonymsEvent, UpdateRelatedIndicesEvent +{ + use HasLabel; + + public function __construct(Synonym $synonym) + { + parent::__construct($synonym); + $this->initializeEmbedFields($synonym); + } + + protected function getDiscordMessageDescription(): string + { + return "Synonym '**{$this->getModel()->getName()}**' has been updated for {$this->privateLabel($this->getModel()->synonymable)} '**{$this->getModel()->synonymable->getName()}**'."; + } + + public function updateRelatedIndices(): void + { + $synonym = $this->getModel()->load([ + Synonym::RELATION_SYNONYMABLE => fn (MorphTo $morphTo): MorphTo => $morphTo->morphWith([ + Anime::class => [ + Anime::RELATION_SERIES, + Anime::RELATION_VIDEOS, + ], + ]), + ]); + + if ($synonym->synonymable instanceof Anime) { + $synonym->synonymable->searchable(); + $synonym->synonymable->series->each(fn (Series $series) => $series->searchable()); + $synonym->synonymable->animethemes->each(function (AnimeTheme $theme): void { + $theme->searchable(); + $theme->animethemeentries->each(function (AnimeThemeEntry $entry): void { + $entry->searchable(); + $entry->videos->each(fn (Video $video) => $video->searchable()); + }); + }); + } + } + + public function updateAnimeSynonyms(): void + { + if ($this->getModel()->synonymable instanceof Anime) { + AnimeSynonym::query() + ->where(AnimeSynonym::ATTRIBUTE_ANIME, $this->getModel()->getOriginal(Synonym::ATTRIBUTE_SYNONYMABLE_ID)) + ->where(AnimeSynonym::ATTRIBUTE_TEXT, $this->getModel()->getOriginal(Synonym::ATTRIBUTE_TEXT)) + ->update([ + AnimeSynonym::ATTRIBUTE_TEXT => $this->getModel()->text, + AnimeSynonym::ATTRIBUTE_TYPE => $this->getModel()->type->value, + ]); + } + } +} diff --git a/app/Filament/Actions/Models/Wiki/Anime/BackfillAnimeAction.php b/app/Filament/Actions/Models/Wiki/Anime/BackfillAnimeAction.php index 92802ba11..c6afe4ffe 100644 --- a/app/Filament/Actions/Models/Wiki/Anime/BackfillAnimeAction.php +++ b/app/Filament/Actions/Models/Wiki/Anime/BackfillAnimeAction.php @@ -97,7 +97,7 @@ class BackfillAnimeAction extends BaseAction return $schema; } - $anime = $anime->load([Anime::RELATION_RESOURCES, Anime::RELATION_IMAGES, Anime::RELATION_STUDIOS, Anime::RELATION_SYNONYMS]); + $anime = $anime->load([Anime::RELATION_RESOURCES, Anime::RELATION_IMAGES, Anime::RELATION_STUDIOS, Anime::RELATION_ANIMESYNONYMS]); $resources = $anime->resources->pluck(ExternalResource::ATTRIBUTE_SITE)->keyBy(fn (ResourceSite $site) => $site->value)->keys(); $images = $anime->images->pluck(Image::ATTRIBUTE_FACET)->keyBy(fn (ImageFacet $facet) => $facet->value)->keys(); @@ -173,7 +173,7 @@ class BackfillAnimeAction extends BaseAction Checkbox::make(self::BACKFILL_SYNONYMS) ->label(__('filament.actions.anime.backfill.fields.synonyms.name')) ->helperText(__('filament.actions.anime.backfill.fields.synonyms.help')) - ->default(fn () => $anime->animesynonyms->isEmpty()), + ->default(fn () => $anime->synonyms->isEmpty()), ]), ]); } diff --git a/app/Filament/RelationManagers/Wiki/Anime/SynonymRelationManager.php b/app/Filament/RelationManagers/Wiki/SynonymRelationManager.php similarity index 63% rename from app/Filament/RelationManagers/Wiki/Anime/SynonymRelationManager.php rename to app/Filament/RelationManagers/Wiki/SynonymRelationManager.php index 092d0912b..632f210e7 100644 --- a/app/Filament/RelationManagers/Wiki/Anime/SynonymRelationManager.php +++ b/app/Filament/RelationManagers/Wiki/SynonymRelationManager.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace App\Filament\RelationManagers\Wiki\Anime; +namespace App\Filament\RelationManagers\Wiki; use App\Filament\RelationManagers\BaseRelationManager; use App\Filament\Resources\BaseResource; -use App\Filament\Resources\Wiki\Anime\SynonymResource; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Filament\Resources\Wiki\SynonymResource; +use App\Models\Wiki\Synonym; use Filament\Tables\Table; abstract class SynonymRelationManager extends BaseRelationManager @@ -23,8 +23,8 @@ abstract class SynonymRelationManager extends BaseRelationManager { return parent::table( $table - ->recordTitleAttribute(AnimeSynonym::ATTRIBUTE_TEXT) - ->defaultSort(AnimeSynonym::TABLE.'.'.AnimeSynonym::ATTRIBUTE_ID, 'desc') + ->recordTitleAttribute(Synonym::ATTRIBUTE_TEXT) + ->defaultSort(Synonym::TABLE.'.'.Synonym::ATTRIBUTE_ID, 'desc') ); } } diff --git a/app/Filament/Resources/List/PlaylistResource.php b/app/Filament/Resources/List/PlaylistResource.php index b97fee2d6..cba253b11 100644 --- a/app/Filament/Resources/List/PlaylistResource.php +++ b/app/Filament/Resources/List/PlaylistResource.php @@ -140,7 +140,7 @@ class PlaylistResource extends BaseResource TextColumn::make(Playlist::ATTRIBUTE_NAME) ->label(__('filament.fields.playlist.name.name')) ->limit(40) - ->tooltip(fn (TextColumn $column): mixed => $column->getState()) + ->tooltip(fn (string $state): string => $state) ->copyableWithMessage(), TextColumn::make(Playlist::ATTRIBUTE_VISIBILITY) diff --git a/app/Filament/Resources/Wiki/Anime/RelationManagers/SynonymAnimeRelationManager.php b/app/Filament/Resources/Wiki/Anime/RelationManagers/SynonymAnimeRelationManager.php index 006d5fa75..25bd3a2a6 100644 --- a/app/Filament/Resources/Wiki/Anime/RelationManagers/SynonymAnimeRelationManager.php +++ b/app/Filament/Resources/Wiki/Anime/RelationManagers/SynonymAnimeRelationManager.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace App\Filament\Resources\Wiki\Anime\RelationManagers; -use App\Filament\RelationManagers\Wiki\Anime\SynonymRelationManager; +use App\Filament\RelationManagers\Wiki\SynonymRelationManager; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Models\Wiki\Synonym; use Filament\Tables\Table; class SynonymAnimeRelationManager extends SynonymRelationManager @@ -20,7 +20,7 @@ class SynonymAnimeRelationManager extends SynonymRelationManager { return parent::table( $table - ->inverseRelationship(AnimeSynonym::RELATION_ANIME) + ->inverseRelationship(Synonym::RELATION_SYNONYMABLE) ); } } diff --git a/app/Filament/Resources/Wiki/Anime/Theme/EntryResource.php b/app/Filament/Resources/Wiki/Anime/Theme/EntryResource.php index 19879cb1b..745dc438b 100644 --- a/app/Filament/Resources/Wiki/Anime/Theme/EntryResource.php +++ b/app/Filament/Resources/Wiki/Anime/Theme/EntryResource.php @@ -206,7 +206,7 @@ class EntryResource extends BaseResource TextColumn::make(AnimeThemeEntry::ATTRIBUTE_NOTES) ->label(__('filament.fields.anime_theme_entry.notes.name')) ->limit(50) - ->tooltip(fn (TextColumn $column): mixed => $column->getState()), + ->tooltip(fn (string $state): string => $state), BelongsToColumn::make(AnimeThemeEntry::RELATION_SONG_SHALLOW, SongResource::class) ->hiddenOn(EntryThemeRelationManager::class) diff --git a/app/Filament/Resources/Wiki/AnimeResource.php b/app/Filament/Resources/Wiki/AnimeResource.php index b46a7c2b9..1b758cc35 100644 --- a/app/Filament/Resources/Wiki/AnimeResource.php +++ b/app/Filament/Resources/Wiki/AnimeResource.php @@ -146,12 +146,12 @@ class AnimeResource extends BaseResource ->label(__('filament.fields.anime.name.name')) ->copyableWithMessage() ->limit(50) - ->tooltip(fn (TextColumn $column): mixed => $column->getState()), + ->tooltip(fn (string $state): string => $state), TextColumn::make(Anime::ATTRIBUTE_SLUG) ->label(__('filament.fields.anime.slug.name')) ->limit(20) - ->tooltip(fn (TextColumn $column): mixed => $column->getState()), + ->tooltip(fn (string $state): string => $state), TextColumn::make(Anime::ATTRIBUTE_YEAR) ->label(__('filament.fields.anime.year.name')), diff --git a/app/Filament/Resources/Wiki/Anime/Synonym/Pages/ListSynonyms.php b/app/Filament/Resources/Wiki/Synonym/Pages/ListSynonyms.php similarity index 63% rename from app/Filament/Resources/Wiki/Anime/Synonym/Pages/ListSynonyms.php rename to app/Filament/Resources/Wiki/Synonym/Pages/ListSynonyms.php index ce8e99463..8405bcd87 100644 --- a/app/Filament/Resources/Wiki/Anime/Synonym/Pages/ListSynonyms.php +++ b/app/Filament/Resources/Wiki/Synonym/Pages/ListSynonyms.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace App\Filament\Resources\Wiki\Anime\Synonym\Pages; +namespace App\Filament\Resources\Wiki\Synonym\Pages; use App\Filament\Resources\Base\BaseListResources; -use App\Filament\Resources\Wiki\Anime\SynonymResource; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Filament\Resources\Wiki\SynonymResource; +use App\Models\Wiki\Synonym; use Illuminate\Database\Eloquent\Builder; class ListSynonyms extends BaseListResources @@ -18,6 +18,6 @@ class ListSynonyms extends BaseListResources */ protected function applySearchToTableQuery(Builder $query): Builder { - return $this->makeScout($query, AnimeSynonym::class); + return $this->makeScout($query, Synonym::class); } } diff --git a/app/Filament/Resources/Wiki/Anime/Synonym/Pages/ViewSynonym.php b/app/Filament/Resources/Wiki/Synonym/Pages/ViewSynonym.php similarity index 63% rename from app/Filament/Resources/Wiki/Anime/Synonym/Pages/ViewSynonym.php rename to app/Filament/Resources/Wiki/Synonym/Pages/ViewSynonym.php index 8ac5731dd..85c83d665 100644 --- a/app/Filament/Resources/Wiki/Anime/Synonym/Pages/ViewSynonym.php +++ b/app/Filament/Resources/Wiki/Synonym/Pages/ViewSynonym.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace App\Filament\Resources\Wiki\Anime\Synonym\Pages; +namespace App\Filament\Resources\Wiki\Synonym\Pages; use App\Filament\Resources\Base\BaseViewResource; -use App\Filament\Resources\Wiki\Anime\SynonymResource; +use App\Filament\Resources\Wiki\SynonymResource; class ViewSynonym extends BaseViewResource { diff --git a/app/Filament/Resources/Wiki/Anime/SynonymResource.php b/app/Filament/Resources/Wiki/SynonymResource.php similarity index 52% rename from app/Filament/Resources/Wiki/Anime/SynonymResource.php rename to app/Filament/Resources/Wiki/SynonymResource.php index 65fc217c2..5dde9038a 100644 --- a/app/Filament/Resources/Wiki/Anime/SynonymResource.php +++ b/app/Filament/Resources/Wiki/SynonymResource.php @@ -2,31 +2,27 @@ declare(strict_types=1); -namespace App\Filament\Resources\Wiki\Anime; +namespace App\Filament\Resources\Wiki; use App\Enums\Filament\NavigationGroup; -use App\Enums\Models\Wiki\AnimeSynonymType; -use App\Filament\Components\Columns\BelongsToColumn; +use App\Enums\Models\Wiki\SynonymType; use App\Filament\Components\Columns\TextColumn; -use App\Filament\Components\Fields\BelongsTo; use App\Filament\Components\Fields\Select; use App\Filament\Components\Fields\TextInput; -use App\Filament\Components\Infolist\BelongsToEntry; use App\Filament\Components\Infolist\TextEntry; use App\Filament\Components\Infolist\TimestampSection; -use App\Filament\RelationManagers\Wiki\Anime\SynonymRelationManager; use App\Filament\Resources\BaseResource; use App\Filament\Resources\Wiki\Anime\RelationManagers\SynonymAnimeRelationManager; -use App\Filament\Resources\Wiki\Anime\Synonym\Pages\ListSynonyms; -use App\Filament\Resources\Wiki\Anime\Synonym\Pages\ViewSynonym; -use App\Filament\Resources\Wiki\AnimeResource; -use App\Filament\Submission\Resources\Anime\Pages\CreateAnimeSubmission; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Filament\Resources\Wiki\Synonym\Pages\ListSynonyms; +use App\Filament\Resources\Wiki\Synonym\Pages\ViewSynonym; +use App\Models\Wiki\Synonym; +use Filament\Facades\Filament; use Filament\QueryBuilder\Constraints\SelectConstraint; use Filament\QueryBuilder\Constraints\TextConstraint; use Filament\Resources\RelationManagers\RelationGroup; use Filament\Schemas\Components\Section; use Filament\Schemas\Schema; +use Filament\Support\Enums\FontWeight; use Filament\Support\Icons\Heroicon; use Filament\Tables\Filters\QueryBuilder; use Filament\Tables\Table; @@ -40,16 +36,16 @@ class SynonymResource extends BaseResource * * @var class-string|null */ - protected static ?string $model = AnimeSynonym::class; + protected static ?string $model = Synonym::class; public static function getModelLabel(): string { - return __('filament.resources.singularLabel.anime_synonym'); + return __('filament.resources.singularLabel.synonym'); } public static function getPluralModelLabel(): string { - return __('filament.resources.label.anime_synonyms'); + return __('filament.resources.label.synonyms'); } public static function getNavigationGroup(): NavigationGroup @@ -64,12 +60,12 @@ class SynonymResource extends BaseResource public static function getRecordSlug(): string { - return 'anime-synonyms'; + return 'synonyms'; } public static function getRecordTitleAttribute(): string { - return AnimeSynonym::ATTRIBUTE_TEXT; + return Synonym::ATTRIBUTE_TEXT; } public static function getEloquentQuery(): Builder @@ -77,26 +73,22 @@ class SynonymResource extends BaseResource $query = parent::getEloquentQuery(); // Necessary to prevent lazy loading when loading related resources - return $query->with([AnimeSynonym::RELATION_ANIME]); + return $query->with([Synonym::RELATION_SYNONYMABLE]); } public static function form(Schema $schema): Schema { return $schema ->components([ - BelongsTo::make(AnimeSynonym::ATTRIBUTE_ANIME) - ->resource(AnimeResource::class) - ->hiddenOn([SynonymRelationManager::class, CreateAnimeSubmission::class]), - - Select::make(AnimeSynonym::ATTRIBUTE_TYPE) - ->label(__('filament.fields.anime_synonym.type.name')) - ->helperText(__('filament.fields.anime_synonym.type.help')) - ->options(AnimeSynonymType::class) + Select::make(Synonym::ATTRIBUTE_TYPE) + ->label(__('filament.fields.synonym.type.name')) + ->helperText(__('filament.fields.synonym.type.help')) + ->options(SynonymType::class) ->required(), - TextInput::make(AnimeSynonym::ATTRIBUTE_TEXT) - ->label(__('filament.fields.anime_synonym.text.name')) - ->helperText(__('filament.fields.anime_synonym.text.help')) + TextInput::make(Synonym::ATTRIBUTE_TEXT) + ->label(__('filament.fields.synonym.text.name')) + ->helperText(__('filament.fields.synonym.text.help')) ->required() ->maxLength(255), ]) @@ -107,20 +99,28 @@ class SynonymResource extends BaseResource { return parent::table($table) ->columns([ - TextColumn::make(AnimeSynonym::ATTRIBUTE_ID) + TextColumn::make(Synonym::ATTRIBUTE_ID) ->label(__('filament.fields.base.id')), - BelongsToColumn::make(AnimeSynonym::RELATION_ANIME, AnimeResource::class) - ->hiddenOn(SynonymAnimeRelationManager::class), + TextColumn::make(Synonym::RELATION_SYNONYMABLE) + ->hiddenOn(SynonymAnimeRelationManager::class) + ->color('related-link') + ->weight(FontWeight::SemiBold) + ->state(fn (Synonym $record) => $record->synonymable->getName()) + ->url(function (Synonym $record): string { + $synonymable = $record->synonymable; - TextColumn::make(AnimeSynonym::ATTRIBUTE_TYPE) - ->label(__('filament.fields.anime_synonym.type.name')) - ->formatStateUsing(fn (AnimeSynonymType $state): ?string => $state->localize()), + return Filament::getModelResource($synonymable)::getUrl('view', ['record' => $synonymable]); + }), - TextColumn::make(AnimeSynonym::ATTRIBUTE_TEXT) - ->label(__('filament.fields.anime_synonym.text.name')) + TextColumn::make(Synonym::ATTRIBUTE_TYPE) + ->label(__('filament.fields.synonym.type.name')) + ->formatStateUsing(fn (SynonymType $state): ?string => $state->localize()), + + TextColumn::make(Synonym::ATTRIBUTE_TEXT) + ->label(__('filament.fields.synonym.text.name')) ->limit(50) - ->tooltip(fn (TextColumn $column): mixed => $column->getState()), + ->tooltip(fn (string $state): string => $state), ]) ->searchable(); } @@ -131,17 +131,25 @@ class SynonymResource extends BaseResource ->components([ Section::make(static::getRecordTitle($schema->getRecord())) ->schema([ - TextEntry::make(AnimeSynonym::ATTRIBUTE_ID) + TextEntry::make(Synonym::ATTRIBUTE_ID) ->label(__('filament.fields.base.id')), - BelongsToEntry::make(AnimeSynonym::RELATION_ANIME, AnimeResource::class), + TextEntry::make(Synonym::RELATION_SYNONYMABLE) + ->color('related-link') + ->weight(FontWeight::SemiBold) + ->state(fn (Synonym $record) => $record->synonymable->getName()) + ->url(function (Synonym $record): string { + $synonymable = $record->synonymable; - TextEntry::make(AnimeSynonym::ATTRIBUTE_TYPE) - ->label(__('filament.fields.anime_synonym.type.name')) - ->formatStateUsing(fn (AnimeSynonymType $state): ?string => $state->localize()), + return Filament::getModelResource($synonymable)::getUrl('view', ['record' => $synonymable]); + }), - TextEntry::make(AnimeSynonym::ATTRIBUTE_TEXT) - ->label(__('filament.fields.anime_synonym.text.name')) + TextEntry::make(Synonym::ATTRIBUTE_TYPE) + ->label(__('filament.fields.synonym.type.name')) + ->formatStateUsing(fn (SynonymType $state): ?string => $state->localize()), + + TextEntry::make(Synonym::ATTRIBUTE_TEXT) + ->label(__('filament.fields.synonym.text.name')) ->columnSpanFull(), ]) ->columns(3), @@ -159,12 +167,12 @@ class SynonymResource extends BaseResource return [ QueryBuilder::make() ->constraints([ - TextConstraint::make(AnimeSynonym::ATTRIBUTE_TEXT) - ->label(__('filament.fields.anime_synonym.text.name')), + TextConstraint::make(Synonym::ATTRIBUTE_TEXT) + ->label(__('filament.fields.synonym.text.name')), - SelectConstraint::make(AnimeSynonym::ATTRIBUTE_TYPE) - ->label(__('filament.fields.anime_synonym.type.name')) - ->options(AnimeSynonymType::class) + SelectConstraint::make(Synonym::ATTRIBUTE_TYPE) + ->label(__('filament.fields.synonym.type.name')) + ->options(SynonymType::class) ->multiple(), ...parent::getConstraints(), @@ -186,6 +194,11 @@ class SynonymResource extends BaseResource ]; } + public static function canCreate(): bool + { + return false; + } + /** * @return array */ diff --git a/app/Filament/Submission/Resources/Anime/Pages/CreateAnimeSubmission.php b/app/Filament/Submission/Resources/Anime/Pages/CreateAnimeSubmission.php index 51f2ff955..379305ed8 100644 --- a/app/Filament/Submission/Resources/Anime/Pages/CreateAnimeSubmission.php +++ b/app/Filament/Submission/Resources/Anime/Pages/CreateAnimeSubmission.php @@ -9,7 +9,6 @@ use App\Filament\Actions\Models\Wiki\Song\LoadArtistsAction; use App\Filament\Actions\Models\Wiki\Song\Performance\LoadMembersAction; use App\Filament\Components\Fields\SubmissionBelongsTo; use App\Filament\Components\Fields\TextInput; -use App\Filament\Resources\Wiki\Anime\SynonymResource; use App\Filament\Resources\Wiki\Anime\Theme\EntryResource; use App\Filament\Resources\Wiki\Anime\Theme\Schemas\ThemeForm; use App\Filament\Resources\Wiki\AnimeResource; @@ -20,6 +19,7 @@ use App\Filament\Resources\Wiki\SeriesResource; use App\Filament\Resources\Wiki\Song\RelationManagers\PerformanceSongRelationManager; use App\Filament\Resources\Wiki\SongResource; use App\Filament\Resources\Wiki\StudioResource; +use App\Filament\Resources\Wiki\SynonymResource; use App\Filament\Submission\Resources\AnimeSubmissionResource; use App\Models\User\Submission; use App\Models\User\Submission\SubmissionStage; @@ -69,11 +69,11 @@ class CreateAnimeSubmission extends CreateRecord ->schema(AnimeResource::form($schema)->getComponents()), Tab::make(Anime::RELATION_SYNONYMS) - ->label(__('filament.resources.label.anime_synonyms')) + ->label(__('filament.resources.label.synonyms')) ->schema([ Repeater::make(Anime::RELATION_SYNONYMS) - ->label(__('filament.resources.label.anime_synonyms')) - ->addActionLabel(__('filament.buttons.add', ['label' => __('filament.resources.singularLabel.anime_synonym')])) + ->label(__('filament.resources.label.synonyms')) + ->addActionLabel(__('filament.buttons.add', ['label' => __('filament.resources.singularLabel.synonym')])) ->defaultItems(0) ->schema(SynonymResource::form($schema)->getComponents()), ]), diff --git a/app/GraphQL/Schema/Fields/Field.php b/app/GraphQL/Schema/Fields/Field.php index fd6e23ef0..33903429d 100644 --- a/app/GraphQL/Schema/Fields/Field.php +++ b/app/GraphQL/Schema/Fields/Field.php @@ -18,6 +18,8 @@ abstract class Field { use ResolvesArguments; + public ?string $deprecationReason = null; + public function __construct( protected string $column, protected ?string $name = null, @@ -66,6 +68,13 @@ abstract class Field return $type; } + public function deprecate(string $reason): self + { + $this->deprecationReason = $reason; + + return $this; + } + /** * @return Type|BaseType */ diff --git a/app/GraphQL/Schema/Fields/Wiki/Synonym/SynonymTextField.php b/app/GraphQL/Schema/Fields/Wiki/Synonym/SynonymTextField.php new file mode 100644 index 000000000..94ced0fc7 --- /dev/null +++ b/app/GraphQL/Schema/Fields/Wiki/Synonym/SynonymTextField.php @@ -0,0 +1,49 @@ + $args + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + 'string', + 'max:192', + ]; + } + + /** + * @param array $args + */ + public function getUpdateRules(array $args): array + { + return [ + 'sometimes', + 'required', + 'string', + 'max:192', + ]; + } +} diff --git a/app/GraphQL/Schema/Fields/Wiki/Synonym/SynonymTypeField.php b/app/GraphQL/Schema/Fields/Wiki/Synonym/SynonymTypeField.php new file mode 100644 index 000000000..04b847d30 --- /dev/null +++ b/app/GraphQL/Schema/Fields/Wiki/Synonym/SynonymTypeField.php @@ -0,0 +1,49 @@ + $args + */ + public function getCreationRules(array $args): array + { + return [ + 'required', + new Enum(SynonymType::class), + ]; + } + + /** + * @param array $args + */ + public function getUpdateRules(array $args): array + { + return [ + 'sometimes', + 'required', + new Enum(SynonymType::class), + ]; + } +} diff --git a/app/GraphQL/Schema/Queries/BaseQuery.php b/app/GraphQL/Schema/Queries/BaseQuery.php index e2fc190c1..434a14619 100644 --- a/app/GraphQL/Schema/Queries/BaseQuery.php +++ b/app/GraphQL/Schema/Queries/BaseQuery.php @@ -7,6 +7,7 @@ namespace App\GraphQL\Schema\Queries; use App\Concerns\Actions\GraphQL\ConstrainsEagerLoads; use App\Concerns\Actions\GraphQL\FiltersModels; use App\Concerns\GraphQL\ResolvesArguments; +use App\Contracts\GraphQL\Fields\DeprecatedField; use App\GraphQL\Argument\Argument; use App\GraphQL\Argument\FirstArgument; use App\GraphQL\Argument\PageArgument; @@ -49,6 +50,7 @@ abstract class BaseQuery extends Query 'name' => $this->getName(), 'description' => $this->description(), 'baseType' => $this->baseType(), + 'deprecationReason' => $this instanceof DeprecatedField ? $this->deprecationReason() : null, ]; } diff --git a/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/Anime/AnimeSynonymPaginationQuery.php b/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/Anime/AnimeSynonymPaginationQuery.php index 9bd74bf30..2d3fdda51 100644 --- a/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/Anime/AnimeSynonymPaginationQuery.php +++ b/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/Anime/AnimeSynonymPaginationQuery.php @@ -4,10 +4,11 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Queries\Models\Pagination\Wiki\Anime; +use App\Contracts\GraphQL\Fields\DeprecatedField; use App\GraphQL\Schema\Queries\Models\Pagination\EloquentPaginationQuery; use App\GraphQL\Schema\Types\Wiki\Anime\AnimeSynonymType; -class AnimeSynonymPaginationQuery extends EloquentPaginationQuery +class AnimeSynonymPaginationQuery extends EloquentPaginationQuery implements DeprecatedField { public function __construct() { @@ -26,4 +27,9 @@ class AnimeSynonymPaginationQuery extends EloquentPaginationQuery { return new AnimeSynonymType(); } + + public function deprecationReason(): string + { + return 'Use synonymPagination query instead.'; + } } diff --git a/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/SynonymPaginationQuery.php b/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/SynonymPaginationQuery.php new file mode 100644 index 000000000..0376ebd51 --- /dev/null +++ b/app/GraphQL/Schema/Queries/Models/Pagination/Wiki/SynonymPaginationQuery.php @@ -0,0 +1,29 @@ + $relation->type(), 'args' => $relation->args(), 'resolve' => $relation->resolve(...), + 'deprecationReason' => $relation instanceof DeprecatedField ? $relation->deprecationReason() : $relation->deprecationReason, ], ]); @@ -91,10 +92,8 @@ abstract class BaseType extends RebingType 'alias' => $field->getColumn(), 'args' => $field->args(), 'resolve' => $field->resolve(...), - + 'deprecationReason' => $field instanceof DeprecatedField ? $field->deprecationReason() : $field->deprecationReason, 'fieldClass' => $field, - - ...($field instanceof DeprecatedField ? ['deprecationReason' => $field->deprecationReason()] : []), ], ]); diff --git a/app/GraphQL/Schema/Types/Document/PageType.php b/app/GraphQL/Schema/Types/Document/PageType.php index 139df2333..833d67ae0 100644 --- a/app/GraphQL/Schema/Types/Document/PageType.php +++ b/app/GraphQL/Schema/Types/Document/PageType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Document; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdUnbindableField; @@ -17,7 +16,7 @@ use App\GraphQL\Schema\Fields\Relations\BelongsToRelation; use App\GraphQL\Schema\Types\EloquentType; use App\Models\Document\Page; -class PageType extends EloquentType implements SubmitableType +class PageType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Pivot/Morph/ImageableType.php b/app/GraphQL/Schema/Types/Pivot/Morph/ImageableType.php index 98db6ac8c..c753f996f 100644 --- a/app/GraphQL/Schema/Types/Pivot/Morph/ImageableType.php +++ b/app/GraphQL/Schema/Types/Pivot/Morph/ImageableType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Pivot\Morph; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\UpdatedAtField; use App\GraphQL\Schema\Fields\Field; @@ -16,7 +15,7 @@ use App\GraphQL\Schema\Types\Wiki\ImageType; use App\GraphQL\Schema\Unions\ImageableUnion; use App\Pivots\Morph\Imageable; -class ImageableType extends PivotType implements SubmitableType +class ImageableType extends PivotType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Pivot/Morph/ResourceableType.php b/app/GraphQL/Schema/Types/Pivot/Morph/ResourceableType.php index 5de73715f..cc74c96d3 100644 --- a/app/GraphQL/Schema/Types/Pivot/Morph/ResourceableType.php +++ b/app/GraphQL/Schema/Types/Pivot/Morph/ResourceableType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Pivot\Morph; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\UpdatedAtField; use App\GraphQL\Schema\Fields\Field; @@ -16,7 +15,7 @@ use App\GraphQL\Schema\Types\Wiki\ExternalResourceType; use App\GraphQL\Schema\Unions\ResourceableUnion; use App\Pivots\Morph\Resourceable; -class ResourceableType extends PivotType implements SubmitableType +class ResourceableType extends PivotType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeSeriesType.php b/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeSeriesType.php index 55187aaa9..d9a57148b 100644 --- a/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeSeriesType.php +++ b/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeSeriesType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Pivot\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\UpdatedAtField; use App\GraphQL\Schema\Fields\Field; @@ -14,7 +13,7 @@ use App\GraphQL\Schema\Types\Wiki\AnimeType; use App\GraphQL\Schema\Types\Wiki\SeriesType; use App\Pivots\Wiki\AnimeSeries; -class AnimeSeriesType extends PivotType implements SubmitableType +class AnimeSeriesType extends PivotType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeStudioType.php b/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeStudioType.php index 95b383be4..dcd00ad0d 100644 --- a/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeStudioType.php +++ b/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeStudioType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Pivot\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\UpdatedAtField; use App\GraphQL\Schema\Fields\Field; @@ -14,7 +13,7 @@ use App\GraphQL\Schema\Types\Wiki\AnimeType; use App\GraphQL\Schema\Types\Wiki\StudioType; use App\Pivots\Wiki\AnimeStudio; -class AnimeStudioType extends PivotType implements SubmitableType +class AnimeStudioType extends PivotType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeThemeEntryVideoType.php b/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeThemeEntryVideoType.php index 89cd21564..a8b770b1c 100644 --- a/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeThemeEntryVideoType.php +++ b/app/GraphQL/Schema/Types/Pivot/Wiki/AnimeThemeEntryVideoType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Pivot\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\UpdatedAtField; use App\GraphQL\Schema\Fields\Field; @@ -14,7 +13,7 @@ use App\GraphQL\Schema\Types\Wiki\Anime\Theme\AnimeThemeEntryType; use App\GraphQL\Schema\Types\Wiki\VideoType; use App\Pivots\Wiki\AnimeThemeEntryVideo; -class AnimeThemeEntryVideoType extends PivotType implements SubmitableType +class AnimeThemeEntryVideoType extends PivotType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Pivot/Wiki/ArtistMemberType.php b/app/GraphQL/Schema/Types/Pivot/Wiki/ArtistMemberType.php index 95e5d9183..b4761c25b 100644 --- a/app/GraphQL/Schema/Types/Pivot/Wiki/ArtistMemberType.php +++ b/app/GraphQL/Schema/Types/Pivot/Wiki/ArtistMemberType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Pivot\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\UpdatedAtField; use App\GraphQL\Schema\Fields\Field; @@ -17,7 +16,7 @@ use App\GraphQL\Schema\Types\Pivot\PivotType; use App\GraphQL\Schema\Types\Wiki\ArtistType; use App\Pivots\Wiki\ArtistMember; -class ArtistMemberType extends PivotType implements SubmitableType +class ArtistMemberType extends PivotType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/Anime/AnimeSynonymType.php b/app/GraphQL/Schema/Types/Wiki/Anime/AnimeSynonymType.php index dc7674581..077174511 100644 --- a/app/GraphQL/Schema/Types/Wiki/Anime/AnimeSynonymType.php +++ b/app/GraphQL/Schema/Types/Wiki/Anime/AnimeSynonymType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki\Anime; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -18,7 +17,7 @@ use App\GraphQL\Schema\Types\EloquentType; use App\GraphQL\Schema\Types\Wiki\AnimeType; use App\Models\Wiki\Anime\AnimeSynonym; -class AnimeSynonymType extends EloquentType implements SubmitableType +class AnimeSynonymType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/Anime/AnimeThemeType.php b/app/GraphQL/Schema/Types/Wiki/Anime/AnimeThemeType.php index 9721ab2ea..13076fc07 100644 --- a/app/GraphQL/Schema/Types/Wiki/Anime/AnimeThemeType.php +++ b/app/GraphQL/Schema/Types/Wiki/Anime/AnimeThemeType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki\Anime; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -24,7 +23,7 @@ use App\GraphQL\Schema\Types\Wiki\SongType; use App\GraphQL\Schema\Types\Wiki\ThemeGroupType; use App\Models\Wiki\Anime\AnimeTheme; -class AnimeThemeType extends EloquentType implements SubmitableType +class AnimeThemeType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/Anime/Theme/AnimeThemeEntryType.php b/app/GraphQL/Schema/Types/Wiki/Anime/Theme/AnimeThemeEntryType.php index 41e3ea35d..be03577dc 100644 --- a/app/GraphQL/Schema/Types/Wiki/Anime/Theme/AnimeThemeEntryType.php +++ b/app/GraphQL/Schema/Types/Wiki/Anime/Theme/AnimeThemeEntryType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki\Anime\Theme; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\Aggregate\LikesCountField; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; @@ -28,7 +27,7 @@ use App\GraphQL\Schema\Types\Wiki\ExternalResourceType; use App\GraphQL\Schema\Types\Wiki\VideoType; use App\Models\Wiki\Anime\Theme\AnimeThemeEntry; -class AnimeThemeEntryType extends EloquentType implements SubmitableType +class AnimeThemeEntryType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/AnimeType.php b/app/GraphQL/Schema/Types/Wiki/AnimeType.php index 3b6934501..b82be2d69 100644 --- a/app/GraphQL/Schema/Types/Wiki/AnimeType.php +++ b/app/GraphQL/Schema/Types/Wiki/AnimeType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdUnbindableField; @@ -13,6 +12,7 @@ use App\GraphQL\Schema\Fields\Field; use App\GraphQL\Schema\Fields\LocalizedEnumField; use App\GraphQL\Schema\Fields\Relations\BelongsToManyRelation; use App\GraphQL\Schema\Fields\Relations\HasManyRelation; +use App\GraphQL\Schema\Fields\Relations\MorphManyRelation; use App\GraphQL\Schema\Fields\Relations\MorphToManyRelation; use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeMediaFormatField; use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeNameField; @@ -29,7 +29,7 @@ use App\GraphQL\Schema\Types\Wiki\Anime\AnimeSynonymType; use App\GraphQL\Schema\Types\Wiki\Anime\AnimeThemeType; use App\Models\Wiki\Anime; -class AnimeType extends EloquentType implements SubmitableType +class AnimeType extends EloquentType { public function description(): string { @@ -57,7 +57,9 @@ class AnimeType extends EloquentType implements SubmitableType new UpdatedAtField(), new DeletedAtField(), - new HasManyRelation(new AnimeSynonymType(), Anime::RELATION_SYNONYMS), + new HasManyRelation(new AnimeSynonymType(), Anime::RELATION_ANIMESYNONYMS) + ->deprecate('Use synonyms instead.'), + new MorphManyRelation(new SynonymType(), Anime::RELATION_SYNONYMS), new HasManyRelation(new AnimeThemeType(), Anime::RELATION_THEMES), new MorphToManyRelation($this, new ExternalResourceType(), Anime::RELATION_RESOURCES, new ResourceableType()), new MorphToManyRelation($this, new ImageType(), Anime::RELATION_IMAGES, new ImageableType()), diff --git a/app/GraphQL/Schema/Types/Wiki/ArtistType.php b/app/GraphQL/Schema/Types/Wiki/ArtistType.php index d7d1270e8..770bb9478 100644 --- a/app/GraphQL/Schema/Types/Wiki/ArtistType.php +++ b/app/GraphQL/Schema/Types/Wiki/ArtistType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdUnbindableField; @@ -25,7 +24,7 @@ use App\GraphQL\Schema\Types\Wiki\Song\MembershipType; use App\GraphQL\Schema\Types\Wiki\Song\PerformanceType; use App\Models\Wiki\Artist; -class ArtistType extends EloquentType implements SubmitableType +class ArtistType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/ExternalResourceType.php b/app/GraphQL/Schema/Types/Wiki/ExternalResourceType.php index 43b7c5793..b4a1a8110 100644 --- a/app/GraphQL/Schema/Types/Wiki/ExternalResourceType.php +++ b/app/GraphQL/Schema/Types/Wiki/ExternalResourceType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -19,7 +18,7 @@ use App\GraphQL\Schema\Types\EloquentType; use App\GraphQL\Schema\Types\Pivot\Morph\ResourceableType; use App\Models\Wiki\ExternalResource; -class ExternalResourceType extends EloquentType implements SubmitableType +class ExternalResourceType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/ImageType.php b/app/GraphQL/Schema/Types/Wiki/ImageType.php index e123709af..71497119e 100644 --- a/app/GraphQL/Schema/Types/Wiki/ImageType.php +++ b/app/GraphQL/Schema/Types/Wiki/ImageType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -19,7 +18,7 @@ use App\GraphQL\Schema\Types\EloquentType; use App\GraphQL\Schema\Types\Pivot\Morph\ImageableType; use App\Models\Wiki\Image; -class ImageType extends EloquentType implements SubmitableType +class ImageType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/SeriesType.php b/app/GraphQL/Schema/Types/Wiki/SeriesType.php index 50b6edb27..7b38e20a8 100644 --- a/app/GraphQL/Schema/Types/Wiki/SeriesType.php +++ b/app/GraphQL/Schema/Types/Wiki/SeriesType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdUnbindableField; @@ -17,7 +16,7 @@ use App\GraphQL\Schema\Types\EloquentType; use App\GraphQL\Schema\Types\Pivot\Wiki\AnimeSeriesType; use App\Models\Wiki\Series; -class SeriesType extends EloquentType implements SubmitableType +class SeriesType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/Song/MembershipType.php b/app/GraphQL/Schema/Types/Wiki/Song/MembershipType.php index 29348538b..8f4f4ed85 100644 --- a/app/GraphQL/Schema/Types/Wiki/Song/MembershipType.php +++ b/app/GraphQL/Schema/Types/Wiki/Song/MembershipType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki\Song; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -18,7 +17,7 @@ use App\GraphQL\Schema\Types\EloquentType; use App\GraphQL\Schema\Types\Wiki\ArtistType; use App\Models\Wiki\Song\Membership; -class MembershipType extends EloquentType implements SubmitableType +class MembershipType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/Song/PerformanceType.php b/app/GraphQL/Schema/Types/Wiki/Song/PerformanceType.php index a769f9c7d..9697a23aa 100644 --- a/app/GraphQL/Schema/Types/Wiki/Song/PerformanceType.php +++ b/app/GraphQL/Schema/Types/Wiki/Song/PerformanceType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki\Song; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -20,7 +19,7 @@ use App\GraphQL\Schema\Types\Wiki\SongType; use App\GraphQL\Schema\Unions\PerformanceArtistUnion; use App\Models\Wiki\Song\Performance; -class PerformanceType extends EloquentType implements SubmitableType +class PerformanceType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/SongType.php b/app/GraphQL/Schema/Types/Wiki/SongType.php index e283c536c..3fbb3fa9a 100644 --- a/app/GraphQL/Schema/Types/Wiki/SongType.php +++ b/app/GraphQL/Schema/Types/Wiki/SongType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -20,7 +19,7 @@ use App\GraphQL\Schema\Types\Wiki\Anime\AnimeThemeType; use App\GraphQL\Schema\Types\Wiki\Song\PerformanceType; use App\Models\Wiki\Song; -class SongType extends EloquentType implements SubmitableType +class SongType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/StudioType.php b/app/GraphQL/Schema/Types/Wiki/StudioType.php index 0aa18670c..3dccc957d 100644 --- a/app/GraphQL/Schema/Types/Wiki/StudioType.php +++ b/app/GraphQL/Schema/Types/Wiki/StudioType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdUnbindableField; @@ -20,7 +19,7 @@ use App\GraphQL\Schema\Types\Pivot\Morph\ResourceableType; use App\GraphQL\Schema\Types\Pivot\Wiki\AnimeStudioType; use App\Models\Wiki\Studio; -class StudioType extends EloquentType implements SubmitableType +class StudioType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/SynonymType.php b/app/GraphQL/Schema/Types/Wiki/SynonymType.php new file mode 100644 index 000000000..39cef256f --- /dev/null +++ b/app/GraphQL/Schema/Types/Wiki/SynonymType.php @@ -0,0 +1,47 @@ +nonNullable(), + ]; + } +} diff --git a/app/GraphQL/Schema/Types/Wiki/ThemeGroupType.php b/app/GraphQL/Schema/Types/Wiki/ThemeGroupType.php index b55b3d94d..59e88ebeb 100644 --- a/app/GraphQL/Schema/Types/Wiki/ThemeGroupType.php +++ b/app/GraphQL/Schema/Types/Wiki/ThemeGroupType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -17,7 +16,7 @@ use App\GraphQL\Schema\Types\EloquentType; use App\GraphQL\Schema\Types\Wiki\Anime\AnimeThemeType; use App\Models\Wiki\Group; -class ThemeGroupType extends EloquentType implements SubmitableType +class ThemeGroupType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Types/Wiki/VideoType.php b/app/GraphQL/Schema/Types/Wiki/VideoType.php index 870509cbb..eba8d5d90 100644 --- a/app/GraphQL/Schema/Types/Wiki/VideoType.php +++ b/app/GraphQL/Schema/Types/Wiki/VideoType.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\GraphQL\Schema\Types\Wiki; -use App\Contracts\GraphQL\Types\SubmitableType; use App\GraphQL\Schema\Fields\Base\CreatedAtField; use App\GraphQL\Schema\Fields\Base\DeletedAtField; use App\GraphQL\Schema\Fields\Base\IdField; @@ -38,7 +37,7 @@ use App\GraphQL\Schema\Types\Wiki\Anime\Theme\AnimeThemeEntryType; use App\GraphQL\Schema\Types\Wiki\Video\VideoScriptType; use App\Models\Wiki\Video; -class VideoType extends EloquentType implements SubmitableType +class VideoType extends EloquentType { public function description(): string { diff --git a/app/GraphQL/Schema/Unions/SynonymableUnion.php b/app/GraphQL/Schema/Unions/SynonymableUnion.php new file mode 100644 index 000000000..152cbb1a8 --- /dev/null +++ b/app/GraphQL/Schema/Unions/SynonymableUnion.php @@ -0,0 +1,28 @@ +withIntermediatePaths(); + } + + /** + * @return Field[] + */ + public function fields(): array + { + return array_merge( + parent::fields(), + [ + new IdField($this, Synonym::ATTRIBUTE_ID), + new SynonymTextField($this), + new SynonymTypeField($this), + new SynonymSynonymableTypeField($this), + new SynonymSynonymableIdField($this), + ], + ); + } +} diff --git a/app/Http/Controllers/Api/Wiki/Anime/SynonymController.php b/app/Http/Controllers/Api/Wiki/Anime/AnimeSynonymController.php similarity index 72% rename from app/Http/Controllers/Api/Wiki/Anime/SynonymController.php rename to app/Http/Controllers/Api/Wiki/Anime/AnimeSynonymController.php index edad82103..fa027da4b 100644 --- a/app/Http/Controllers/Api/Wiki/Anime/SynonymController.php +++ b/app/Http/Controllers/Api/Wiki/Anime/AnimeSynonymController.php @@ -17,19 +17,19 @@ use App\Http\Requests\Api\IndexRequest; use App\Http\Requests\Api\ShowRequest; use App\Http\Requests\Api\StoreRequest; use App\Http\Requests\Api\UpdateRequest; -use App\Http\Resources\Wiki\Anime\Collection\SynonymCollection; -use App\Http\Resources\Wiki\Anime\Resource\SynonymJsonResource; +use App\Http\Resources\Wiki\Anime\Collection\AnimeSynonymCollection; +use App\Http\Resources\Wiki\Anime\Resource\AnimeSynonymJsonResource; use App\Models\Wiki\Anime\AnimeSynonym; use Illuminate\Http\JsonResponse; -class SynonymController extends BaseController +class AnimeSynonymController extends BaseController { public function __construct() { parent::__construct(AnimeSynonym::class, 'animesynonym'); } - public function index(IndexRequest $request, IndexAction $action): SynonymCollection + public function index(IndexRequest $request, IndexAction $action): AnimeSynonymCollection { $query = new Query($request->validated()); @@ -37,47 +37,47 @@ class SynonymController extends BaseController ? $action->search($query, $request->schema()) : $action->index(AnimeSynonym::query(), $query, $request->schema()); - return new SynonymCollection($synonyms, $query); + return new AnimeSynonymCollection($synonyms, $query); } /** * @param StoreAction $action */ - public function store(StoreRequest $request, StoreAction $action): SynonymJsonResource + public function store(StoreRequest $request, StoreAction $action): AnimeSynonymJsonResource { $synonym = $action->store(AnimeSynonym::query(), $request->validated()); - return new SynonymJsonResource($synonym, new Query()); + return new AnimeSynonymJsonResource($synonym, new Query()); } - public function show(ShowRequest $request, AnimeSynonym $animesynonym, ShowAction $action): SynonymJsonResource + public function show(ShowRequest $request, AnimeSynonym $animesynonym, ShowAction $action): AnimeSynonymJsonResource { $query = new Query($request->validated()); $show = $action->show($animesynonym, $query, $request->schema()); - return new SynonymJsonResource($show, $query); + return new AnimeSynonymJsonResource($show, $query); } - public function update(UpdateRequest $request, AnimeSynonym $animesynonym, UpdateAction $action): SynonymJsonResource + public function update(UpdateRequest $request, AnimeSynonym $animesynonym, UpdateAction $action): AnimeSynonymJsonResource { $updated = $action->update($animesynonym, $request->validated()); - return new SynonymJsonResource($updated, new Query()); + return new AnimeSynonymJsonResource($updated, new Query()); } - public function destroy(AnimeSynonym $animesynonym, DestroyAction $action): SynonymJsonResource + public function destroy(AnimeSynonym $animesynonym, DestroyAction $action): AnimeSynonymJsonResource { $deleted = $action->destroy($animesynonym); - return new SynonymJsonResource($deleted, new Query()); + return new AnimeSynonymJsonResource($deleted, new Query()); } - public function restore(AnimeSynonym $animesynonym, RestoreAction $action): SynonymJsonResource + public function restore(AnimeSynonym $animesynonym, RestoreAction $action): AnimeSynonymJsonResource { $restored = $action->restore($animesynonym); - return new SynonymJsonResource($restored, new Query()); + return new AnimeSynonymJsonResource($restored, new Query()); } public function forceDelete(AnimeSynonym $animesynonym, ForceDeleteAction $action): JsonResponse diff --git a/app/Http/Controllers/Api/Wiki/SynonymController.php b/app/Http/Controllers/Api/Wiki/SynonymController.php new file mode 100644 index 000000000..6a8cb20c1 --- /dev/null +++ b/app/Http/Controllers/Api/Wiki/SynonymController.php @@ -0,0 +1,91 @@ +validated()); + + $synonyms = $query->hasSearchCriteria() + ? $action->search($query, $request->schema()) + : $action->index(Synonym::query(), $query, $request->schema()); + + return new SynonymCollection($synonyms, $query); + } + + /** + * @param StoreAction $action + */ + public function store(StoreRequest $request, StoreAction $action): SynonymJsonResource + { + $synonym = $action->store(Synonym::query(), $request->validated()); + + return new SynonymJsonResource($synonym, new Query()); + } + + public function show(ShowRequest $request, Synonym $synonym, ShowAction $action): SynonymJsonResource + { + $query = new Query($request->validated()); + + $show = $action->show($synonym, $query, $request->schema()); + + return new SynonymJsonResource($show, $query); + } + + public function update(UpdateRequest $request, Synonym $synonym, UpdateAction $action): SynonymJsonResource + { + $updated = $action->update($synonym, $request->validated()); + + return new SynonymJsonResource($updated, new Query()); + } + + public function destroy(Synonym $synonym, DestroyAction $action): SynonymJsonResource + { + $deleted = $action->destroy($synonym); + + return new SynonymJsonResource($deleted, new Query()); + } + + public function restore(Synonym $synonym, RestoreAction $action): SynonymJsonResource + { + $restored = $action->restore($synonym); + + return new SynonymJsonResource($restored, new Query()); + } + + public function forceDelete(Synonym $synonym, ForceDeleteAction $action): JsonResponse + { + $message = $action->forceDelete($synonym); + + return new JsonResponse([ + 'message' => $message, + ]); + } +} diff --git a/app/Http/Resources/Wiki/Anime/Collection/SynonymCollection.php b/app/Http/Resources/Wiki/Anime/Collection/AnimeSynonymCollection.php similarity index 71% rename from app/Http/Resources/Wiki/Anime/Collection/SynonymCollection.php rename to app/Http/Resources/Wiki/Anime/Collection/AnimeSynonymCollection.php index 4254ed60e..36ec2fde2 100644 --- a/app/Http/Resources/Wiki/Anime/Collection/SynonymCollection.php +++ b/app/Http/Resources/Wiki/Anime/Collection/AnimeSynonymCollection.php @@ -5,11 +5,11 @@ declare(strict_types=1); namespace App\Http\Resources\Wiki\Anime\Collection; use App\Http\Resources\BaseCollection; -use App\Http\Resources\Wiki\Anime\Resource\SynonymJsonResource; +use App\Http\Resources\Wiki\Anime\Resource\AnimeSynonymJsonResource; use App\Models\Wiki\Anime\AnimeSynonym; use Illuminate\Http\Request; -class SynonymCollection extends BaseCollection +class AnimeSynonymCollection extends BaseCollection { /** * The "data" wrapper that should be applied. @@ -27,7 +27,7 @@ class SynonymCollection extends BaseCollection public function toArray(Request $request): array { return $this->collection->map( - fn (AnimeSynonym $synonym): SynonymJsonResource => new SynonymJsonResource($synonym, $this->query) + fn (AnimeSynonym $synonym): AnimeSynonymJsonResource => new AnimeSynonymJsonResource($synonym, $this->query) )->all(); } } diff --git a/app/Http/Resources/Wiki/Anime/Resource/AnimeSynonymJsonResource.php b/app/Http/Resources/Wiki/Anime/Resource/AnimeSynonymJsonResource.php new file mode 100644 index 000000000..c9fced574 --- /dev/null +++ b/app/Http/Resources/Wiki/Anime/Resource/AnimeSynonymJsonResource.php @@ -0,0 +1,27 @@ +collection->map( + fn (Synonym $synonym): SynonymJsonResource => new SynonymJsonResource($synonym, $this->query) + )->all(); + } +} diff --git a/app/Http/Resources/Wiki/Anime/Resource/SynonymJsonResource.php b/app/Http/Resources/Wiki/Resource/SynonymJsonResource.php similarity index 73% rename from app/Http/Resources/Wiki/Anime/Resource/SynonymJsonResource.php rename to app/Http/Resources/Wiki/Resource/SynonymJsonResource.php index 60d369689..4e9c8de77 100644 --- a/app/Http/Resources/Wiki/Anime/Resource/SynonymJsonResource.php +++ b/app/Http/Resources/Wiki/Resource/SynonymJsonResource.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace App\Http\Resources\Wiki\Anime\Resource; +namespace App\Http\Resources\Wiki\Resource; use App\Http\Api\Schema\Schema; -use App\Http\Api\Schema\Wiki\Anime\SynonymSchema; +use App\Http\Api\Schema\Wiki\SynonymSchema; use App\Http\Resources\BaseJsonResource; class SynonymJsonResource extends BaseJsonResource @@ -15,7 +15,7 @@ class SynonymJsonResource extends BaseJsonResource * * @var string|null */ - public static $wrap = 'animesynonym'; + public static $wrap = 'synonym'; /** * Get the resource schema. diff --git a/app/Listeners/Wiki/UpdateAnimeSynonyms.php b/app/Listeners/Wiki/UpdateAnimeSynonyms.php new file mode 100644 index 000000000..19ea6f347 --- /dev/null +++ b/app/Listeners/Wiki/UpdateAnimeSynonyms.php @@ -0,0 +1,16 @@ +updateAnimeSynonyms(); + } +} diff --git a/app/Models/Wiki/Anime.php b/app/Models/Wiki/Anime.php index 19c03835a..7369d5303 100644 --- a/app/Models/Wiki/Anime.php +++ b/app/Models/Wiki/Anime.php @@ -8,6 +8,7 @@ use App\Concerns\Models\SoftDeletes; use App\Concerns\Models\Submitable; use App\Contracts\Models\HasImages; use App\Contracts\Models\HasResources; +use App\Contracts\Models\HasSynonyms; use App\Contracts\Models\SoftDeletable; use App\Enums\Models\Wiki\AnimeMediaFormat; use App\Enums\Models\Wiki\AnimeSeason; @@ -28,12 +29,14 @@ use App\Pivots\Morph\Resourceable; use App\Pivots\Wiki\AnimeSeries; use App\Pivots\Wiki\AnimeStudio; use Database\Factories\Wiki\AnimeFactory; +use Deprecated; use Elastic\ScoutDriverPlus\Searchable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Support\Collection; use OwenIt\Auditing\Auditable as HasAudits; @@ -41,7 +44,6 @@ use OwenIt\Auditing\Contracts\Auditable; /** * @property int $anime_id - * @property Collection $animesynonyms * @property Collection $animethemes * @property DiscordThread|null $discordthread * @property Collection $externalentries @@ -53,12 +55,13 @@ use OwenIt\Auditing\Contracts\Auditable; * @property Collection $series * @property string $slug * @property Collection $studios + * @property Collection $synonyms * @property string|null $synopsis * @property int|null $year * * @method static AnimeFactory factory(...$parameters) */ -class Anime extends BaseModel implements Auditable, HasImages, HasResources, SoftDeletable +class Anime extends BaseModel implements Auditable, HasImages, HasResources, HasSynonyms, SoftDeletable { use HasAudits; use HasFactory; @@ -88,7 +91,8 @@ class Anime extends BaseModel implements Auditable, HasImages, HasResources, Sof final public const string RELATION_SERIES = 'series'; final public const string RELATION_SONG = 'animethemes.song'; final public const string RELATION_STUDIOS = 'studios'; - final public const string RELATION_SYNONYMS = 'animesynonyms'; + final public const string RELATION_ANIMESYNONYMS = 'animesynonyms'; + final public const string RELATION_SYNONYMS = 'synonyms'; final public const string RELATION_THEMES = 'animethemes'; final public const string RELATION_VIDEOS = 'animethemes.animethemeentries.videos'; @@ -163,7 +167,7 @@ class Anime extends BaseModel implements Auditable, HasImages, HasResources, Sof public function toSearchableArray(): array { $array = $this->toArray(); - $array['synonyms'] = $this->animesynonyms->toArray(); + $array['synonyms'] = $this->synonyms->toArray(); return $array; } @@ -191,11 +195,22 @@ class Anime extends BaseModel implements Auditable, HasImages, HasResources, Sof /** * @return HasMany */ + #[Deprecated('Use synonyms() instead.')] public function animesynonyms(): HasMany { return $this->hasMany(AnimeSynonym::class, AnimeSynonym::ATTRIBUTE_ANIME); } + /** + * Get the synonyms for the owner model. + * + * @return MorphMany + */ + public function synonyms(): MorphMany + { + return $this->morphMany(Synonym::class, Synonym::RELATION_SYNONYMABLE); + } + /** * @return HasOne */ diff --git a/app/Models/Wiki/Anime/AnimeSynonym.php b/app/Models/Wiki/Anime/AnimeSynonym.php index 3e6fe7658..7ae7b40de 100644 --- a/app/Models/Wiki/Anime/AnimeSynonym.php +++ b/app/Models/Wiki/Anime/AnimeSynonym.php @@ -8,13 +8,8 @@ use App\Concerns\Models\SoftDeletes; use App\Concerns\Models\Submitable; use App\Contracts\Models\SoftDeletable; use App\Enums\Models\Wiki\AnimeSynonymType; -use App\Events\Wiki\Anime\Synonym\SynonymCreated; -use App\Events\Wiki\Anime\Synonym\SynonymDeleted; -use App\Events\Wiki\Anime\Synonym\SynonymRestored; -use App\Events\Wiki\Anime\Synonym\SynonymUpdated; use App\Models\BaseModel; use App\Models\Wiki\Anime; -use Database\Factories\Wiki\Anime\AnimeSynonymFactory; use Elastic\ScoutDriverPlus\Searchable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -28,7 +23,7 @@ use OwenIt\Auditing\Contracts\Auditable; * @property string|null $text * @property AnimeSynonymType $type * - * @method static AnimeSynonymFactory factory(...$parameters) + * @deprecated Use Synonym instead. */ class AnimeSynonym extends BaseModel implements Auditable, SoftDeletable { @@ -74,20 +69,6 @@ class AnimeSynonym extends BaseModel implements Auditable, SoftDeletable AnimeSynonym::ATTRIBUTE_TYPE, ]; - /** - * The event map for the model. - * - * Allows for object-based events for native Eloquent events. - * - * @var array - */ - protected $dispatchesEvents = [ - 'created' => SynonymCreated::class, - 'deleted' => SynonymDeleted::class, - 'restored' => SynonymRestored::class, - 'updated' => SynonymUpdated::class, - ]; - /** * Get the attributes that should be cast. * diff --git a/app/Models/Wiki/Synonym.php b/app/Models/Wiki/Synonym.php new file mode 100644 index 000000000..c4baae211 --- /dev/null +++ b/app/Models/Wiki/Synonym.php @@ -0,0 +1,123 @@ + + */ + protected $fillable = [ + Synonym::ATTRIBUTE_SYNONYMABLE_TYPE, + Synonym::ATTRIBUTE_SYNONYMABLE_ID, + Synonym::ATTRIBUTE_TEXT, + Synonym::ATTRIBUTE_TYPE, + ]; + + /** + * The event map for the model. + * + * Allows for object-based events for native Eloquent events. + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => SynonymCreated::class, + 'deleted' => SynonymDeleted::class, + 'restored' => SynonymRestored::class, + 'updated' => SynonymUpdated::class, + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + Synonym::ATTRIBUTE_TYPE => SynonymType::class, + ]; + } + + public function getName(): string + { + return $this->text; + } + + public function getSubtitle(): string + { + return $this->synonymable->getName(); + } + + /** + * @return MorphTo + */ + public function synonymable(): MorphTo + { + return $this->morphTo(); + } +} diff --git a/app/Policies/Wiki/SynonymPolicy.php b/app/Policies/Wiki/SynonymPolicy.php new file mode 100644 index 000000000..09959ab97 --- /dev/null +++ b/app/Policies/Wiki/SynonymPolicy.php @@ -0,0 +1,9 @@ +mustRaw([ + 'dis_max' => [ + 'queries' => [ + [ + 'bool' => [ + 'should' => $this->createTextQuery('text', $criteria->getTerm()), + ], + ], + ], + ], + ]); + + return Synonym::searchQuery($query); + } +} diff --git a/app/Scout/Elasticsearch/Api/Schema/Wiki/Anime/SynonymSchema.php b/app/Scout/Elasticsearch/Api/Schema/Wiki/Anime/AnimeSynonymSchema.php similarity index 83% rename from app/Scout/Elasticsearch/Api/Schema/Wiki/Anime/SynonymSchema.php rename to app/Scout/Elasticsearch/Api/Schema/Wiki/Anime/AnimeSynonymSchema.php index 0f0cf89d4..9a355095a 100644 --- a/app/Scout/Elasticsearch/Api/Schema/Wiki/Anime/SynonymSchema.php +++ b/app/Scout/Elasticsearch/Api/Schema/Wiki/Anime/AnimeSynonymSchema.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace App\Scout\Elasticsearch\Api\Schema\Wiki\Anime; use App\Http\Api\Include\AllowedInclude; -use App\Http\Resources\Wiki\Anime\Resource\SynonymJsonResource; +use App\Http\Resources\Wiki\Anime\Resource\AnimeSynonymJsonResource; use App\Models\Wiki\Anime\AnimeSynonym; use App\Scout\Elasticsearch\Api\Field\Base\IdField; use App\Scout\Elasticsearch\Api\Field\Field; @@ -14,11 +14,11 @@ use App\Scout\Elasticsearch\Api\Field\Wiki\Anime\Synonym\SynonymTypeField; use App\Scout\Elasticsearch\Api\Schema\Schema; use App\Scout\Elasticsearch\Api\Schema\Wiki\AnimeSchema; -class SynonymSchema extends Schema +class AnimeSynonymSchema extends Schema { public function type(): string { - return SynonymJsonResource::$wrap; + return AnimeSynonymJsonResource::$wrap; } /** @@ -45,9 +45,4 @@ class SynonymSchema extends Schema ], ); } - - public function model(): AnimeSynonym - { - return new AnimeSynonym(); - } } diff --git a/app/Scout/Elasticsearch/Api/Schema/Wiki/AnimeSchema.php b/app/Scout/Elasticsearch/Api/Schema/Wiki/AnimeSchema.php index 98c24e5c1..e6ac6dfdf 100644 --- a/app/Scout/Elasticsearch/Api/Schema/Wiki/AnimeSchema.php +++ b/app/Scout/Elasticsearch/Api/Schema/Wiki/AnimeSchema.php @@ -21,7 +21,7 @@ use App\Scout\Elasticsearch\Api\Field\Wiki\Anime\AnimeSlugField; use App\Scout\Elasticsearch\Api\Field\Wiki\Anime\AnimeSynopsisField; use App\Scout\Elasticsearch\Api\Field\Wiki\Anime\AnimeYearField; use App\Scout\Elasticsearch\Api\Schema\Schema; -use App\Scout\Elasticsearch\Api\Schema\Wiki\Anime\SynonymSchema; +use App\Scout\Elasticsearch\Api\Schema\Wiki\Anime\AnimeSynonymSchema; use App\Scout\Elasticsearch\Api\Schema\Wiki\Anime\Theme\EntrySchema; use App\Scout\Elasticsearch\Api\Schema\Wiki\Anime\ThemeSchema; @@ -48,7 +48,7 @@ class AnimeSchema extends Schema new AllowedInclude(new SeriesSchema(), Anime::RELATION_SERIES), new AllowedInclude(new SongSchema(), Anime::RELATION_SONG), new AllowedInclude(new StudioSchema(), Anime::RELATION_STUDIOS), - new AllowedInclude(new SynonymSchema(), Anime::RELATION_SYNONYMS), + new AllowedInclude(new AnimeSynonymSchema(), Anime::RELATION_ANIMESYNONYMS), new AllowedInclude(new ThemeSchema(), Anime::RELATION_THEMES), new AllowedInclude(new VideoSchema(), Anime::RELATION_VIDEOS), ]); diff --git a/app/Search/Drivers/ElasticsearchDriver.php b/app/Search/Drivers/ElasticsearchDriver.php index 1ffeaf96d..89aa5bda0 100644 --- a/app/Search/Drivers/ElasticsearchDriver.php +++ b/app/Search/Drivers/ElasticsearchDriver.php @@ -14,10 +14,11 @@ use App\Models\Wiki\Artist; use App\Models\Wiki\Series; use App\Models\Wiki\Song; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Scout\Elasticsearch\Api\Query\ElasticQuery; use App\Scout\Elasticsearch\Api\Query\List\PlaylistQuery; -use App\Scout\Elasticsearch\Api\Query\Wiki\Anime\SynonymQuery; +use App\Scout\Elasticsearch\Api\Query\Wiki\Anime\SynonymQuery as AnimeSynonymQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\Anime\Theme\EntryQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\Anime\ThemeQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\AnimeQuery; @@ -25,6 +26,7 @@ use App\Scout\Elasticsearch\Api\Query\Wiki\ArtistQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\SeriesQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\SongQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\StudioQuery; +use App\Scout\Elasticsearch\Api\Query\Wiki\SynonymQuery; use App\Scout\Elasticsearch\Api\Query\Wiki\VideoQuery; use App\Search\Criteria; use Closure; @@ -137,13 +139,14 @@ class ElasticsearchDriver implements SearchDriver return match ($model) { Playlist::class => new PlaylistQuery(), AnimeThemeEntry::class => new EntryQuery(), - AnimeSynonym::class => new SynonymQuery(), + AnimeSynonym::class => new AnimeSynonymQuery(), AnimeTheme::class => new ThemeQuery(), Anime::class => new AnimeQuery(), Artist::class => new ArtistQuery(), Series::class => new SeriesQuery(), Song::class => new SongQuery(), Studio::class => new StudioQuery(), + Synonym::class => new SynonymQuery(), Video::class => new VideoQuery(), default => throw new RuntimeException("No ElasticQuery defined for model: {$model}"), }; diff --git a/database/factories/Wiki/Anime/AnimeSynonymFactory.php b/database/factories/Wiki/Anime/AnimeSynonymFactory.php deleted file mode 100644 index 02b0f98a7..000000000 --- a/database/factories/Wiki/Anime/AnimeSynonymFactory.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -class AnimeSynonymFactory extends Factory -{ - /** - * The name of the factory's corresponding model. - * - * @var class-string - */ - protected $model = AnimeSynonym::class; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - $type = Arr::random(AnimeSynonymType::cases()); - - return [ - AnimeSynonym::ATTRIBUTE_TEXT => fake()->words(3, true), - AnimeSynonym::ATTRIBUTE_TYPE => $type->value, - ]; - } -} diff --git a/database/factories/Wiki/AnimeFactory.php b/database/factories/Wiki/AnimeFactory.php index dac64e60b..9562b0c6d 100644 --- a/database/factories/Wiki/AnimeFactory.php +++ b/database/factories/Wiki/AnimeFactory.php @@ -7,7 +7,6 @@ namespace Database\Factories\Wiki; use App\Enums\Models\Wiki\AnimeMediaFormat; use App\Enums\Models\Wiki\AnimeSeason; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; use App\Models\Wiki\Anime\AnimeTheme; use App\Models\Wiki\Anime\Theme\AnimeThemeEntry; use App\Models\Wiki\ExternalResource; @@ -63,11 +62,6 @@ class AnimeFactory extends Factory { return $this->afterCreating( function (Anime $anime): void { - AnimeSynonym::factory() - ->for($anime) - ->count(fake()->numberBetween(1, 3)) - ->create(); - AnimeTheme::factory() ->for($anime) ->for(Song::factory()) diff --git a/database/factories/Wiki/SynonymFactory.php b/database/factories/Wiki/SynonymFactory.php new file mode 100644 index 000000000..14a7acd34 --- /dev/null +++ b/database/factories/Wiki/SynonymFactory.php @@ -0,0 +1,47 @@ + + */ +class SynonymFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = Synonym::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $type = Arr::random(SynonymType::cases()); + + return [ + Synonym::ATTRIBUTE_TEXT => fake()->words(3, true), + Synonym::ATTRIBUTE_TYPE => $type->value, + ]; + } + + public function forAnime(): static + { + return $this->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE); + } +} diff --git a/database/migrations/2026_02_18_042241_create_synonyms_table.php b/database/migrations/2026_02_18_042241_create_synonyms_table.php new file mode 100644 index 000000000..3340b8320 --- /dev/null +++ b/database/migrations/2026_02_18_042241_create_synonyms_table.php @@ -0,0 +1,27 @@ +id('synonym_id'); + $table->morphs('synonymable'); + $table->string('text'); + $table->integer('type'); + $table->timestamps(6); + $table->softDeletes(precision: 6); + }); + } + } +}; diff --git a/database/seeders/Auth/Permission/PermissionSeeder.php b/database/seeders/Auth/Permission/PermissionSeeder.php index d5a262b11..6b20374c2 100644 --- a/database/seeders/Auth/Permission/PermissionSeeder.php +++ b/database/seeders/Auth/Permission/PermissionSeeder.php @@ -40,6 +40,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; use Illuminate\Database\Seeder; @@ -100,6 +101,7 @@ class PermissionSeeder extends Seeder $this->registerResource(Series::class, $extendedCrudPermissions); $this->registerResource(Song::class, $extendedCrudPermissions); $this->registerResource(Studio::class, $extendedCrudPermissions); + $this->registerResource(Synonym::class, $extendedCrudPermissions); $this->registerResource(Video::class, $extendedCrudPermissions); $this->registerResource(VideoScript::class, $extendedCrudPermissions); diff --git a/database/seeders/Auth/Role/AdminSeeder.php b/database/seeders/Auth/Role/AdminSeeder.php index 859eb3976..5b6322c5d 100644 --- a/database/seeders/Auth/Role/AdminSeeder.php +++ b/database/seeders/Auth/Role/AdminSeeder.php @@ -41,6 +41,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; use Illuminate\Support\Arr; @@ -108,6 +109,7 @@ class AdminSeeder extends RoleSeeder $this->configureResource($role, Series::class, $extendedCrudPermissions); $this->configureResource($role, Song::class, $extendedCrudPermissions); $this->configureResource($role, Studio::class, $extendedCrudPermissions); + $this->configureResource($role, Synonym::class, $extendedCrudPermissions); $this->configureResource($role, Video::class, $extendedCrudPermissions); $this->configureResource($role, VideoScript::class, $extendedCrudPermissions); diff --git a/database/seeders/Auth/Role/ContentModeratorRoleSeeder.php b/database/seeders/Auth/Role/ContentModeratorRoleSeeder.php index b0ea46dda..e5180e114 100644 --- a/database/seeders/Auth/Role/ContentModeratorRoleSeeder.php +++ b/database/seeders/Auth/Role/ContentModeratorRoleSeeder.php @@ -32,6 +32,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; @@ -91,6 +92,7 @@ class ContentModeratorRoleSeeder extends RoleSeeder $this->configureResource($role, Series::class, $extendedCrudPermissions); $this->configureResource($role, Song::class, $extendedCrudPermissions); $this->configureResource($role, Studio::class, $extendedCrudPermissions); + $this->configureResource($role, Synonym::class, $extendedCrudPermissions); $this->configureResource($role, Video::class, [CrudPermission::VIEW, CrudPermission::UPDATE]); $this->configureResource($role, VideoScript::class, [CrudPermission::VIEW]); diff --git a/database/seeders/Auth/Role/ContributorRoleSeeder.php b/database/seeders/Auth/Role/ContributorRoleSeeder.php index f05952aa9..46a357199 100644 --- a/database/seeders/Auth/Role/ContributorRoleSeeder.php +++ b/database/seeders/Auth/Role/ContributorRoleSeeder.php @@ -31,6 +31,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; @@ -78,6 +79,7 @@ class ContributorRoleSeeder extends RoleSeeder $this->configureResource($role, Series::class, [CrudPermission::VIEW]); $this->configureResource($role, Song::class, [CrudPermission::VIEW]); $this->configureResource($role, Studio::class, [CrudPermission::VIEW]); + $this->configureResource($role, Synonym::class, [CrudPermission::VIEW]); $this->configureResource($role, Video::class, [CrudPermission::VIEW]); $this->configureResource($role, VideoScript::class, [CrudPermission::VIEW]); diff --git a/database/seeders/Auth/Role/DeveloperRoleSeeder.php b/database/seeders/Auth/Role/DeveloperRoleSeeder.php index 787fc1cb8..516f033c5 100644 --- a/database/seeders/Auth/Role/DeveloperRoleSeeder.php +++ b/database/seeders/Auth/Role/DeveloperRoleSeeder.php @@ -31,6 +31,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; @@ -78,6 +79,7 @@ class DeveloperRoleSeeder extends RoleSeeder $this->configureResource($role, Series::class, [CrudPermission::VIEW]); $this->configureResource($role, Song::class, [CrudPermission::VIEW]); $this->configureResource($role, Studio::class, [CrudPermission::VIEW]); + $this->configureResource($role, Synonym::class, [CrudPermission::VIEW]); $this->configureResource($role, Video::class, [CrudPermission::VIEW]); $this->configureResource($role, VideoScript::class, [CrudPermission::VIEW]); diff --git a/database/seeders/Auth/Role/EncoderRoleSeeder.php b/database/seeders/Auth/Role/EncoderRoleSeeder.php index 612197648..97d56df3c 100644 --- a/database/seeders/Auth/Role/EncoderRoleSeeder.php +++ b/database/seeders/Auth/Role/EncoderRoleSeeder.php @@ -32,6 +32,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; @@ -91,6 +92,7 @@ class EncoderRoleSeeder extends RoleSeeder $this->configureResource($role, Series::class, $extendedCrudPermissions); $this->configureResource($role, Song::class, $extendedCrudPermissions); $this->configureResource($role, Studio::class, $extendedCrudPermissions); + $this->configureResource($role, Synonym::class, $extendedCrudPermissions); $this->configureResource($role, Video::class, $extendedCrudPermissions); $this->configureResource($role, VideoScript::class, $extendedCrudPermissions); diff --git a/database/seeders/Auth/Role/PanelViewerRoleSeeder.php b/database/seeders/Auth/Role/PanelViewerRoleSeeder.php index 8372374c9..218a9fe8d 100644 --- a/database/seeders/Auth/Role/PanelViewerRoleSeeder.php +++ b/database/seeders/Auth/Role/PanelViewerRoleSeeder.php @@ -31,6 +31,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; @@ -78,6 +79,7 @@ class PanelViewerRoleSeeder extends RoleSeeder $this->configureResource($role, Series::class, [CrudPermission::VIEW]); $this->configureResource($role, Song::class, [CrudPermission::VIEW]); $this->configureResource($role, Studio::class, [CrudPermission::VIEW]); + $this->configureResource($role, Synonym::class, [CrudPermission::VIEW]); $this->configureResource($role, Video::class, [CrudPermission::VIEW]); $this->configureResource($role, VideoScript::class, [CrudPermission::VIEW]); diff --git a/database/seeders/Auth/Role/PatronRoleSeeder.php b/database/seeders/Auth/Role/PatronRoleSeeder.php index 44c8e4ca4..757479329 100644 --- a/database/seeders/Auth/Role/PatronRoleSeeder.php +++ b/database/seeders/Auth/Role/PatronRoleSeeder.php @@ -31,6 +31,7 @@ use App\Models\Wiki\Song; use App\Models\Wiki\Song\Membership; use App\Models\Wiki\Song\Performance; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use App\Models\Wiki\Video\VideoScript; @@ -78,6 +79,7 @@ class PatronRoleSeeder extends RoleSeeder $this->configureResource($role, Series::class, [CrudPermission::VIEW]); $this->configureResource($role, Song::class, [CrudPermission::VIEW]); $this->configureResource($role, Studio::class, [CrudPermission::VIEW]); + $this->configureResource($role, Synonym::class, [CrudPermission::VIEW]); $this->configureResource($role, Video::class, [CrudPermission::VIEW]); $this->configureResource($role, VideoScript::class, [CrudPermission::VIEW]); diff --git a/database/seeders/Scout/ImportModelsSeeder.php b/database/seeders/Scout/ImportModelsSeeder.php index 08a6e0e69..c38d9a97e 100644 --- a/database/seeders/Scout/ImportModelsSeeder.php +++ b/database/seeders/Scout/ImportModelsSeeder.php @@ -13,6 +13,7 @@ use App\Models\Wiki\Artist; use App\Models\Wiki\Series; use App\Models\Wiki\Song; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Seeder; @@ -51,6 +52,7 @@ class ImportModelsSeeder extends Seeder $this->scoutImport(Series::class); $this->scoutImport(Song::class); $this->scoutImport(Studio::class); + $this->scoutImport(Synonym::class); $this->scoutImport(Video::class); } diff --git a/elastic/migrations/2020_12_21_225415_create_anime_index.php b/elastic/migrations/2020_12_21_225415_create_anime_index.php index c159f4ef7..cf77c1fcc 100644 --- a/elastic/migrations/2020_12_21_225415_create_anime_index.php +++ b/elastic/migrations/2020_12_21_225415_create_anime_index.php @@ -41,7 +41,10 @@ final class CreateAnimeIndex implements MigrationInterface ]); $mapping->nested('synonyms', [ 'properties' => [ - 'anime_id' => [ + 'synonymable_type' => [ + 'type' => 'keyword', + ], + 'synonymable_id' => [ 'type' => 'long', ], 'created_at' => [ diff --git a/elastic/migrations/2020_12_22_034505_create_entry_index.php b/elastic/migrations/2020_12_22_034505_create_entry_index.php index e578826b4..ce6350d0f 100644 --- a/elastic/migrations/2020_12_22_034505_create_entry_index.php +++ b/elastic/migrations/2020_12_22_034505_create_entry_index.php @@ -65,7 +65,10 @@ final class CreateEntryIndex implements MigrationInterface 'synonyms' => [ 'type' => 'nested', 'properties' => [ - 'anime_id' => [ + 'synonymable_type' => [ + 'type' => 'keyword', + ], + 'synonymable_id' => [ 'type' => 'long', ], 'created_at' => [ diff --git a/elastic/migrations/2020_12_22_034544_create_series_index.php b/elastic/migrations/2020_12_22_034544_create_series_index.php index 2fafeec2d..e4ab73b1a 100644 --- a/elastic/migrations/2020_12_22_034544_create_series_index.php +++ b/elastic/migrations/2020_12_22_034544_create_series_index.php @@ -60,7 +60,10 @@ final class CreateSeriesIndex implements MigrationInterface 'synonyms' => [ 'type' => 'nested', 'properties' => [ - 'anime_id' => [ + 'synonymable_type' => [ + 'type' => 'keyword', + ], + 'synonymable_id' => [ 'type' => 'long', ], 'created_at' => [ diff --git a/elastic/migrations/2020_12_22_034553_create_synonym_index.php b/elastic/migrations/2020_12_22_034553_create_synonym_index.php index 0364763b6..c68e68b36 100644 --- a/elastic/migrations/2020_12_22_034553_create_synonym_index.php +++ b/elastic/migrations/2020_12_22_034553_create_synonym_index.php @@ -8,7 +8,7 @@ use Elastic\Adapter\Indices\Settings; use Elastic\Migrations\Facades\Index; use Elastic\Migrations\MigrationInterface; -final class CreateSynonymIndex implements MigrationInterface +final class CreateAnimeSynonymIndex implements MigrationInterface { use ConfiguresTextAnalyzers; diff --git a/elastic/migrations/2020_12_22_034557_create_theme_index.php b/elastic/migrations/2020_12_22_034557_create_theme_index.php index 0f53caeb7..a6ef43cb7 100644 --- a/elastic/migrations/2020_12_22_034557_create_theme_index.php +++ b/elastic/migrations/2020_12_22_034557_create_theme_index.php @@ -42,7 +42,10 @@ final class CreateThemeIndex implements MigrationInterface 'synonyms' => [ 'type' => 'nested', 'properties' => [ - 'anime_id' => [ + 'synonymable_type' => [ + 'type' => 'keyword', + ], + 'synonymable_id' => [ 'type' => 'long', ], 'created_at' => [ diff --git a/elastic/migrations/2020_12_22_034601_create_video_index.php b/elastic/migrations/2020_12_22_034601_create_video_index.php index 6ed72092d..1d6690705 100644 --- a/elastic/migrations/2020_12_22_034601_create_video_index.php +++ b/elastic/migrations/2020_12_22_034601_create_video_index.php @@ -76,7 +76,10 @@ final class CreateVideoIndex implements MigrationInterface 'synonyms' => [ 'type' => 'nested', 'properties' => [ - 'anime_id' => [ + 'synonymable_type' => [ + 'type' => 'keyword', + ], + 'synonymable_id' => [ 'type' => 'long', ], 'created_at' => [ diff --git a/elastic/migrations/2026_02_18_163943_create_synonym_index.php b/elastic/migrations/2026_02_18_163943_create_synonym_index.php new file mode 100644 index 000000000..ed5099463 --- /dev/null +++ b/elastic/migrations/2026_02_18_163943_create_synonym_index.php @@ -0,0 +1,47 @@ +configureTextAnalyzers($settings); + + $mapping->keyword('synonymable_type'); + $mapping->long('synonymable_id'); + $mapping->date('created_at'); + $mapping->long('synonym_id'); + $mapping->text('text', [ + 'analyzer' => 'name_search', + 'fields' => [ + 'keyword' => [ + 'type' => 'keyword', + ], + ], + ]); + $mapping->long('type'); + $mapping->date('updated_at'); + }); + } + + /** + * Reverse the migration. + */ + public function down(): void + { + Index::dropIfExists('synonyms'); + } +} diff --git a/lang/en/filament.php b/lang/en/filament.php index 0c00bb4e3..58064e057 100644 --- a/lang/en/filament.php +++ b/lang/en/filament.php @@ -534,16 +534,6 @@ return [ 'finished_at' => 'Finished At', 'exception' => 'Exception', ], - 'anime_synonym' => [ - 'text' => [ - 'help' => 'For alternative titles, licensed titles, common abbreviations and/or shortenings.', - 'name' => 'Text', - ], - 'type' => [ - 'help' => 'The title type.', - 'name' => 'Type', - ], - ], 'anime_theme_entry' => [ 'episodes' => [ 'help' => 'The range(s) of episodes that the theme entry is used. Can be left blank if used for all episodes or if there are not episodes as with movies. Ex: "1-", "1-11", "1-2, 10, 12".', @@ -906,6 +896,16 @@ return [ 'moderator_notes' => 'Moderator Notes', 'notes' => 'Notes', ], + 'synonym' => [ + 'text' => [ + 'help' => 'For alternative titles, licensed titles, common abbreviations and/or shortenings.', + 'name' => 'Text', + ], + 'type' => [ + 'help' => 'The title type.', + 'name' => 'Type', + ], + ], 'resourceable' => [ 'as' => [ 'help' => 'Used to distinguish resources that map to the same model. For example, Aware! Meisaku-kun has one MAL page and many aniDB pages.', @@ -1032,7 +1032,6 @@ return [ 'resources' => [ 'label' => [ 'action_logs' => 'Action Logs', - 'anime_synonyms' => 'Anime Synonyms', 'anime_theme_entries' => 'Anime Theme Entries', 'anime_themes' => 'Anime Themes', 'anime' => 'Anime', @@ -1064,13 +1063,13 @@ return [ 'series' => 'Series', 'songs' => 'Songs', 'studios' => 'Studios', + 'synonyms' => 'Synonyms', 'users' => 'Users', 'video_scripts' => 'Video Scripts', 'videos' => 'Videos', ], 'singularLabel' => [ 'action_log' => 'Action Log', - 'anime_synonym' => 'Anime Synonym', 'anime_theme_entry' => 'Anime Theme Entry', 'anime_theme' => 'Anime Theme', 'anime' => 'Anime', @@ -1102,6 +1101,7 @@ return [ 'series' => 'Series', 'song' => 'Song', 'studio' => 'Studio', + 'synonym' => 'Synonym', 'user' => 'User', 'video_script' => 'Video Script', 'video' => 'Video', diff --git a/routes/api.php b/routes/api.php index 2963f6fd5..08ff85c84 100644 --- a/routes/api.php +++ b/routes/api.php @@ -26,7 +26,7 @@ use App\Http\Controllers\Api\Pivot\Wiki\AnimeThemeEntryVideoController; use App\Http\Controllers\Api\Pivot\Wiki\ArtistMemberController; use App\Http\Controllers\Api\Pivot\Wiki\ArtistSongController; use App\Http\Controllers\Api\SearchController; -use App\Http\Controllers\Api\Wiki\Anime\SynonymController; +use App\Http\Controllers\Api\Wiki\Anime\AnimeSynonymController; use App\Http\Controllers\Api\Wiki\Anime\Theme\EntryController; use App\Http\Controllers\Api\Wiki\Anime\ThemeController; use App\Http\Controllers\Api\Wiki\Anime\YearController; @@ -41,6 +41,7 @@ use App\Http\Controllers\Api\Wiki\Song\MembershipController; use App\Http\Controllers\Api\Wiki\Song\PerformanceController; use App\Http\Controllers\Api\Wiki\SongController; use App\Http\Controllers\Api\Wiki\StudioController; +use App\Http\Controllers\Api\Wiki\SynonymController; use App\Http\Controllers\Api\Wiki\Video\ScriptController; use App\Http\Controllers\Api\Wiki\VideoController; use Illuminate\Support\Facades\Route; @@ -247,10 +248,11 @@ Route::get('search', [SearchController::class, 'show'])->name('search.show'); apiResource('series', SeriesController::class); apiResource('song', SongController::class); apiResource('studio', StudioController::class); +apiResource('synonym', SynonymController::class); apiResource('video', VideoController::class); // Anime Routes -apiResource('animesynonym', SynonymController::class); +apiResource('animesynonym', AnimeSynonymController::class); apiResource('animetheme', ThemeController::class); Route::get('animeyear', [YearController::class, 'index'])->name('animeyear.index'); Route::get('animeyear/{year}', [YearController::class, 'show'])->name('animeyear.show'); diff --git a/tests/Feature/Events/Wiki/Anime/SynonymTest.php b/tests/Feature/Events/Wiki/SynonymTest.php similarity index 58% rename from tests/Feature/Events/Wiki/Anime/SynonymTest.php rename to tests/Feature/Events/Wiki/SynonymTest.php index e171b46d7..82ef00a0f 100644 --- a/tests/Feature/Events/Wiki/Anime/SynonymTest.php +++ b/tests/Feature/Events/Wiki/SynonymTest.php @@ -2,26 +2,26 @@ declare(strict_types=1); -use App\Events\Wiki\Anime\Synonym\SynonymCreated; -use App\Events\Wiki\Anime\Synonym\SynonymDeleted; -use App\Events\Wiki\Anime\Synonym\SynonymRestored; -use App\Events\Wiki\Anime\Synonym\SynonymUpdated; +use App\Events\Wiki\Synonym\SynonymCreated; +use App\Events\Wiki\Synonym\SynonymDeleted; +use App\Events\Wiki\Synonym\SynonymRestored; +use App\Events\Wiki\Synonym\SynonymUpdated; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Models\Wiki\Synonym; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Event; test('synonym created event dispatched', function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->createOne(); Event::assertDispatched(SynonymCreated::class); }); test('synonym deleted event dispatched', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) + $synonym = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->createOne(); $synonym->delete(); @@ -30,8 +30,8 @@ test('synonym deleted event dispatched', function () { }); test('synonym restored event dispatched', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) + $synonym = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->createOne(); $synonym->restore(); @@ -40,8 +40,8 @@ test('synonym restored event dispatched', function () { }); test('synonym restores quietly', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) + $synonym = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->createOne(); $synonym->restore(); @@ -50,12 +50,12 @@ test('synonym restores quietly', function () { }); test('synonym updated event dispatched', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) + $synonym = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->createOne(); - $changes = AnimeSynonym::factory() - ->for(Anime::factory()) + $changes = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->makeOne(); $synonym->fill($changes->getAttributes()); @@ -65,12 +65,12 @@ test('synonym updated event dispatched', function () { }); test('synonym updated event embed fields', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) + $synonym = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->createOne(); - $changes = AnimeSynonym::factory() - ->for(Anime::factory()) + $changes = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) ->makeOne(); $synonym->fill($changes->getAttributes()); diff --git a/tests/Unit/Filament/Resources/Admin/AnnouncementTest.php b/tests/Feature/Filament/Resources/Admin/AnnouncementTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Admin/AnnouncementTest.php rename to tests/Feature/Filament/Resources/Admin/AnnouncementTest.php diff --git a/tests/Unit/Filament/Resources/Admin/DumpTest.php b/tests/Feature/Filament/Resources/Admin/DumpTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Admin/DumpTest.php rename to tests/Feature/Filament/Resources/Admin/DumpTest.php diff --git a/tests/Unit/Filament/Resources/Admin/FeatureTest.php b/tests/Feature/Filament/Resources/Admin/FeatureTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Admin/FeatureTest.php rename to tests/Feature/Filament/Resources/Admin/FeatureTest.php diff --git a/tests/Unit/Filament/Resources/Admin/FeaturedThemeTest.php b/tests/Feature/Filament/Resources/Admin/FeaturedThemeTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Admin/FeaturedThemeTest.php rename to tests/Feature/Filament/Resources/Admin/FeaturedThemeTest.php diff --git a/tests/Unit/Filament/Resources/Discord/DiscordThreadTest.php b/tests/Feature/Filament/Resources/Discord/DiscordThreadTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Discord/DiscordThreadTest.php rename to tests/Feature/Filament/Resources/Discord/DiscordThreadTest.php diff --git a/tests/Unit/Filament/Resources/Document/PageTest.php b/tests/Feature/Filament/Resources/Document/PageTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Document/PageTest.php rename to tests/Feature/Filament/Resources/Document/PageTest.php diff --git a/tests/Unit/Filament/Resources/List/External/ExternalEntryTest.php b/tests/Feature/Filament/Resources/List/External/ExternalEntryTest.php similarity index 100% rename from tests/Unit/Filament/Resources/List/External/ExternalEntryTest.php rename to tests/Feature/Filament/Resources/List/External/ExternalEntryTest.php diff --git a/tests/Unit/Filament/Resources/List/ExternalProfileTest.php b/tests/Feature/Filament/Resources/List/ExternalProfileTest.php similarity index 100% rename from tests/Unit/Filament/Resources/List/ExternalProfileTest.php rename to tests/Feature/Filament/Resources/List/ExternalProfileTest.php diff --git a/tests/Unit/Filament/Resources/List/Playlist/TrackTest.php b/tests/Feature/Filament/Resources/List/Playlist/TrackTest.php similarity index 100% rename from tests/Unit/Filament/Resources/List/Playlist/TrackTest.php rename to tests/Feature/Filament/Resources/List/Playlist/TrackTest.php diff --git a/tests/Unit/Filament/Resources/List/PlaylistTest.php b/tests/Feature/Filament/Resources/List/PlaylistTest.php similarity index 100% rename from tests/Unit/Filament/Resources/List/PlaylistTest.php rename to tests/Feature/Filament/Resources/List/PlaylistTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/Anime/Theme/EntryTest.php b/tests/Feature/Filament/Resources/Wiki/Anime/Theme/EntryTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/Anime/Theme/EntryTest.php rename to tests/Feature/Filament/Resources/Wiki/Anime/Theme/EntryTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/Anime/ThemeTest.php b/tests/Feature/Filament/Resources/Wiki/Anime/ThemeTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/Anime/ThemeTest.php rename to tests/Feature/Filament/Resources/Wiki/Anime/ThemeTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/AnimeTest.php b/tests/Feature/Filament/Resources/Wiki/AnimeTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/AnimeTest.php rename to tests/Feature/Filament/Resources/Wiki/AnimeTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/ArtistTest.php b/tests/Feature/Filament/Resources/Wiki/ArtistTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/ArtistTest.php rename to tests/Feature/Filament/Resources/Wiki/ArtistTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/AudioTest.php b/tests/Feature/Filament/Resources/Wiki/AudioTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/AudioTest.php rename to tests/Feature/Filament/Resources/Wiki/AudioTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/ExternalResourceTest.php b/tests/Feature/Filament/Resources/Wiki/ExternalResourceTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/ExternalResourceTest.php rename to tests/Feature/Filament/Resources/Wiki/ExternalResourceTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/GroupTest.php b/tests/Feature/Filament/Resources/Wiki/GroupTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/GroupTest.php rename to tests/Feature/Filament/Resources/Wiki/GroupTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/ImageTest.php b/tests/Feature/Filament/Resources/Wiki/ImageTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/ImageTest.php rename to tests/Feature/Filament/Resources/Wiki/ImageTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/SeriesTest.php b/tests/Feature/Filament/Resources/Wiki/SeriesTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/SeriesTest.php rename to tests/Feature/Filament/Resources/Wiki/SeriesTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/Song/MembershipTest.php b/tests/Feature/Filament/Resources/Wiki/Song/MembershipTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/Song/MembershipTest.php rename to tests/Feature/Filament/Resources/Wiki/Song/MembershipTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/Song/PerformanceTest.php b/tests/Feature/Filament/Resources/Wiki/Song/PerformanceTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/Song/PerformanceTest.php rename to tests/Feature/Filament/Resources/Wiki/Song/PerformanceTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/SongTest.php b/tests/Feature/Filament/Resources/Wiki/SongTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/SongTest.php rename to tests/Feature/Filament/Resources/Wiki/SongTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/StudioTest.php b/tests/Feature/Filament/Resources/Wiki/StudioTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/StudioTest.php rename to tests/Feature/Filament/Resources/Wiki/StudioTest.php diff --git a/tests/Feature/Filament/Resources/Wiki/SynonymTest.php b/tests/Feature/Filament/Resources/Wiki/SynonymTest.php new file mode 100644 index 000000000..78595818f --- /dev/null +++ b/tests/Feature/Filament/Resources/Wiki/SynonymTest.php @@ -0,0 +1,117 @@ +withPermissions( + SpecialPermission::VIEW_FILAMENT->value, + CrudPermission::VIEW->format(Synonym::class) + ) + ->createOne(); + + actingAs($user); + + $records = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->count(10)->create(); + + get(SynonymResource::getUrl('index')) + ->assertSuccessful(); + + Livewire::test(getIndexPage(SynonymResource::class)) + ->assertCanSeeTableRecords($records); +}); + +test('render view page', function () { + $user = User::factory() + ->withPermissions( + SpecialPermission::VIEW_FILAMENT->value, + CrudPermission::VIEW->format(Synonym::class) + ) + ->createOne(); + + actingAs($user); + + $record = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->createOne(); + + get(SynonymResource::getUrl('view', ['record' => $record])) + ->assertSuccessful(); +}); + +test('mount edit action', function () { + $user = User::factory() + ->withPermissions( + SpecialPermission::VIEW_FILAMENT->value, + CrudPermission::UPDATE->format(Synonym::class) + ) + ->createOne(); + + actingAs($user); + + $record = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->createOne(); + + Livewire::test(getIndexPage(SynonymResource::class)) + ->mountAction(TestAction::make(EditAction::getDefaultName())->table($record)) + ->callMountedAction() + ->assertHasNoErrors(); +}); + +test('user cannot edit record', function () { + $record = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->createOne(); + + Livewire::test(getIndexPage(SynonymResource::class)) + ->assertActionDoesNotExist(TestAction::make(EditAction::getDefaultName())->table($record)); +}); + +test('user cannot delete record', function () { + $record = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->createOne(); + + Livewire::test(getIndexPage(SynonymResource::class)) + ->assertActionDoesNotExist(TestAction::make(DeleteAction::getDefaultName())->table($record)); +}); + +test('user cannot restore record', function () { + $record = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->createOne(); + + $record->delete(); + + Livewire::test(getIndexPage(SynonymResource::class)) + ->filterTable('trashed', 0) + ->assertActionDoesNotExist(TestAction::make(RestoreAction::getDefaultName())->table($record)); +}); + +test('user cannot force delete record', function () { + $record = Synonym::factory() + ->for(Anime::factory(), Synonym::RELATION_SYNONYMABLE) + ->createOne(); + + Livewire::test(getIndexPage(SynonymResource::class)) + ->assertActionDoesNotExist(TestAction::make(ForceDeleteAction::getDefaultName())->table($record)); +}); diff --git a/tests/Unit/Filament/Resources/Wiki/Video/ScriptTest.php b/tests/Feature/Filament/Resources/Wiki/Video/ScriptTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/Video/ScriptTest.php rename to tests/Feature/Filament/Resources/Wiki/Video/ScriptTest.php diff --git a/tests/Unit/Filament/Resources/Wiki/VideoTest.php b/tests/Feature/Filament/Resources/Wiki/VideoTest.php similarity index 100% rename from tests/Unit/Filament/Resources/Wiki/VideoTest.php rename to tests/Feature/Filament/Resources/Wiki/VideoTest.php diff --git a/tests/Feature/Http/Api/Wiki/Anime/AnimeIndexTest.php b/tests/Feature/Http/Api/Wiki/Anime/AnimeIndexTest.php index 756782412..898c1d90e 100644 --- a/tests/Feature/Http/Api/Wiki/Anime/AnimeIndexTest.php +++ b/tests/Feature/Http/Api/Wiki/Anime/AnimeIndexTest.php @@ -8,9 +8,9 @@ use App\Enums\Http\Api\Filter\TrashedStatus; use App\Enums\Http\Api\Sort\Direction; use App\Enums\Models\Wiki\AnimeMediaFormat; use App\Enums\Models\Wiki\AnimeSeason; -use App\Enums\Models\Wiki\AnimeSynonymType; use App\Enums\Models\Wiki\ImageFacet; use App\Enums\Models\Wiki\ResourceSite; +use App\Enums\Models\Wiki\SynonymType; use App\Enums\Models\Wiki\ThemeType; use App\Enums\Models\Wiki\VideoOverlap; use App\Enums\Models\Wiki\VideoSource; @@ -27,17 +27,17 @@ use App\Http\Api\Parser\SortParser; use App\Http\Api\Query\Query; use App\Http\Api\Schema\Wiki\AnimeSchema; use App\Http\Api\Sort\Sort; -use App\Http\Resources\Wiki\Anime\Resource\SynonymJsonResource; use App\Http\Resources\Wiki\Anime\Resource\ThemeJsonResource; use App\Http\Resources\Wiki\Collection\AnimeCollection; use App\Http\Resources\Wiki\Resource\AnimeJsonResource; +use App\Http\Resources\Wiki\Resource\SynonymJsonResource; use App\Models\BaseModel; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; use App\Models\Wiki\Anime\AnimeTheme; use App\Models\Wiki\Anime\Theme\AnimeThemeEntry; use App\Models\Wiki\ExternalResource; use App\Models\Wiki\Image; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use Illuminate\Database\Eloquent\Factories\Sequence; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -461,25 +461,25 @@ test('deleted at filter', function () { }); test('synonyms by type', function () { - $typeFilter = Arr::random(AnimeSynonymType::cases()); + $typeFilter = Arr::random(SynonymType::cases()); $parameters = [ FilterParser::param() => [ SynonymJsonResource::$wrap => [ - AnimeSynonym::ATTRIBUTE_TYPE => $typeFilter->localize(), + Synonym::ATTRIBUTE_TYPE => $typeFilter->localize(), ], ], - IncludeParser::param() => Anime::RELATION_SYNONYMS, + IncludeParser::param() => Anime::RELATION_ANIMESYNONYMS, ]; Anime::factory() - ->has(AnimeSynonym::factory()->count(fake()->randomDigitNotNull())) + ->has(Synonym::factory()->count(fake()->randomDigitNotNull())) ->count(fake()->randomDigitNotNull()) ->create(); $anime = Anime::with([ - Anime::RELATION_SYNONYMS => function (HasMany $query) use ($typeFilter) { - $query->where(AnimeSynonym::ATTRIBUTE_TYPE, $typeFilter->value); + Anime::RELATION_ANIMESYNONYMS => function (HasMany $query) use ($typeFilter) { + $query->where(Synonym::ATTRIBUTE_TYPE, $typeFilter->value); }, ]) ->get(); diff --git a/tests/Feature/Http/Api/Wiki/Anime/AnimeShowTest.php b/tests/Feature/Http/Api/Wiki/Anime/AnimeShowTest.php index 003353ee3..593fc882f 100644 --- a/tests/Feature/Http/Api/Wiki/Anime/AnimeShowTest.php +++ b/tests/Feature/Http/Api/Wiki/Anime/AnimeShowTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); -use App\Enums\Models\Wiki\AnimeSynonymType; use App\Enums\Models\Wiki\ImageFacet; use App\Enums\Models\Wiki\ResourceSite; +use App\Enums\Models\Wiki\SynonymType; use App\Enums\Models\Wiki\ThemeType; use App\Enums\Models\Wiki\VideoOverlap; use App\Enums\Models\Wiki\VideoSource; @@ -15,15 +15,15 @@ use App\Http\Api\Parser\FilterParser; use App\Http\Api\Parser\IncludeParser; use App\Http\Api\Query\Query; use App\Http\Api\Schema\Wiki\AnimeSchema; -use App\Http\Resources\Wiki\Anime\Resource\SynonymJsonResource; use App\Http\Resources\Wiki\Anime\Resource\ThemeJsonResource; use App\Http\Resources\Wiki\Resource\AnimeJsonResource; +use App\Http\Resources\Wiki\Resource\SynonymJsonResource; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; use App\Models\Wiki\Anime\AnimeTheme; use App\Models\Wiki\Anime\Theme\AnimeThemeEntry; use App\Models\Wiki\ExternalResource; use App\Models\Wiki\Image; +use App\Models\Wiki\Synonym; use App\Models\Wiki\Video; use Illuminate\Database\Eloquent\Factories\Sequence; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -129,24 +129,24 @@ test('sparse fieldsets', function () { }); test('synonyms by type', function () { - $typeFilter = Arr::random(AnimeSynonymType::cases()); + $typeFilter = Arr::random(SynonymType::cases()); $parameters = [ FilterParser::param() => [ SynonymJsonResource::$wrap => [ - AnimeSynonym::ATTRIBUTE_TYPE => $typeFilter->localize(), + Synonym::ATTRIBUTE_TYPE => $typeFilter->localize(), ], ], - IncludeParser::param() => Anime::RELATION_SYNONYMS, + IncludeParser::param() => Anime::RELATION_ANIMESYNONYMS, ]; $anime = Anime::factory() - ->has(AnimeSynonym::factory()->count(fake()->randomDigitNotNull())) + ->has(Synonym::factory()->count(fake()->randomDigitNotNull())) ->createOne(); $anime->unsetRelations()->load([ - Anime::RELATION_SYNONYMS => function (HasMany $query) use ($typeFilter) { - $query->where(AnimeSynonym::ATTRIBUTE_TYPE, $typeFilter->value); + Anime::RELATION_ANIMESYNONYMS => function (HasMany $query) use ($typeFilter) { + $query->where(Synonym::ATTRIBUTE_TYPE, $typeFilter->value); }, ]); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymDestroyTest.php b/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymDestroyTest.php deleted file mode 100644 index d123b7f1f..000000000 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymDestroyTest.php +++ /dev/null @@ -1,59 +0,0 @@ -for(Anime::factory())->createOne(); - - $response = delete(route('api.animesynonym.destroy', ['animesynonym' => $synonym])); - - $response->assertUnauthorized(); -}); - -test('forbidden', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $user = User::factory()->createOne(); - - Sanctum::actingAs($user); - - $response = delete(route('api.animesynonym.destroy', ['animesynonym' => $synonym])); - - $response->assertForbidden(); -}); - -test('trashed', function () { - $synonym = AnimeSynonym::factory() - ->trashed() - ->for(Anime::factory()) - ->createOne(); - - $user = User::factory()->withPermissions(CrudPermission::DELETE->format(AnimeSynonym::class))->createOne(); - - Sanctum::actingAs($user); - - $response = delete(route('api.animesynonym.destroy', ['animesynonym' => $synonym])); - - $response->assertNotFound(); -}); - -test('deleted', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $user = User::factory()->withPermissions(CrudPermission::DELETE->format(AnimeSynonym::class))->createOne(); - - Sanctum::actingAs($user); - - $response = delete(route('api.animesynonym.destroy', ['animesynonym' => $synonym])); - - $response->assertOk(); - $this->assertSoftDeleted($synonym); -}); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymForceDeleteTest.php b/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymForceDeleteTest.php deleted file mode 100644 index 8ba37eba2..000000000 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymForceDeleteTest.php +++ /dev/null @@ -1,44 +0,0 @@ -for(Anime::factory())->createOne(); - - $response = delete(route('api.animesynonym.forceDelete', ['animesynonym' => $synonym])); - - $response->assertUnauthorized(); -}); - -test('forbidden', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $user = User::factory()->createOne(); - - Sanctum::actingAs($user); - - $response = delete(route('api.animesynonym.forceDelete', ['animesynonym' => $synonym])); - - $response->assertForbidden(); -}); - -test('deleted', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $user = User::factory()->withPermissions(ExtendedCrudPermission::FORCE_DELETE->format(AnimeSynonym::class))->createOne(); - - Sanctum::actingAs($user); - - $response = delete(route('api.animesynonym.forceDelete', ['animesynonym' => $synonym])); - - $response->assertOk(); - $this->assertModelMissing($synonym); -}); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymShowTest.php b/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymShowTest.php deleted file mode 100644 index 8971aa14b..000000000 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymShowTest.php +++ /dev/null @@ -1,227 +0,0 @@ -for(Anime::factory())->createOne(); - - $synonym->unsetRelations(); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym])); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query()) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('soft delete', function () { - $synonym = AnimeSynonym::factory() - ->trashed() - ->for(Anime::factory()) - ->createOne(); - - $synonym->unsetRelations(); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym])); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query()) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('allowed include paths', function () { - $schema = new SynonymSchema(); - - $allowedIncludes = collect($schema->allowedIncludes()); - - $selectedIncludes = $allowedIncludes->random(fake()->numberBetween(1, $allowedIncludes->count())); - - $includedPaths = $selectedIncludes->map(fn (AllowedInclude $include) => $include->path()); - - $parameters = [ - IncludeParser::param() => $includedPaths->join(','), - ]; - - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym] + $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('sparse fieldsets', function () { - $schema = new SynonymSchema(); - - $fields = collect($schema->fields()); - - $includedFields = $fields->random(fake()->numberBetween(1, $fields->count())); - - $parameters = [ - FieldParser::param() => [ - SynonymJsonResource::$wrap => $includedFields->map(fn (Field $field) => $field->getKey())->join(','), - ], - ]; - - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $synonym->unsetRelations(); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym] + $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('anime by media format', function () { - $mediaFormatFilter = Arr::random(AnimeMediaFormat::cases()); - - $parameters = [ - FilterParser::param() => [ - Anime::ATTRIBUTE_MEDIA_FORMAT => $mediaFormatFilter->localize(), - ], - IncludeParser::param() => AnimeSynonym::RELATION_ANIME, - ]; - - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $synonym->unsetRelations()->load([ - AnimeSynonym::RELATION_ANIME => function (BelongsTo $query) use ($mediaFormatFilter) { - $query->where(Anime::ATTRIBUTE_MEDIA_FORMAT, $mediaFormatFilter->value); - }, - ]); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym] + $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('anime by season', function () { - $seasonFilter = Arr::random(AnimeSeason::cases()); - - $parameters = [ - FilterParser::param() => [ - Anime::ATTRIBUTE_SEASON => $seasonFilter->localize(), - ], - IncludeParser::param() => AnimeSynonym::RELATION_ANIME, - ]; - - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $synonym->unsetRelations()->load([ - AnimeSynonym::RELATION_ANIME => function (BelongsTo $query) use ($seasonFilter) { - $query->where(Anime::ATTRIBUTE_SEASON, $seasonFilter->value); - }, - ]); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym] + $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('anime by year', function () { - $yearFilter = intval(fake()->year()); - $excludedYear = $yearFilter + 1; - - $parameters = [ - FilterParser::param() => [ - Anime::ATTRIBUTE_YEAR => $yearFilter, - ], - IncludeParser::param() => AnimeSynonym::RELATION_ANIME, - ]; - - $synonym = AnimeSynonym::factory() - ->for( - Anime::factory() - ->state([ - Anime::ATTRIBUTE_YEAR => fake()->boolean() ? $yearFilter : $excludedYear, - ]) - ) - ->createOne(); - - $synonym->unsetRelations()->load([ - AnimeSynonym::RELATION_ANIME => function (BelongsTo $query) use ($yearFilter) { - $query->where(Anime::ATTRIBUTE_YEAR, $yearFilter); - }, - ]); - - $response = get(route('api.animesynonym.show', ['animesynonym' => $synonym] + $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymJsonResource($synonym, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymUpdateTest.php b/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymUpdateTest.php deleted file mode 100644 index 4c77bec52..000000000 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymUpdateTest.php +++ /dev/null @@ -1,66 +0,0 @@ -for(Anime::factory())->createOne(); - - $parameters = AnimeSynonym::factory()->raw(); - - $response = put(route('api.animesynonym.update', ['animesynonym' => $synonym] + $parameters)); - - $response->assertUnauthorized(); -}); - -test('forbidden', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $parameters = AnimeSynonym::factory()->raw(); - - $user = User::factory()->createOne(); - - Sanctum::actingAs($user); - - $response = put(route('api.animesynonym.update', ['animesynonym' => $synonym] + $parameters)); - - $response->assertForbidden(); -}); - -test('trashed', function () { - $synonym = AnimeSynonym::factory() - ->trashed() - ->for(Anime::factory()) - ->createOne(); - - $parameters = AnimeSynonym::factory()->raw(); - - $user = User::factory()->withPermissions(CrudPermission::VIEW->format(AnimeSynonym::class))->createOne(); - - Sanctum::actingAs($user); - - $response = put(route('api.animesynonym.update', ['animesynonym' => $synonym] + $parameters)); - - $response->assertForbidden(); -}); - -test('update', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); - - $parameters = AnimeSynonym::factory()->raw(); - - $user = User::factory()->withPermissions(CrudPermission::UPDATE->format(AnimeSynonym::class))->createOne(); - - Sanctum::actingAs($user); - - $response = put(route('api.animesynonym.update', ['animesynonym' => $synonym] + $parameters)); - - $response->assertOk(); -}); diff --git a/tests/Feature/Http/Api/Wiki/Synonym/SynonymDestroyTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymDestroyTest.php new file mode 100644 index 000000000..b8c191c81 --- /dev/null +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymDestroyTest.php @@ -0,0 +1,58 @@ +forAnime()->createOne(); + + $response = delete(route('api.synonym.destroy', ['synonym' => $synonym])); + + $response->assertUnauthorized(); +}); + +test('forbidden', function () { + $synonym = Synonym::factory()->forAnime()->createOne(); + + $user = User::factory()->createOne(); + + Sanctum::actingAs($user); + + $response = delete(route('api.synonym.destroy', ['synonym' => $synonym])); + + $response->assertForbidden(); +}); + +test('trashed', function () { + $synonym = Synonym::factory() + ->trashed() + ->forAnime() + ->createOne(); + + $user = User::factory()->withPermissions(CrudPermission::DELETE->format(Synonym::class))->createOne(); + + Sanctum::actingAs($user); + + $response = delete(route('api.synonym.destroy', ['synonym' => $synonym])); + + $response->assertNotFound(); +}); + +test('deleted', function () { + $synonym = Synonym::factory()->forAnime()->createOne(); + + $user = User::factory()->withPermissions(CrudPermission::DELETE->format(Synonym::class))->createOne(); + + Sanctum::actingAs($user); + + $response = delete(route('api.synonym.destroy', ['synonym' => $synonym])); + + $response->assertOk(); + $this->assertSoftDeleted($synonym); +}); diff --git a/tests/Feature/Http/Api/Wiki/Synonym/SynonymForceDeleteTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymForceDeleteTest.php new file mode 100644 index 000000000..e4b1b666c --- /dev/null +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymForceDeleteTest.php @@ -0,0 +1,43 @@ +forAnime()->createOne(); + + $response = delete(route('api.synonym.forceDelete', ['synonym' => $synonym])); + + $response->assertUnauthorized(); +}); + +test('forbidden', function () { + $synonym = Synonym::factory()->forAnime()->createOne(); + + $user = User::factory()->createOne(); + + Sanctum::actingAs($user); + + $response = delete(route('api.synonym.forceDelete', ['synonym' => $synonym])); + + $response->assertForbidden(); +}); + +test('deleted', function () { + $synonym = Synonym::factory()->forAnime()->createOne(); + + $user = User::factory()->withPermissions(ExtendedCrudPermission::FORCE_DELETE->format(Synonym::class))->createOne(); + + Sanctum::actingAs($user); + + $response = delete(route('api.synonym.forceDelete', ['synonym' => $synonym])); + + $response->assertOk(); + $this->assertModelMissing($synonym); +}); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymIndexTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymIndexTest.php similarity index 53% rename from tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymIndexTest.php rename to tests/Feature/Http/Api/Wiki/Synonym/SynonymIndexTest.php index 0c33f63f8..2f8b9e08f 100644 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymIndexTest.php +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymIndexTest.php @@ -6,27 +6,21 @@ use App\Constants\ModelConstants; use App\Contracts\Http\Api\Field\SortableField; use App\Enums\Http\Api\Filter\TrashedStatus; use App\Enums\Http\Api\Sort\Direction; -use App\Enums\Models\Wiki\AnimeMediaFormat; -use App\Enums\Models\Wiki\AnimeSeason; use App\Http\Api\Criteria\Filter\TrashedCriteria; use App\Http\Api\Criteria\Paging\Criteria; use App\Http\Api\Criteria\Paging\OffsetCriteria; use App\Http\Api\Field\Field; -use App\Http\Api\Include\AllowedInclude; use App\Http\Api\Parser\FieldParser; use App\Http\Api\Parser\FilterParser; -use App\Http\Api\Parser\IncludeParser; use App\Http\Api\Parser\PagingParser; use App\Http\Api\Parser\SortParser; use App\Http\Api\Query\Query; -use App\Http\Api\Schema\Wiki\Anime\SynonymSchema; +use App\Http\Api\Schema\Wiki\SynonymSchema; use App\Http\Api\Sort\Sort; -use App\Http\Resources\Wiki\Anime\Collection\SynonymCollection; -use App\Http\Resources\Wiki\Anime\Resource\SynonymJsonResource; +use App\Http\Resources\Wiki\Collection\SynonymCollection; +use App\Http\Resources\Wiki\Resource\SynonymJsonResource; use App\Models\BaseModel; -use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; -use Illuminate\Database\Eloquent\Relations\BelongsTo; +use App\Models\Wiki\Synonym; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; @@ -37,14 +31,14 @@ uses(App\Concerns\Actions\Http\Api\SortsModels::class); uses(Illuminate\Foundation\Testing\WithFaker::class); test('default', function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $synonyms = AnimeSynonym::all(); + $synonyms = Synonym::all(); - $response = get(route('api.animesynonym.index')); + $response = get(route('api.synonym.index')); $response->assertJson( json_decode( @@ -59,12 +53,12 @@ test('default', function () { }); test('paginated', function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $response = get(route('api.animesynonym.index')); + $response = get(route('api.synonym.index')); $response->assertJsonStructure([ SynonymCollection::$wrap, @@ -73,40 +67,6 @@ test('paginated', function () { ]); }); -test('allowed include paths', function () { - $schema = new SynonymSchema(); - - $allowedIncludes = collect($schema->allowedIncludes()); - - $selectedIncludes = $allowedIncludes->random(fake()->numberBetween(1, $allowedIncludes->count())); - - $includedPaths = $selectedIncludes->map(fn (AllowedInclude $include) => $include->path()); - - $parameters = [ - IncludeParser::param() => $includedPaths->join(','), - ]; - - AnimeSynonym::factory() - ->for(Anime::factory()) - ->count(fake()->randomDigitNotNull()) - ->create(); - - $synonyms = AnimeSynonym::with($includedPaths->all())->get(); - - $response = get(route('api.animesynonym.index', $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymCollection($synonyms, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - test('sparse fieldsets', function () { $schema = new SynonymSchema(); @@ -120,14 +80,14 @@ test('sparse fieldsets', function () { ], ]; - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $synonyms = AnimeSynonym::all(); + $synonyms = Synonym::all(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -156,14 +116,14 @@ test('sorts', function () { $query = new Query($parameters); - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); - $synonyms = $this->sort(AnimeSynonym::query(), $query, $schema)->get(); + $synonyms = $this->sort(Synonym::query(), $query, $schema)->get(); $response->assertJson( json_decode( @@ -191,22 +151,22 @@ test('created at filter', function () { ]; Carbon::withTestNow($createdFilter, function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); }); Carbon::withTestNow($excludedDate, function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); }); - $synonym = AnimeSynonym::query()->where(BaseModel::ATTRIBUTE_CREATED_AT, $createdFilter)->get(); + $synonym = Synonym::query()->where(BaseModel::ATTRIBUTE_CREATED_AT, $createdFilter)->get(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -234,22 +194,22 @@ test('updated at filter', function () { ]; Carbon::withTestNow($updatedFilter, function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); }); Carbon::withTestNow($excludedDate, function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); }); - $synonym = AnimeSynonym::query()->where(BaseModel::ATTRIBUTE_UPDATED_AT, $updatedFilter)->get(); + $synonym = Synonym::query()->where(BaseModel::ATTRIBUTE_UPDATED_AT, $updatedFilter)->get(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -273,20 +233,20 @@ test('without trashed filter', function () { ], ]; - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - AnimeSynonym::factory() + Synonym::factory() ->trashed() - ->for(Anime::factory()) + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $synonym = AnimeSynonym::withoutTrashed()->get(); + $synonym = Synonym::withoutTrashed()->get(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -310,20 +270,20 @@ test('with trashed filter', function () { ], ]; - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - AnimeSynonym::factory() + Synonym::factory() ->trashed() - ->for(Anime::factory()) + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $synonym = AnimeSynonym::withTrashed()->get(); + $synonym = Synonym::withTrashed()->get(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -347,20 +307,20 @@ test('only trashed filter', function () { ], ]; - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - AnimeSynonym::factory() + Synonym::factory() ->trashed() - ->for(Anime::factory()) + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); - $synonym = AnimeSynonym::onlyTrashed()->get(); + $synonym = Synonym::onlyTrashed()->get(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -389,22 +349,22 @@ test('deleted at filter', function () { ]; Carbon::withTestNow($deletedFilter, function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); }); Carbon::withTestNow($excludedDate, function () { - AnimeSynonym::factory() - ->for(Anime::factory()) + Synonym::factory() + ->forAnime() ->count(fake()->randomDigitNotNull()) ->create(); }); - $synonym = AnimeSynonym::withTrashed()->where(ModelConstants::ATTRIBUTE_DELETED_AT, $deletedFilter)->get(); + $synonym = Synonym::withTrashed()->where(ModelConstants::ATTRIBUTE_DELETED_AT, $deletedFilter)->get(); - $response = get(route('api.animesynonym.index', $parameters)); + $response = get(route('api.synonym.index', $parameters)); $response->assertJson( json_decode( @@ -417,117 +377,3 @@ test('deleted at filter', function () { ) ); }); - -test('anime by media format', function () { - $mediaFormatFilter = Arr::random(AnimeMediaFormat::cases()); - - $parameters = [ - FilterParser::param() => [ - Anime::ATTRIBUTE_MEDIA_FORMAT => $mediaFormatFilter->localize(), - ], - IncludeParser::param() => AnimeSynonym::RELATION_ANIME, - ]; - - AnimeSynonym::factory() - ->for(Anime::factory()) - ->count(fake()->randomDigitNotNull()) - ->create(); - - $synonyms = AnimeSynonym::with([ - AnimeSynonym::RELATION_ANIME => function (BelongsTo $query) use ($mediaFormatFilter) { - $query->where(Anime::ATTRIBUTE_MEDIA_FORMAT, $mediaFormatFilter->value); - }, - ]) - ->get(); - - $response = get(route('api.animesynonym.index', $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymCollection($synonyms, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('anime by season', function () { - $seasonFilter = Arr::random(AnimeSeason::cases()); - - $parameters = [ - FilterParser::param() => [ - Anime::ATTRIBUTE_SEASON => $seasonFilter->localize(), - ], - IncludeParser::param() => AnimeSynonym::RELATION_ANIME, - ]; - - AnimeSynonym::factory() - ->for(Anime::factory()) - ->count(fake()->randomDigitNotNull()) - ->create(); - - $synonyms = AnimeSynonym::with([ - AnimeSynonym::RELATION_ANIME => function (BelongsTo $query) use ($seasonFilter) { - $query->where(Anime::ATTRIBUTE_SEASON, $seasonFilter->value); - }, - ]) - ->get(); - - $response = get(route('api.animesynonym.index', $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymCollection($synonyms, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); - -test('anime by year', function () { - $yearFilter = intval(fake()->year()); - $excludedYear = $yearFilter + 1; - - $parameters = [ - FilterParser::param() => [ - Anime::ATTRIBUTE_YEAR => $yearFilter, - ], - IncludeParser::param() => AnimeSynonym::RELATION_ANIME, - ]; - - AnimeSynonym::factory() - ->for( - Anime::factory() - ->state([ - Anime::ATTRIBUTE_YEAR => fake()->boolean() ? $yearFilter : $excludedYear, - ]) - ) - ->count(fake()->randomDigitNotNull()) - ->create(); - - $synonyms = AnimeSynonym::with([ - AnimeSynonym::RELATION_ANIME => function (BelongsTo $query) use ($yearFilter) { - $query->where(Anime::ATTRIBUTE_YEAR, $yearFilter); - }, - ]) - ->get(); - - $response = get(route('api.animesynonym.index', $parameters)); - - $response->assertJson( - json_decode( - json_encode( - new SynonymCollection($synonyms, new Query($parameters)) - ->response() - ->getData() - ), - true - ) - ); -}); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymRestoreTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymRestoreTest.php similarity index 51% rename from tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymRestoreTest.php rename to tests/Feature/Http/Api/Wiki/Synonym/SynonymRestoreTest.php index bce3ee349..7f3bc6aae 100644 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymRestoreTest.php +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymRestoreTest.php @@ -4,61 +4,60 @@ declare(strict_types=1); use App\Enums\Auth\ExtendedCrudPermission; use App\Models\Auth\User; -use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Models\Wiki\Synonym; use Laravel\Sanctum\Sanctum; use function Pest\Laravel\patch; test('protected', function () { - $synonym = AnimeSynonym::factory() + $synonym = Synonym::factory() ->trashed() - ->for(Anime::factory()) + ->forAnime() ->createOne(); - $response = patch(route('api.animesynonym.restore', ['animesynonym' => $synonym])); + $response = patch(route('api.synonym.restore', ['synonym' => $synonym])); $response->assertUnauthorized(); }); test('forbidden', function () { - $synonym = AnimeSynonym::factory() + $synonym = Synonym::factory() ->trashed() - ->for(Anime::factory()) + ->forAnime() ->createOne(); $user = User::factory()->createOne(); Sanctum::actingAs($user); - $response = patch(route('api.animesynonym.restore', ['animesynonym' => $synonym])); + $response = patch(route('api.synonym.restore', ['synonym' => $synonym])); $response->assertForbidden(); }); test('trashed', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->createOne(); + $synonym = Synonym::factory()->forAnime()->createOne(); - $user = User::factory()->withPermissions(ExtendedCrudPermission::RESTORE->format(AnimeSynonym::class))->createOne(); + $user = User::factory()->withPermissions(ExtendedCrudPermission::RESTORE->format(Synonym::class))->createOne(); Sanctum::actingAs($user); - $response = patch(route('api.animesynonym.restore', ['animesynonym' => $synonym])); + $response = patch(route('api.synonym.restore', ['synonym' => $synonym])); $response->assertForbidden(); }); test('restored', function () { - $synonym = AnimeSynonym::factory() + $synonym = Synonym::factory() ->trashed() - ->for(Anime::factory()) + ->forAnime() ->createOne(); - $user = User::factory()->withPermissions(ExtendedCrudPermission::RESTORE->format(AnimeSynonym::class))->createOne(); + $user = User::factory()->withPermissions(ExtendedCrudPermission::RESTORE->format(Synonym::class))->createOne(); Sanctum::actingAs($user); - $response = patch(route('api.animesynonym.restore', ['animesynonym' => $synonym])); + $response = patch(route('api.synonym.restore', ['synonym' => $synonym])); $response->assertOk(); $this->assertNotSoftDeleted($synonym); diff --git a/tests/Feature/Http/Api/Wiki/Synonym/SynonymShowTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymShowTest.php new file mode 100644 index 000000000..5bc990e5a --- /dev/null +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymShowTest.php @@ -0,0 +1,86 @@ +forAnime()->createOne(); + + $synonym->unsetRelations(); + + $response = get(route('api.synonym.show', ['synonym' => $synonym])); + + $response->assertJson( + json_decode( + json_encode( + new SynonymJsonResource($synonym, new Query()) + ->response() + ->getData() + ), + true + ) + ); +}); + +test('soft delete', function () { + $synonym = Synonym::factory() + ->trashed() + ->forAnime() + ->createOne(); + + $synonym->unsetRelations(); + + $response = get(route('api.synonym.show', ['synonym' => $synonym])); + + $response->assertJson( + json_decode( + json_encode( + new SynonymJsonResource($synonym, new Query()) + ->response() + ->getData() + ), + true + ) + ); +}); + +test('sparse fieldsets', function () { + $schema = new SynonymSchema(); + + $fields = collect($schema->fields()); + + $includedFields = $fields->random(fake()->numberBetween(1, $fields->count())); + + $parameters = [ + FieldParser::param() => [ + SynonymJsonResource::$wrap => $includedFields->map(fn (Field $field) => $field->getKey())->join(','), + ], + ]; + + $synonym = Synonym::factory()->forAnime()->createOne(); + + $synonym->unsetRelations(); + + $response = get(route('api.synonym.show', ['synonym' => $synonym] + $parameters)); + + $response->assertJson( + json_decode( + json_encode( + new SynonymJsonResource($synonym, new Query($parameters)) + ->response() + ->getData() + ), + true + ) + ); +}); diff --git a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymStoreTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymStoreTest.php similarity index 52% rename from tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymStoreTest.php rename to tests/Feature/Http/Api/Wiki/Synonym/SynonymStoreTest.php index 99cfce2b3..222c07de0 100644 --- a/tests/Feature/Http/Api/Wiki/Anime/Synonym/SynonymStoreTest.php +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymStoreTest.php @@ -5,40 +5,40 @@ declare(strict_types=1); use App\Enums\Auth\CrudPermission; use App\Models\Auth\User; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Models\Wiki\Synonym; use Laravel\Sanctum\Sanctum; use function Pest\Laravel\post; test('protected', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->makeOne(); + $synonym = Synonym::factory()->forAnime()->makeOne(); - $response = post(route('api.animesynonym.store', $synonym->toArray())); + $response = post(route('api.synonym.store', $synonym->toArray())); $response->assertUnauthorized(); }); test('forbidden', function () { - $synonym = AnimeSynonym::factory()->for(Anime::factory())->makeOne(); + $synonym = Synonym::factory()->forAnime()->makeOne(); $user = User::factory()->createOne(); Sanctum::actingAs($user); - $response = post(route('api.animesynonym.store', $synonym->toArray())); + $response = post(route('api.synonym.store', $synonym->toArray())); $response->assertForbidden(); }); test('required fields', function () { - $user = User::factory()->withPermissions(CrudPermission::CREATE->format(AnimeSynonym::class))->createOne(); + $user = User::factory()->withPermissions(CrudPermission::CREATE->format(Synonym::class))->createOne(); Sanctum::actingAs($user); - $response = post(route('api.animesynonym.store')); + $response = post(route('api.synonym.store')); $response->assertJsonValidationErrors([ - AnimeSynonym::ATTRIBUTE_TEXT, + Synonym::ATTRIBUTE_TEXT, ]); }); @@ -46,16 +46,19 @@ test('create', function () { $anime = Anime::factory()->createOne(); $parameters = array_merge( - AnimeSynonym::factory()->raw(), - [AnimeSynonym::ATTRIBUTE_ANIME => $anime->getKey()], + Synonym::factory()->raw(), + [ + Synonym::ATTRIBUTE_SYNONYMABLE_ID => $anime->getKey(), + Synonym::ATTRIBUTE_SYNONYMABLE_TYPE => 'anime', + ], ); - $user = User::factory()->withPermissions(CrudPermission::CREATE->format(AnimeSynonym::class))->createOne(); + $user = User::factory()->withPermissions(CrudPermission::CREATE->format(Synonym::class))->createOne(); Sanctum::actingAs($user); - $response = post(route('api.animesynonym.store', $parameters)); + $response = post(route('api.synonym.store', $parameters)); $response->assertCreated(); - $this->assertDatabaseCount(AnimeSynonym::class, 1); + $this->assertDatabaseCount(Synonym::class, 1); }); diff --git a/tests/Feature/Http/Api/Wiki/Synonym/SynonymUpdateTest.php b/tests/Feature/Http/Api/Wiki/Synonym/SynonymUpdateTest.php new file mode 100644 index 000000000..fc522ed89 --- /dev/null +++ b/tests/Feature/Http/Api/Wiki/Synonym/SynonymUpdateTest.php @@ -0,0 +1,65 @@ +forAnime()->createOne(); + + $parameters = Synonym::factory()->raw(); + + $response = put(route('api.synonym.update', ['synonym' => $synonym] + $parameters)); + + $response->assertUnauthorized(); +}); + +test('forbidden', function () { + $synonym = Synonym::factory()->forAnime()->createOne(); + + $parameters = Synonym::factory()->raw(); + + $user = User::factory()->createOne(); + + Sanctum::actingAs($user); + + $response = put(route('api.synonym.update', ['synonym' => $synonym] + $parameters)); + + $response->assertForbidden(); +}); + +test('trashed', function () { + $synonym = Synonym::factory() + ->trashed() + ->forAnime() + ->createOne(); + + $parameters = Synonym::factory()->raw(); + + $user = User::factory()->withPermissions(CrudPermission::VIEW->format(Synonym::class))->createOne(); + + Sanctum::actingAs($user); + + $response = put(route('api.synonym.update', ['synonym' => $synonym] + $parameters)); + + $response->assertForbidden(); +}); + +test('update', function () { + $synonym = Synonym::factory()->forAnime()->createOne(); + + $parameters = Synonym::factory()->raw(); + + $user = User::factory()->withPermissions(CrudPermission::UPDATE->format(Synonym::class))->createOne(); + + Sanctum::actingAs($user); + + $response = put(route('api.synonym.update', ['synonym' => $synonym] + $parameters)); + + $response->assertOk(); +}); diff --git a/tests/Feature/Jobs/Wiki/Anime/SynonymTest.php b/tests/Feature/Jobs/Wiki/SynonymTest.php similarity index 70% rename from tests/Feature/Jobs/Wiki/Anime/SynonymTest.php rename to tests/Feature/Jobs/Wiki/SynonymTest.php index e5af6758d..995655ea0 100644 --- a/tests/Feature/Jobs/Wiki/Anime/SynonymTest.php +++ b/tests/Feature/Jobs/Wiki/SynonymTest.php @@ -3,13 +3,13 @@ declare(strict_types=1); use App\Constants\FeatureConstants; -use App\Events\Wiki\Anime\Synonym\SynonymCreated; -use App\Events\Wiki\Anime\Synonym\SynonymDeleted; -use App\Events\Wiki\Anime\Synonym\SynonymRestored; -use App\Events\Wiki\Anime\Synonym\SynonymUpdated; +use App\Events\Wiki\Synonym\SynonymCreated; +use App\Events\Wiki\Synonym\SynonymDeleted; +use App\Events\Wiki\Synonym\SynonymRestored; +use App\Events\Wiki\Synonym\SynonymUpdated; use App\Jobs\SendDiscordNotificationJob; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; +use App\Models\Wiki\Synonym; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Event; use Laravel\Pennant\Feature; @@ -21,15 +21,13 @@ test('synonym created sends discord notification', function () { Bus::fake(SendDiscordNotificationJob::class); Event::fakeExcept(SynonymCreated::class); - AnimeSynonym::factory()->for($anime)->createOne(); + Synonym::factory()->forAnime()->createOne(); Bus::assertDispatched(SendDiscordNotificationJob::class); }); test('synonym deleted sends discord notification', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); + $synonym = Synonym::factory()->forAnime()->createOne(); Feature::activate(FeatureConstants::ALLOW_DISCORD_NOTIFICATIONS); Bus::fake(SendDiscordNotificationJob::class); @@ -41,9 +39,7 @@ test('synonym deleted sends discord notification', function () { }); test('synonym restored sends discord notification', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); + $synonym = Synonym::factory()->forAnime()->createOne(); Feature::activate(FeatureConstants::ALLOW_DISCORD_NOTIFICATIONS); Bus::fake(SendDiscordNotificationJob::class); @@ -55,13 +51,9 @@ test('synonym restored sends discord notification', function () { }); test('synonym updated sends discord notification', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); + $synonym = Synonym::factory()->forAnime()->createOne(); - $changes = AnimeSynonym::factory() - ->for(Anime::factory()) - ->makeOne(); + $changes = Synonym::factory()->forAnime()->makeOne(); Feature::activate(FeatureConstants::ALLOW_DISCORD_NOTIFICATIONS); Bus::fake(SendDiscordNotificationJob::class); diff --git a/tests/Unit/Http/Api/Scope/TypeScopeTest.php b/tests/Unit/Http/Api/Scope/TypeScopeTest.php index f5d330cee..4c7581010 100644 --- a/tests/Unit/Http/Api/Scope/TypeScopeTest.php +++ b/tests/Unit/Http/Api/Scope/TypeScopeTest.php @@ -17,9 +17,9 @@ test('global scope is not within scope', function () { }); test('type scope is not within scope', function () { - $scope = new TypeScope(fake()->word()); + $scope = new TypeScope(fake()->unique()->word()); - $otherScope = new TypeScope(fake()->word()); + $otherScope = new TypeScope(fake()->unique()->word()); $this->assertFalse($scope->isWithinScope($otherScope)); }); diff --git a/tests/Unit/Models/List/PlaylistTest.php b/tests/Unit/Models/List/PlaylistTest.php index 27048124b..ac4ffb06f 100644 --- a/tests/Unit/Models/List/PlaylistTest.php +++ b/tests/Unit/Models/List/PlaylistTest.php @@ -150,7 +150,7 @@ test('tracks', function () { test('likes', function () { $playlist = Playlist::factory() - ->for(Like::factory()->for(User::factory())) + ->has(Like::factory()->for(User::factory())) ->createOne(); $this->assertInstanceOf(MorphMany::class, $playlist->likes()); diff --git a/tests/Unit/Models/Wiki/Anime/AnimeSynonymTest.php b/tests/Unit/Models/Wiki/Anime/AnimeSynonymTest.php deleted file mode 100644 index 7e56d5f06..000000000 --- a/tests/Unit/Models/Wiki/Anime/AnimeSynonymTest.php +++ /dev/null @@ -1,62 +0,0 @@ -for(Anime::factory()) - ->createOne(); - - $type = $theme->type; - - $this->assertInstanceOf(AnimeSynonymType::class, $type); -}); - -test('searchable as', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); - - $this->assertIsString($synonym->searchableAs()); -}); - -test('to searchable array', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); - - $this->assertIsArray($synonym->toSearchableArray()); -}); - -test('nameable', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); - - $this->assertIsString($synonym->getName()); -}); - -test('has subtitle', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); - - $this->assertIsString($synonym->getSubtitle()); -}); - -test('anime', function () { - $synonym = AnimeSynonym::factory() - ->for(Anime::factory()) - ->createOne(); - - $this->assertInstanceOf(BelongsTo::class, $synonym->anime()); - $this->assertInstanceOf(Anime::class, $synonym->anime()->first()); -}); diff --git a/tests/Unit/Models/Wiki/Anime/Theme/AnimeThemeEntryTest.php b/tests/Unit/Models/Wiki/Anime/Theme/AnimeThemeEntryTest.php index f1e4deff9..6da0d1f60 100644 --- a/tests/Unit/Models/Wiki/Anime/Theme/AnimeThemeEntryTest.php +++ b/tests/Unit/Models/Wiki/Anime/Theme/AnimeThemeEntryTest.php @@ -100,7 +100,7 @@ test('anime', function () { test('likes', function () { $entry = AnimeThemeEntry::factory() - ->for(Like::factory()->for(User::factory())) + ->has(Like::factory()->for(User::factory())) ->createOne(); $this->assertInstanceOf(MorphMany::class, $entry->likes()); diff --git a/tests/Unit/Models/Wiki/AnimeTest.php b/tests/Unit/Models/Wiki/AnimeTest.php index c230d9e64..6456814f1 100644 --- a/tests/Unit/Models/Wiki/AnimeTest.php +++ b/tests/Unit/Models/Wiki/AnimeTest.php @@ -5,18 +5,19 @@ declare(strict_types=1); use App\Enums\Models\Wiki\AnimeMediaFormat; use App\Enums\Models\Wiki\AnimeSeason; use App\Models\Wiki\Anime; -use App\Models\Wiki\Anime\AnimeSynonym; use App\Models\Wiki\Anime\AnimeTheme; use App\Models\Wiki\ExternalResource; use App\Models\Wiki\Image; use App\Models\Wiki\Series; use App\Models\Wiki\Studio; +use App\Models\Wiki\Synonym; use App\Pivots\Morph\Imageable; use App\Pivots\Morph\Resourceable; use App\Pivots\Wiki\AnimeSeries; use App\Pivots\Wiki\AnimeStudio; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Foundation\Testing\WithFaker; @@ -66,12 +67,12 @@ test('synonyms', function () { $synonymCount = fake()->randomDigitNotNull(); $anime = Anime::factory() - ->has(AnimeSynonym::factory()->count($synonymCount)) + ->has(Synonym::factory()->count($synonymCount)) ->createOne(); - $this->assertInstanceOf(HasMany::class, $anime->animesynonyms()); - $this->assertEquals($synonymCount, $anime->animesynonyms()->count()); - $this->assertInstanceOf(AnimeSynonym::class, $anime->animesynonyms()->first()); + $this->assertInstanceOf(MorphMany::class, $anime->synonyms()); + $this->assertEquals($synonymCount, $anime->synonyms()->count()); + $this->assertInstanceOf(Synonym::class, $anime->synonyms()->first()); }); test('series', function () {