diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index 56388ca..8619968 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -30,6 +30,8 @@ public function store(Request $request) 'subcategory_ids.*' => 'integer|exists:sub_categories,id', 'image_id' => 'nullable|exists:images,id', 'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', + 'detail_image_id' => 'nullable|exists:images,id', + 'detail_image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', 'duration' => 'nullable|integer', 'is_premium' => 'nullable|boolean', 'file' => 'nullable|file|extensions:mp3,wav,ogg,flac,aac,m4a,opus,mpga,wma,mp4,mov,m4v,webm,mkv,avi,3gp|max:512000', @@ -47,6 +49,7 @@ public function store(Request $request) // An uploaded image file takes precedence over a provided image_id. $imageId = $this->uploadedImageId($request) ?? ($data['image_id'] ?? null); + $detailImageId = $this->uploadedImageId($request, 'detail_image') ?? ($data['detail_image_id'] ?? null); // When a file is uploaded, expose its public URL as external_url too // (older front-end versions read external_url for the playable source). @@ -60,6 +63,7 @@ public function store(Request $request) 'file_path' => $path, 'external_url' => $externalUrl, 'image_id' => $imageId, + 'detail_image_id' => $detailImageId, 'duration' => $data['duration'] ?? null, 'visibility' => $data['visibility'] ?? 'public', 'is_premium'=> $data['is_premium'] ?? false @@ -80,12 +84,12 @@ public function store(Request $request) } return response()->json([ 'message' => 'Media created successfully', - 'media' => $media->load(['image', 'categories', 'subCategories', 'tags']), + 'media' => $media->load(['image', 'detailImage', 'categories', 'subCategories', 'tags']), ]); } public function index(Request $request) { - $query = Media::with(['image', 'categories', 'subCategories', 'myNote', 'tags','comments']) + $query = Media::with(['image', 'detailImage', 'categories', 'subCategories', 'myNote', 'tags','comments']) ->where(function ($q) { $q->where('visibility', 'public') ->orWhere('user_id', auth()->id()); @@ -354,6 +358,7 @@ public function show($id) { $media = Media::with([ 'image', + 'detailImage', 'categories', 'subCategories', 'myNote', @@ -392,6 +397,7 @@ public function show($id) 'updated_at' => $media->updated_at, 'is_premium' => $media->is_premium, 'image' => $media->image, + 'detail_image' => $media->detailImage, 'categories' => $media->categories, 'sub_categories' => $media->subCategories, 'tags' => $media->tags, @@ -482,6 +488,8 @@ public function update(Request $request, $id) 'is_premium' => 'nullable|boolean', 'image_id' => 'nullable|exists:images,id', 'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', + 'detail_image_id' => 'nullable|exists:images,id', + 'detail_image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', 'duration' => 'nullable|integer', 'file' => 'nullable|file|extensions:mp3,wav,ogg,flac,aac,m4a,opus,mpga,wma,mp4,mov,m4v,webm,mkv,avi,3gp|max:512000', 'external_url' => 'nullable|string', @@ -522,6 +530,16 @@ public function update(Request $request, $id) $updateData['image_id'] = $media->image_id; } + // --- Handle detail image (shown on the show-by-id screen) --- + $uploadedDetailImageId = $this->uploadedImageId($request, 'detail_image'); + if ($uploadedDetailImageId !== null) { + $updateData['detail_image_id'] = $uploadedDetailImageId; + } elseif (array_key_exists('detail_image_id', $data)) { + $updateData['detail_image_id'] = $data['detail_image_id']; + } else { + $updateData['detail_image_id'] = $media->detail_image_id; + } + // --- update media --- $media->update($updateData); @@ -546,7 +564,7 @@ public function update(Request $request, $id) return response()->json([ 'message' => 'Media updated successfully', - 'media' => $media->load(['image', 'categories', 'subCategories', 'tags']), + 'media' => $media->load(['image', 'detailImage', 'categories', 'subCategories', 'tags']), ]); } @@ -583,7 +601,7 @@ public function toggleSaveMedia($id) // GET saved public function saved() { - return auth()->user()->savedMedia()->with(['image','categories', 'subCategories', 'myNote' , 'tags'])->get(); + return auth()->user()->savedMedia()->with(['image','detailImage','categories', 'subCategories', 'myNote' , 'tags'])->get(); } // RECORD a play for the current user (feeds popular + recently played). @@ -621,7 +639,7 @@ public function popular(Request $request) }) ->withCount('plays as listeners_count') // distinct users who played ->withSum('plays as plays_count', 'play_count') // total plays - ->with(['image', 'categories', 'subCategories', 'tags']) + ->with(['image', 'detailImage', 'categories', 'subCategories', 'tags']) ->orderByDesc('plays_count') ->orderByDesc('listeners_count') ->orderByDesc('created_at') @@ -638,7 +656,7 @@ public function recentlyPlayed(Request $request) $plays = MediaPlay::where('user_id', auth()->id()) ->whereNotNull('last_played_at') - ->with(['media' => fn ($q) => $q->with(['image', 'categories', 'subCategories', 'tags'])]) + ->with(['media' => fn ($q) => $q->with(['image', 'detailImage', 'categories', 'subCategories', 'tags'])]) ->orderByDesc('last_played_at') ->limit($limit) ->get(); diff --git a/app/Http/Controllers/MusicPlaylistController.php b/app/Http/Controllers/MusicPlaylistController.php index 0cd5d62..74138d4 100644 --- a/app/Http/Controllers/MusicPlaylistController.php +++ b/app/Http/Controllers/MusicPlaylistController.php @@ -13,7 +13,7 @@ class MusicPlaylistController extends Controller public function index(Request $request, $categoryId = null) { - $query = MusicPlaylist::with(['categories', 'subcategories', 'image']); + $query = MusicPlaylist::with(['categories', 'subcategories', 'image', 'detailImage']); $categoryId = $categoryId ?? $request->input('category_id'); @@ -46,6 +46,8 @@ public function store(Request $request) 'description' => 'nullable|string', 'image_id' => 'nullable|exists:images,id', 'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', + 'detail_image_id' => 'nullable|exists:images,id', + 'detail_image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', 'order' => 'nullable|integer', 'is_active' => 'nullable|boolean', ]); @@ -61,6 +63,7 @@ public function store(Request $request) // An uploaded image file takes precedence over a provided image_id. $data['image_id'] = $this->uploadedImageId($request) ?? ($data['image_id'] ?? null); + $data['detail_image_id'] = $this->uploadedImageId($request, 'detail_image') ?? ($data['detail_image_id'] ?? null); $playlist = MusicPlaylist::create($data); @@ -69,7 +72,7 @@ public function store(Request $request) return response()->json([ 'message' => 'Playlist created successfully', - 'playlist' => $playlist->load(['categories', 'subcategories', 'image']) + 'playlist' => $playlist->load(['categories', 'subcategories', 'image', 'detailImage']) ], 201); } @@ -79,6 +82,7 @@ public function show($id) 'categories', 'subcategories', 'image', + 'detailImage', 'musics' => function($q) { $q->where('music.is_active', true) ->with(['image', 'tags']) @@ -127,6 +131,8 @@ public function update(Request $request, $id) 'description' => 'nullable|string', 'image_id' => 'nullable|exists:images,id', 'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', + 'detail_image_id' => 'nullable|exists:images,id', + 'detail_image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', 'order' => 'nullable|integer', 'is_active' => 'nullable|boolean', ]); @@ -139,6 +145,9 @@ public function update(Request $request, $id) if (($uploadedImageId = $this->uploadedImageId($request)) !== null) { $data['image_id'] = $uploadedImageId; } + if (($uploadedDetailImageId = $this->uploadedImageId($request, 'detail_image')) !== null) { + $data['detail_image_id'] = $uploadedDetailImageId; + } $playlist->update($data); @@ -152,7 +161,7 @@ public function update(Request $request, $id) return response()->json([ 'message' => 'Playlist updated successfully', - 'playlist' => $playlist->load(['categories', 'subcategories', 'image']) + 'playlist' => $playlist->load(['categories', 'subcategories', 'image', 'detailImage']) ]); } diff --git a/app/Models/Media.php b/app/Models/Media.php index 8e0ad66..e548f7d 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -13,6 +13,7 @@ class Media extends Model protected $fillable = [ 'user_id', 'image_id', + 'detail_image_id', 'title', 'caption', 'type', @@ -42,6 +43,12 @@ public function image() return $this->belongsTo(Image::class); } + // Image shown on the detail (show-by-id) screen; image() is the list thumbnail. + public function detailImage() + { + return $this->belongsTo(Image::class, 'detail_image_id'); + } + public function categories() { return $this->belongsToMany(Category::class, 'category_media'); diff --git a/app/Models/MusicPlaylist.php b/app/Models/MusicPlaylist.php index 4e2c490..4a5bcc3 100644 --- a/app/Models/MusicPlaylist.php +++ b/app/Models/MusicPlaylist.php @@ -15,7 +15,7 @@ class MusicPlaylist extends Model protected $table = 'music_playlists'; protected $fillable = [ - 'name', 'slug', 'description', 'image_id', 'order', 'is_active' + 'name', 'slug', 'description', 'image_id', 'detail_image_id', 'order', 'is_active' ]; protected $casts = [ @@ -54,6 +54,12 @@ public function image(): BelongsTo { return $this->belongsTo(Image::class); } + + // Image shown on the detail (show-by-id) screen; image() is the list thumbnail. + public function detailImage(): BelongsTo + { + return $this->belongsTo(Image::class, 'detail_image_id'); + } public function getActiveMusicsAttribute() { diff --git a/database/migrations/2026_06_03_130000_add_detail_image_to_media_and_playlists.php b/database/migrations/2026_06_03_130000_add_detail_image_to_media_and_playlists.php new file mode 100644 index 0000000..fac58de --- /dev/null +++ b/database/migrations/2026_06_03_130000_add_detail_image_to_media_and_playlists.php @@ -0,0 +1,41 @@ +unsignedBigInteger('detail_image_id')->nullable()->after('image_id'); + $table->foreign('detail_image_id')->references('id')->on('images')->nullOnDelete(); + }); + + Schema::table('music_playlists', function (Blueprint $table) { + $table->unsignedBigInteger('detail_image_id')->nullable()->after('image_id'); + $table->foreign('detail_image_id')->references('id')->on('images')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('media', function (Blueprint $table) { + $table->dropForeign(['detail_image_id']); + $table->dropColumn('detail_image_id'); + }); + + Schema::table('music_playlists', function (Blueprint $table) { + $table->dropForeign(['detail_image_id']); + $table->dropColumn('detail_image_id'); + }); + } +};