input('category_id'); if ($categoryId) { $query->whereHas('categories', function ($q) use ($categoryId) { $q->where('music_categories.id', $categoryId); }); } 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_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', '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', ]); // Ensure at least one category or subcategory is provided if (empty($data['category_ids']) && empty($data['subcategory_ids'])) { return response()->json([ 'message' => 'At least one category or subcategory is required' ], 422); } $data['slug'] = $this->uniqueSlug($data['name']); // 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); $playlist->categories()->sync($data['category_ids'] ?? []); $playlist->subcategories()->sync($data['subcategory_ids'] ?? []); return response()->json([ 'message' => 'Playlist created successfully', 'playlist' => $playlist->load(['categories', 'subcategories', 'image', 'detailImage']) ], 201); } public function show($id) { $playlist = MusicPlaylist::with([ 'categories', 'subcategories', 'image', 'detailImage', 'musics' => function($q) { $q->where('music.is_active', true) ->with(['image', 'tags']) ->orderBy('music_playlist.order'); }, 'comments' => function($q) { // Add comments relationship $q->with('user')->latest()->limit(10); } ])->findOrFail($id); $userComment = $playlist->userComment(); $comments = $playlist->comments() ->with('user') ->latest() ->paginate(15); return response()->json([ $playlist, 'statistics' => [ 'total_musics' => $playlist->musics->count(), 'total_duration' => $playlist->total_duration, 'total_comments' => $playlist->comments_count, 'total_likes' => $playlist->likes_count, 'total_saves' => $playlist->saved_count, ], 'user_interaction' => [ 'has_commented' => $playlist->has_user_commented, 'user_comment' => $playlist->user_comment, 'user_comment_id' => $playlist->user_comment_id, 'has_liked' => $playlist->is_liked, 'has_saved' => $playlist->is_saved, ], 'comments' => $comments, ]); } public function update(Request $request, $id) { $playlist = MusicPlaylist::findOrFail($id); $data = $request->validate([ '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', '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', ]); if (isset($data['name'])) { $data['slug'] = $this->uniqueSlug($data['name'], $playlist->id); } // An uploaded image file takes precedence over a provided image_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); 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(['categories', 'subcategories', 'image', 'detailImage']) ]); } // Build a slug that is unique within music_playlists. Persian names often // transliterate to the same/empty value, so we fall back and add a counter. private function uniqueSlug(string $name, ?int $ignoreId = null): string { $base = Str::slug($name); if ($base === '') { $base = 'playlist'; } $slug = $base; $i = 2; while ( MusicPlaylist::where('slug', $slug) ->when($ignoreId, fn ($q) => $q->where('id', '!=', $ignoreId)) ->exists() ) { $slug = "{$base}-{$i}"; $i++; } return $slug; } public function destroy($id) { $playlist = MusicPlaylist::findOrFail($id); $playlist->delete(); return response()->json(['message' => 'Playlist deleted successfully']); } }