diff --git a/app/Http/Controllers/MusicPlaylistController.php b/app/Http/Controllers/MusicPlaylistController.php index 64c09b7..ca34f44 100644 --- a/app/Http/Controllers/MusicPlaylistController.php +++ b/app/Http/Controllers/MusicPlaylistController.php @@ -8,57 +8,69 @@ class MusicPlaylistController extends Controller { - public function index(Request $request) + public function index(Request $request, $categoryId = null) { - $query = MusicPlaylist::with(['category', 'subcategory', 'image']); - - if ($request->has('category_id')) { - $query->where('category_id', $request->category_id)->whereNull('subcategory_id'); + $query = MusicPlaylist::with(['categories', 'subcategories', 'image']); + + $categoryId = $categoryId ?? $request->input('category_id'); + + if ($categoryId) { + $query->whereHas('categories', function ($q) use ($categoryId) { + $q->where('music_categories.id', $categoryId); + }); } - - if ($request->has('subcategory_id')) { - $query->where('subcategory_id', $request->subcategory_id); + + if ($request->filled('subcategory_id')) { + $subcategoryId = $request->input('subcategory_id'); + $query->whereHas('subcategories', function ($q) use ($subcategoryId) { + $q->where('music_subcategories.id', $subcategoryId); + }); } - + $playlists = $query->where('is_active', true)->orderBy('order')->get(); - + return response()->json($playlists); } public function store(Request $request) { $data = $request->validate([ - 'category_id' => 'nullable|exists:music_categories,id', - 'subcategory_id' => 'nullable|exists:music_subcategories,id', + 'category_ids' => 'nullable|array', + 'category_ids.*' => 'integer|exists:music_categories,id', + 'subcategory_ids' => 'nullable|array', + 'subcategory_ids.*' => 'integer|exists:music_subcategories,id', 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'image_id' => 'nullable|exists:images,id', 'order' => 'nullable|integer', 'is_active' => 'nullable|boolean', ]); - - // Ensure either category_id or subcategory_id is provided - if (!$data['category_id'] && !$data['subcategory_id']) { + + // Ensure at least one category or subcategory is provided + if (empty($data['category_ids']) && empty($data['subcategory_ids'])) { return response()->json([ - 'message' => 'Either category_id or subcategory_id is required' + 'message' => 'At least one category or subcategory is required' ], 422); } - + $data['slug'] = Str::slug($data['name']); - + $playlist = MusicPlaylist::create($data); - + + $playlist->categories()->sync($data['category_ids'] ?? []); + $playlist->subcategories()->sync($data['subcategory_ids'] ?? []); + return response()->json([ 'message' => 'Playlist created successfully', - 'playlist' => $playlist->load(['category', 'subcategory', 'image']) + 'playlist' => $playlist->load(['categories', 'subcategories', 'image']) ], 201); } public function show($id) { $playlist = MusicPlaylist::with([ - 'category', - 'subcategory', + 'categories', + 'subcategories', 'image', 'musics' => function($q) { $q->where('is_active', true) @@ -100,23 +112,34 @@ public function update(Request $request, $id) $playlist = MusicPlaylist::findOrFail($id); $data = $request->validate([ - 'category_id' => 'sometimes|exists:music_categories,id', + 'category_ids' => 'sometimes|array', + 'category_ids.*' => 'integer|exists:music_categories,id', + 'subcategory_ids' => 'sometimes|array', + 'subcategory_ids.*' => 'integer|exists:music_subcategories,id', 'name' => 'sometimes|string|max:255', 'description' => 'nullable|string', 'image_id' => 'nullable|exists:images,id', 'order' => 'nullable|integer', 'is_active' => 'nullable|boolean', ]); - + if (isset($data['name'])) { $data['slug'] = Str::slug($data['name']); } - + $playlist->update($data); - + + if (array_key_exists('category_ids', $data)) { + $playlist->categories()->sync($data['category_ids'] ?? []); + } + + if (array_key_exists('subcategory_ids', $data)) { + $playlist->subcategories()->sync($data['subcategory_ids'] ?? []); + } + return response()->json([ 'message' => 'Playlist updated successfully', - 'playlist' => $playlist->load(['category', 'image']) + 'playlist' => $playlist->load(['categories', 'subcategories', 'image']) ]); } diff --git a/app/Models/MusicCategory.php b/app/Models/MusicCategory.php index 5387cf9..0264f51 100644 --- a/app/Models/MusicCategory.php +++ b/app/Models/MusicCategory.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this class MusicCategory extends Model { @@ -18,9 +19,9 @@ class MusicCategory extends Model 'order' => 'integer', ]; - public function playlists(): HasMany + public function playlists(): BelongsToMany { - return $this->hasMany(MusicPlaylist::class, 'category_id'); + return $this->belongsToMany(MusicPlaylist::class, 'music_category_playlist', 'category_id', 'playlist_id'); } public function subcategories(): HasMany @@ -45,7 +46,7 @@ public function image(): BelongsTo public function getActivePlaylistsAttribute() { - return $this->playlists()->where('is_active', true)->get(); + return $this->playlists()->where('music_playlists.is_active', true)->get(); } // Total music count across all playlists and subcategories diff --git a/app/Models/MusicPlaylist.php b/app/Models/MusicPlaylist.php index b777c3d..c701e9b 100644 --- a/app/Models/MusicPlaylist.php +++ b/app/Models/MusicPlaylist.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this use App\Traits\HasComments; // Add this use App\Traits\HasLikes; @@ -14,7 +15,7 @@ class MusicPlaylist extends Model protected $table = 'music_playlists'; protected $fillable = [ - 'category_id', 'subcategory_id','name', 'slug', 'description', 'image_id', 'order', 'is_active' + 'name', 'slug', 'description', 'image_id', 'order', 'is_active' ]; protected $casts = [ @@ -31,14 +32,14 @@ class MusicPlaylist extends Model 'is_saved', 'saved_count' ]; - public function category(): BelongsTo + public function categories(): BelongsToMany { - return $this->belongsTo(MusicCategory::class, 'category_id'); + return $this->belongsToMany(MusicCategory::class, 'music_category_playlist', 'playlist_id', 'category_id'); } - - public function subcategory(): BelongsTo + + public function subcategories(): BelongsToMany { - return $this->belongsTo(MusicSubcategory::class, 'subcategory_id'); + return $this->belongsToMany(MusicSubcategory::class, 'music_subcategory_playlist', 'playlist_id', 'subcategory_id'); } diff --git a/app/Models/MusicSubcategory.php b/app/Models/MusicSubcategory.php index 41103ad..5fa43e8 100644 --- a/app/Models/MusicSubcategory.php +++ b/app/Models/MusicSubcategory.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; class MusicSubcategory extends Model { @@ -23,9 +24,9 @@ public function category(): BelongsTo return $this->belongsTo(MusicCategory::class, 'category_id'); } - public function playlists(): HasMany + public function playlists(): BelongsToMany { - return $this->hasMany(MusicPlaylist::class, 'subcategory_id'); + return $this->belongsToMany(MusicPlaylist::class, 'music_subcategory_playlist', 'subcategory_id', 'playlist_id'); } public function image(): BelongsTo @@ -35,7 +36,7 @@ public function image(): BelongsTo public function getActivePlaylistsAttribute() { - return $this->playlists()->where('is_active', true)->orderBy('order')->get(); + return $this->playlists()->where('music_playlists.is_active', true)->orderBy('music_playlists.order')->get(); } // Get total music count across all playlists in this subcategory diff --git a/database/migrations/2026_05_31_110000_create_music_playlist_category_pivot_tables.php b/database/migrations/2026_05_31_110000_create_music_playlist_category_pivot_tables.php new file mode 100644 index 0000000..6a09f68 --- /dev/null +++ b/database/migrations/2026_05_31_110000_create_music_playlist_category_pivot_tables.php @@ -0,0 +1,79 @@ +id(); + $table->foreignId('playlist_id')->constrained('music_playlists')->cascadeOnDelete(); + $table->foreignId('category_id')->constrained('music_categories')->cascadeOnDelete(); + $table->timestamps(); + + $table->unique(['playlist_id', 'category_id']); + }); + + Schema::create('music_subcategory_playlist', function (Blueprint $table) { + $table->id(); + $table->foreignId('playlist_id')->constrained('music_playlists')->cascadeOnDelete(); + $table->foreignId('subcategory_id')->constrained('music_subcategories')->cascadeOnDelete(); + $table->timestamps(); + + $table->unique(['playlist_id', 'subcategory_id']); + }); + + // Backfill the new pivots from the existing single columns. + if (Schema::hasColumn('music_playlists', 'category_id')) { + DB::table('music_playlists') + ->whereNotNull('category_id') + ->orderBy('id') + ->select('id', 'category_id') + ->chunk(200, function ($rows) { + $now = now(); + $insert = $rows->map(fn ($row) => [ + 'playlist_id' => $row->id, + 'category_id' => $row->category_id, + 'created_at' => $now, + 'updated_at' => $now, + ])->all(); + + DB::table('music_category_playlist')->insertOrIgnore($insert); + }); + } + + if (Schema::hasColumn('music_playlists', 'subcategory_id')) { + DB::table('music_playlists') + ->whereNotNull('subcategory_id') + ->orderBy('id') + ->select('id', 'subcategory_id') + ->chunk(200, function ($rows) { + $now = now(); + $insert = $rows->map(fn ($row) => [ + 'playlist_id' => $row->id, + 'subcategory_id' => $row->subcategory_id, + 'created_at' => $now, + 'updated_at' => $now, + ])->all(); + + DB::table('music_subcategory_playlist')->insertOrIgnore($insert); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('music_subcategory_playlist'); + Schema::dropIfExists('music_category_playlist'); + } +}; diff --git a/database/migrations/2026_05_31_110100_drop_category_columns_from_music_playlists_table.php b/database/migrations/2026_05_31_110100_drop_category_columns_from_music_playlists_table.php new file mode 100644 index 0000000..bf70594 --- /dev/null +++ b/database/migrations/2026_05_31_110100_drop_category_columns_from_music_playlists_table.php @@ -0,0 +1,35 @@ +dropForeign(['subcategory_id']); + $table->dropColumn('subcategory_id'); + + $table->dropForeign(['category_id']); + $table->dropColumn('category_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('music_playlists', function (Blueprint $table) { + $table->foreignId('category_id')->nullable()->after('id') + ->constrained('music_categories')->nullOnDelete(); + $table->foreignId('subcategory_id')->nullable()->after('category_id') + ->constrained('music_subcategories')->nullOnDelete(); + }); + } +};