diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index cc30392..5b3fc79 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -65,6 +65,34 @@ public function show($id) return response()->json($category); } + // Category with each of its sub-categories and that sub-category's medias. + public function grouped($id) + { + $category = Category::with(['subcategories' => function ($q) { + $q->orderBy('name')->with(['media' => function ($m) { + $m->with(['image', 'detailImage', 'tags'])->latest(); + }]); + }])->findOrFail($id); + + return response()->json([ + 'category' => [ + 'id' => $category->id, + 'name' => $category->name, + 'description' => $category->description, + 'icon_url' => $category->icon_url, + 'type' => $category->type, + ], + 'sub_categories' => $category->subcategories->map(fn ($sub) => [ + 'sub_category' => [ + 'id' => $sub->id, + 'name' => $sub->name, + 'description' => $sub->description, + ], + 'medias' => $sub->media, + ])->values(), + ]); + } + public function update(Request $request, $id) { $category = Category::findOrFail($id); diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index 4c913c6..e6c86d6 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -201,9 +201,31 @@ public function index(Request $request) }); } - return response()->json( - $query->orderBy('created_at', 'desc')->get() - ); + $query->orderBy('created_at', 'desc'); + + // Optional cap: ?count=10 returns only the first 10 items. + if ($request->filled('count')) { + $query->limit(max(1, (int) $request->input('count'))); + } + + return response()->json($query->get()); +} + +// NEWEST media, paginated (?per_page=, ?page=). Same visibility scope as index. +public function newest(Request $request) +{ + $perPage = (int) $request->input('per_page', 20); + $perPage = max(1, min($perPage, 100)); + + $media = Media::with(['image', 'detailImage', 'categories', 'subCategories', 'myNote', 'tags']) + ->where(function ($q) { + $q->where('visibility', 'public') + ->orWhere('user_id', auth()->id()); + }) + ->orderBy('created_at', 'desc') + ->paginate($perPage); + + return response()->json($media); } public function submitFeedback(Request $request, $mediaId) diff --git a/app/Http/Controllers/MusicCategoryController.php b/app/Http/Controllers/MusicCategoryController.php index 273e427..7f3ac64 100644 --- a/app/Http/Controllers/MusicCategoryController.php +++ b/app/Http/Controllers/MusicCategoryController.php @@ -89,6 +89,41 @@ public function show($id) return response()->json($category); } + // Category with each of its sub-categories and that sub-category's playlists. + public function grouped($id) + { + $category = MusicCategory::with([ + 'image', + 'subcategories' => function ($q) { + $q->orderBy('order')->with([ + 'image', + 'playlists' => function ($p) { + $p->where('is_active', true)->with('image')->orderBy('order'); + }, + ]); + }, + ])->findOrFail($id); + + return response()->json([ + 'category' => [ + 'id' => $category->id, + 'name' => $category->name, + 'description' => $category->description, + 'icon_url' => $category->image?->url, + 'type' => 'playlist', + ], + 'sub_categories' => $category->subcategories->map(fn ($sub) => [ + 'sub_category' => [ + 'id' => $sub->id, + 'name' => $sub->name, + 'description' => $sub->description, + 'image_url' => $sub->image?->url, + ], + 'playlists' => $sub->playlists, + ])->values(), + ]); + } + public function update(Request $request, $id) { $category = MusicCategory::findOrFail($id); diff --git a/routes/api.php b/routes/api.php index 1d0ff16..5205588 100644 --- a/routes/api.php +++ b/routes/api.php @@ -223,6 +223,7 @@ ///media Route::get('/media/filters', [MediaController::class, 'filters']); Route::get('/media/popular', [MediaController::class, 'popular']); + Route::get('/media/newest', [MediaController::class, 'newest']); Route::get('/media/recently-played', [MediaController::class, 'recentlyPlayed']); Route::post('/media', [MediaController::class, 'store']); Route::get('/media', [MediaController::class, 'index']); @@ -241,6 +242,8 @@ Route::post('/media/{id}/note', [MediaController::class, 'storeNote']); // Media categories + // Category with its sub-categories, each carrying its medias. + Route::get('categories/{id}/grouped', [CategoryController::class, 'grouped']); // Explicit POST update so an icon image can be uploaded (multipart can't ride a PUT). Route::post('categories/{id}', [CategoryController::class, 'update']); Route::apiResource('categories', CategoryController::class); @@ -252,6 +255,8 @@ // Music Categories + // Category with its sub-categories, each carrying its playlists. + Route::get('music-categories/{id}/grouped', [MusicCategoryController::class, 'grouped']); Route::apiResource('music-categories', MusicCategoryController::class); Route::get('public/music-categories', [MusicCategoryController::class, 'index']);