From e8a7b35168ad9ce1a158df6153b0ef99f87a366b Mon Sep 17 00:00:00 2001 From: kyledoesdev Date: Mon, 8 Jun 2026 22:43:40 -0400 Subject: [PATCH] fix(media): Migrate type column to string-backed enum Renames the `media.type_id` column to `media.type` and converts it to a string type, aligning with the `MediaType` enum which is now string-backed. This change enhances type safety, improves database readability with descriptive string values (e.g., 'movie' instead of '1'), and leverages Laravel's enum casting. It also resolves display issues in views by utilizing the `label()` method of the enum. --- app/Actions/Api/SearchMedia.php | 4 +- app/Actions/Api/Spotify/SearchArtist.php | 4 +- app/Actions/Api/Spotify/SearchTrack.php | 4 +- app/Actions/Api/Twitch/SearchCategories.php | 4 +- app/Console/Commands/UpdateArtistImages.php | 2 +- app/Enums/MediaType.php | 12 ++-- app/Livewire/Forms/MediaForm.php | 2 +- app/Livewire/Media/Games/Edit.php | 2 +- app/Livewire/Media/Games/Show.php | 10 +-- app/Livewire/Media/Movies/Edit.php | 2 +- app/Livewire/Media/Movies/Show.php | 6 +- app/Livewire/Media/Music/Edit.php | 10 +-- app/Livewire/Media/Music/Show.php | 4 +- app/Livewire/Media/Tv/Edit.php | 2 +- app/Livewire/Media/Tv/Show.php | 8 +-- app/Models/Media.php | 5 +- database/factories/MediaFactory.php | 8 +-- ...09_023533_rename_media_type_id_to_type.php | 61 +++++++++++++++++++ .../Feature/Livewire/Media/GamesEditTest.php | 4 +- .../Feature/Livewire/Media/MoviesEditTest.php | 2 +- tests/Feature/Livewire/Media/TvEditTest.php | 2 +- 21 files changed, 110 insertions(+), 48 deletions(-) create mode 100644 database/migrations/2026_06_09_023533_rename_media_type_id_to_type.php diff --git a/app/Actions/Api/SearchMedia.php b/app/Actions/Api/SearchMedia.php index 637a76b..5432117 100644 --- a/app/Actions/Api/SearchMedia.php +++ b/app/Actions/Api/SearchMedia.php @@ -24,7 +24,7 @@ public function search(User $user, string $phrase, MediaType $mediaType) ]); if ($response->successful()) { - $existingMedia = Media::where('type_id', $mediaType)->pluck('media_id')->toArray(); + $existingMedia = Media::where('type', $mediaType)->pluck('media_id')->toArray(); return collect($response->json('results'))->map(function ($media) use ($existingMedia, $mediaType) { if (in_array($media['id'], $existingMedia)) { @@ -32,7 +32,7 @@ public function search(User $user, string $phrase, MediaType $mediaType) } return [ - 'type_id' => $mediaType, + 'type' => $mediaType, 'media_id' => $media['id'], 'name' => $mediaType->value == MediaType::MOVIE->value ? $media['original_title'] : $media['original_name'], 'cover' => 'https://image.tmdb.org/t/p/original'.$media['poster_path'], diff --git a/app/Actions/Api/Spotify/SearchArtist.php b/app/Actions/Api/Spotify/SearchArtist.php index b827b3e..7e6104b 100644 --- a/app/Actions/Api/Spotify/SearchArtist.php +++ b/app/Actions/Api/Spotify/SearchArtist.php @@ -26,7 +26,7 @@ public function handle(User $user, string $phrase) if ($response->successful()) { $artists = Media::query() - ->where('type_id', MediaType::ARTIST->value) + ->where('type', MediaType::ARTIST->value) ->pluck('media_id') ->toArray(); @@ -38,7 +38,7 @@ public function handle(User $user, string $phrase) } return [ - 'type_id' => MediaType::ARTIST->value, + 'type' => MediaType::ARTIST->value, 'media_id' => $artist['id'], 'name' => $artist['name'], 'cover' => $cover, diff --git a/app/Actions/Api/Spotify/SearchTrack.php b/app/Actions/Api/Spotify/SearchTrack.php index 7161522..8855d1b 100644 --- a/app/Actions/Api/Spotify/SearchTrack.php +++ b/app/Actions/Api/Spotify/SearchTrack.php @@ -27,7 +27,7 @@ public function handle(User $user, string $phrase): Collection if ($response->successful()) { $tracks = Media::query() - ->where('type_id', MediaType::TRACK->value) + ->where('type', MediaType::TRACK->value) ->pluck('media_id') ->toArray(); @@ -39,7 +39,7 @@ public function handle(User $user, string $phrase): Collection } return [ - 'type_id' => MediaType::TRACK->value, + 'type' => MediaType::TRACK->value, 'media_id' => $track['id'], 'name' => $track['name'], 'cover' => $cover, diff --git a/app/Actions/Api/Twitch/SearchCategories.php b/app/Actions/Api/Twitch/SearchCategories.php index 727e1d6..05d7d2c 100644 --- a/app/Actions/Api/Twitch/SearchCategories.php +++ b/app/Actions/Api/Twitch/SearchCategories.php @@ -24,7 +24,7 @@ public function search(User $user, string $phrase, MediaType $mediaType) ]); if ($response->successful()) { - $games = Media::where('type_id', $mediaType->value)->pluck('media_id')->toArray(); + $games = Media::where('type', $mediaType->value)->pluck('media_id')->toArray(); return collect($response->json('data'))->map(function ($game) use ($games, $mediaType) { if (in_array($game['id'], $games)) { @@ -32,7 +32,7 @@ public function search(User $user, string $phrase, MediaType $mediaType) } return [ - 'type_id' => $mediaType->value, + 'type' => $mediaType->value, 'media_id' => $game['id'], 'name' => $game['name'], 'cover' => $this->fix_box_art($game['box_art_url']), diff --git a/app/Console/Commands/UpdateArtistImages.php b/app/Console/Commands/UpdateArtistImages.php index bec9991..d9bfaea 100644 --- a/app/Console/Commands/UpdateArtistImages.php +++ b/app/Console/Commands/UpdateArtistImages.php @@ -24,7 +24,7 @@ public function handle() /* Loop through every artist in chunks of 50 and attempt to update their image. */ Media::query() - ->where('type_id', MediaType::ARTIST->value) + ->where('type', MediaType::ARTIST->value) ->get() ->chunk(50) ->each(function ($chunk) use ($user) { diff --git a/app/Enums/MediaType.php b/app/Enums/MediaType.php index e64b253..53785f0 100644 --- a/app/Enums/MediaType.php +++ b/app/Enums/MediaType.php @@ -2,13 +2,13 @@ namespace App\Enums; -enum MediaType: int +enum MediaType: string { - case MOVIE = 1; - case TV = 2; - case ARTIST = 3; - case TRACK = 4; - case VIDEO_GAME = 5; + case MOVIE = 'movie'; + case TV = 'tv'; + case ARTIST = 'artist'; + case TRACK = 'track'; + case VIDEO_GAME = 'video_game'; public function label(): string { diff --git a/app/Livewire/Forms/MediaForm.php b/app/Livewire/Forms/MediaForm.php index 20578a6..1d29e03 100644 --- a/app/Livewire/Forms/MediaForm.php +++ b/app/Livewire/Forms/MediaForm.php @@ -15,7 +15,7 @@ public function store($media): void $states = collect($this->states); Media::create([ - 'type_id' => $media['type_id'], + 'type' => $media['type'], 'media_id' => $media['media_id'], 'name' => $media['name'], 'cover' => $media['cover'], diff --git a/app/Livewire/Media/Games/Edit.php b/app/Livewire/Media/Games/Edit.php index 7a55f2b..b204bb9 100644 --- a/app/Livewire/Media/Games/Edit.php +++ b/app/Livewire/Media/Games/Edit.php @@ -40,7 +40,7 @@ public function render() public function games() { return Media::query() - ->where('type_id', MediaType::VIDEO_GAME->value) + ->where('type', MediaType::VIDEO_GAME->value) ->when($this->search != '', fn (Builder $query) => $query->where('name', 'LIKE', "%$this->search%")) ->tap(fn ($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query) ->paginate($this->perPage); diff --git a/app/Livewire/Media/Games/Show.php b/app/Livewire/Media/Games/Show.php index d1faafd..08ea361 100644 --- a/app/Livewire/Media/Games/Show.php +++ b/app/Livewire/Media/Games/Show.php @@ -18,23 +18,23 @@ public function render() { return view('livewire.pages.media.games.show', [ 'favorites' => Media::query() - ->where('type_id', MediaType::VIDEO_GAME->value) + ->where('type', MediaType::VIDEO_GAME->value) ->where('is_favorite', true) ->paginate(27, pageName: 'favorites'), 'current' => Media::query() - ->where('type_id', MediaType::VIDEO_GAME->value) + ->where('type', MediaType::VIDEO_GAME->value) ->where('is_active', true) ->paginate(27, pageName: 'currentlyPlaying'), 'backlog' => Media::query() - ->where('type_id', MediaType::VIDEO_GAME->value) + ->where('type', MediaType::VIDEO_GAME->value) ->where('in_backlog', true) ->paginate(27, pageName: 'backlog'), 'playedBefore' => Media::query() - ->where('type_id', MediaType::VIDEO_GAME->value) + ->where('type', MediaType::VIDEO_GAME->value) ->where('is_completed', true) ->paginate(27, pageName: 'playedBefore'), 'completed' => Media::query() - ->where('type_id', MediaType::VIDEO_GAME->value) + ->where('type', MediaType::VIDEO_GAME->value) ->where('data->total_completion', true) ->paginate(27, pageName: 'totalCompletion'), 'panel' => Panel::where('name', 'video_games')->first()->content, diff --git a/app/Livewire/Media/Movies/Edit.php b/app/Livewire/Media/Movies/Edit.php index 1d0d1f3..f719013 100644 --- a/app/Livewire/Media/Movies/Edit.php +++ b/app/Livewire/Media/Movies/Edit.php @@ -38,7 +38,7 @@ public function render() public function medias() { return Media::query() - ->where('type_id', MediaType::MOVIE->value) + ->where('type', MediaType::MOVIE->value) ->when($this->search != '', fn (Builder $query) => $query->where('name', 'LIKE', "%$this->search%")) ->tap(fn ($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query) ->paginate($this->perPage); diff --git a/app/Livewire/Media/Movies/Show.php b/app/Livewire/Media/Movies/Show.php index 24cb807..6c4604d 100644 --- a/app/Livewire/Media/Movies/Show.php +++ b/app/Livewire/Media/Movies/Show.php @@ -18,15 +18,15 @@ public function render() { return view('livewire.pages.media.movies.show', [ 'favorites' => Media::query() - ->where('type_id', MediaType::MOVIE->value) + ->where('type', MediaType::MOVIE->value) ->where('is_favorite', true) ->paginate(27, pageName: 'favorites'), 'backlog' => Media::query() - ->where('type_id', MediaType::MOVIE->value) + ->where('type', MediaType::MOVIE->value) ->where('in_backlog', true) ->paginate(27, pageName: 'backlog'), 'completed' => Media::query() - ->where('type_id', MediaType::MOVIE->value) + ->where('type', MediaType::MOVIE->value) ->where('is_completed', true) ->paginate(27, pageName: 'completed'), 'panel' => Panel::where('name', 'movies')->first()->content, diff --git a/app/Livewire/Media/Music/Edit.php b/app/Livewire/Media/Music/Edit.php index 0c05fca..ad33fa9 100644 --- a/app/Livewire/Media/Music/Edit.php +++ b/app/Livewire/Media/Music/Edit.php @@ -42,7 +42,7 @@ public function render() public function artists() { return Media::query() - ->where('type_id', MediaType::ARTIST) + ->where('type', MediaType::ARTIST->value) ->when($this->searchArtists != '', fn (Builder $query) => $query->where('name', 'LIKE', "%$this->searchArtists%")) ->tap(fn ($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query) ->paginate($this->perPageArtists); @@ -52,7 +52,7 @@ public function artists() public function tracks() { return Media::query() - ->where('type_id', MediaType::TRACK) + ->where('type', MediaType::TRACK->value) ->when($this->searchTracks != '', fn (Builder $query) => $query->where('name', 'LIKE', "%$this->searchTracks%")) ->tap(fn ($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query) ->paginate($this->perPage); @@ -78,7 +78,7 @@ public function store($mediaId) $selectedMedia = collect($this->searchedMedia)->firstWhere('media_id', $mediaId); $media = Media::create([ - 'type_id' => $selectedMedia['type_id'], + 'type' => $selectedMedia['type'], 'media_id' => $selectedMedia['media_id'], 'name' => $selectedMedia['name'], 'cover' => $selectedMedia['cover'], @@ -88,7 +88,7 @@ public function store($mediaId) $this->phrase = ''; $this->searchedMedia = []; - Flux::toast(variant: 'success', text: "{$media->type->name} added!", duration: 3000); + Flux::toast(variant: 'success', text: "{$media->type->label()} added!", duration: 3000); } public function destroy($id) @@ -97,7 +97,7 @@ public function destroy($id) $media->delete(); - Flux::toast(variant: 'success', text: "{$media->type->name} deleted.", duration: 3000); + Flux::toast(variant: 'success', text: "{$media->type->label()} deleted.", duration: 3000); } public function resetSearch() diff --git a/app/Livewire/Media/Music/Show.php b/app/Livewire/Media/Music/Show.php index 8ce2af9..23a46b0 100644 --- a/app/Livewire/Media/Music/Show.php +++ b/app/Livewire/Media/Music/Show.php @@ -18,10 +18,10 @@ public function render() { return view('livewire.pages.media.music.show', [ 'favoriteArtists' => Media::query() - ->where('type_id', MediaType::ARTIST->value) + ->where('type', MediaType::ARTIST->value) ->paginate(20, pageName: 'artists'), 'favoriteTracks' => Media::query() - ->where('type_id', MediaType::TRACK->value) + ->where('type', MediaType::TRACK->value) ->paginate(9, pageName: 'tracks'), 'panel' => Panel::where('name', 'music')->first()->content, ]); diff --git a/app/Livewire/Media/Tv/Edit.php b/app/Livewire/Media/Tv/Edit.php index 577dd46..e17a15e 100644 --- a/app/Livewire/Media/Tv/Edit.php +++ b/app/Livewire/Media/Tv/Edit.php @@ -38,7 +38,7 @@ public function render() public function medias() { return Media::query() - ->where('type_id', MediaType::TV->value) + ->where('type', MediaType::TV->value) ->when($this->search != '', fn (Builder $query) => $query->where('name', 'LIKE', "%$this->search%")) ->tap(fn ($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query) ->paginate($this->perPage); diff --git a/app/Livewire/Media/Tv/Show.php b/app/Livewire/Media/Tv/Show.php index 55cf975..082e03e 100644 --- a/app/Livewire/Media/Tv/Show.php +++ b/app/Livewire/Media/Tv/Show.php @@ -18,19 +18,19 @@ public function render() { return view('livewire.pages.media.tv.show', [ 'favorites' => Media::query() - ->where('type_id', MediaType::TV->value) + ->where('type', MediaType::TV->value) ->where('is_favorite', true) ->paginate(27, pageName: 'favorites'), 'current' => Media::query() - ->where('type_id', MediaType::TV->value) + ->where('type', MediaType::TV->value) ->where('is_active', true) ->paginate(27, pageName: 'currentlyWatching'), 'backlog' => Media::query() - ->where('type_id', MediaType::TV->value) + ->where('type', MediaType::TV->value) ->where('in_backlog', true) ->paginate(27, pageName: 'backlog'), 'completed' => Media::query() - ->where('type_id', MediaType::TV->value) + ->where('type', MediaType::TV->value) ->where('is_completed', true) ->paginate(27, pageName: 'completed'), 'panel' => Panel::where('name', 'tv')->first()->content, diff --git a/app/Models/Media.php b/app/Models/Media.php index edf06bf..9f08df7 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -2,13 +2,13 @@ namespace App\Models; -use App\Models\Model; +use App\Enums\MediaType; use Illuminate\Contracts\Database\Query\Builder; class Media extends Model { protected $fillable = [ - 'type_id', + 'type', 'media_id', 'name', 'cover', @@ -29,6 +29,7 @@ public static function boot() public function casts(): array { return [ + 'type' => MediaType::class, 'is_favorite' => 'boolean', 'is_active' => 'boolean', 'in_backlog' => 'boolean', diff --git a/database/factories/MediaFactory.php b/database/factories/MediaFactory.php index 86bb40e..8d5277b 100644 --- a/database/factories/MediaFactory.php +++ b/database/factories/MediaFactory.php @@ -17,7 +17,7 @@ class MediaFactory extends Factory public function definition(): array { return [ - 'type_id' => MediaType::VIDEO_GAME->value, + 'type' => MediaType::VIDEO_GAME->value, 'media_id' => (string) $this->faker->unique()->numberBetween(1, 1_000_000), 'name' => $this->faker->words(3, true), 'cover' => $this->faker->imageUrl(), @@ -31,16 +31,16 @@ public function definition(): array public function videoGame(): static { - return $this->state(fn () => ['type_id' => MediaType::VIDEO_GAME->value]); + return $this->state(fn () => ['type' => MediaType::VIDEO_GAME->value]); } public function movie(): static { - return $this->state(fn () => ['type_id' => MediaType::MOVIE->value]); + return $this->state(fn () => ['type' => MediaType::MOVIE->value]); } public function tv(): static { - return $this->state(fn () => ['type_id' => MediaType::TV->value]); + return $this->state(fn () => ['type' => MediaType::TV->value]); } } diff --git a/database/migrations/2026_06_09_023533_rename_media_type_id_to_type.php b/database/migrations/2026_06_09_023533_rename_media_type_id_to_type.php new file mode 100644 index 0000000..162f1f2 --- /dev/null +++ b/database/migrations/2026_06_09_023533_rename_media_type_id_to_type.php @@ -0,0 +1,61 @@ +renameColumn('type_id', 'type'); + }); + + Schema::table('media', function (Blueprint $table) { + $table->string('type')->change(); + }); + + $map = [ + 1 => MediaType::MOVIE->value, + 2 => MediaType::TV->value, + 3 => MediaType::ARTIST->value, + 4 => MediaType::TRACK->value, + 5 => MediaType::VIDEO_GAME->value, + ]; + + DB::transaction(function () use ($map): void { + foreach ($map as $old => $new) { + Media::withoutGlobalScopes()->where('type', (string) $old)->update(['type' => $new]); + } + }); + } + + public function down(): void + { + $map = [ + MediaType::MOVIE->value => 1, + MediaType::TV->value => 2, + MediaType::ARTIST->value => 3, + MediaType::TRACK->value => 4, + MediaType::VIDEO_GAME->value => 5, + ]; + + DB::transaction(function () use ($map): void { + foreach ($map as $old => $new) { + Media::withoutGlobalScopes()->where('type', $old)->update(['type' => $new]); + } + }); + + Schema::table('media', function (Blueprint $table) { + $table->unsignedBigInteger('type')->change(); + }); + + Schema::table('media', function (Blueprint $table) { + $table->renameColumn('type', 'type_id'); + }); + } +}; diff --git a/tests/Feature/Livewire/Media/GamesEditTest.php b/tests/Feature/Livewire/Media/GamesEditTest.php index e68140a..9c48cce 100644 --- a/tests/Feature/Livewire/Media/GamesEditTest.php +++ b/tests/Feature/Livewire/Media/GamesEditTest.php @@ -49,10 +49,10 @@ $game = Media::factory()->videoGame()->create(); Livewire::test(Edit::class) - ->call('toggleState', $game->getKey(), 'type_id') + ->call('toggleState', $game->getKey(), 'type') ->assertStatus(422); - expect($game->fresh()->type_id)->toBe(MediaType::VIDEO_GAME->value); + expect($game->fresh()->type)->toBe(MediaType::VIDEO_GAME); }); it('destroys a game', function () { diff --git a/tests/Feature/Livewire/Media/MoviesEditTest.php b/tests/Feature/Livewire/Media/MoviesEditTest.php index 4c5d61c..9214c29 100644 --- a/tests/Feature/Livewire/Media/MoviesEditTest.php +++ b/tests/Feature/Livewire/Media/MoviesEditTest.php @@ -31,7 +31,7 @@ Livewire::test(Edit::class) ->call('toggleState', $movie->getKey(), $value) ->assertStatus(422); -})->with(['is_active', 'total_completion', 'type_id']); +})->with(['is_active', 'total_completion', 'type']); it('destroys a movie', function () { $movie = Media::factory()->movie()->create(); diff --git a/tests/Feature/Livewire/Media/TvEditTest.php b/tests/Feature/Livewire/Media/TvEditTest.php index 841866d..0f1cc26 100644 --- a/tests/Feature/Livewire/Media/TvEditTest.php +++ b/tests/Feature/Livewire/Media/TvEditTest.php @@ -32,4 +32,4 @@ Livewire::test(Edit::class) ->call('toggleState', $show->getKey(), $value) ->assertStatus(422); -})->with(['total_completion', 'type_id']); +})->with(['total_completion', 'type']);