From e446de79fd770ad7cb181a5602d1884b572a8b75 Mon Sep 17 00:00:00 2001 From: AmirmahdiNourkazemi Date: Wed, 20 May 2026 08:48:35 +0330 Subject: [PATCH] fix: music test apis --- .../Controllers/MusicCategoryController.php | 74 ++++-- app/Http/Controllers/MusicController.php | 210 ++++++++++++------ app/Models/Music.php | 8 +- app/Models/MusicCategory.php | 3 +- app/Models/MusicPlaylist.php | 3 +- app/Models/Tag.php | 3 +- public/.htaccess | 15 ++ public/phpinfo.php | 3 + routes/api.php | 10 +- 9 files changed, 242 insertions(+), 87 deletions(-) create mode 100644 public/phpinfo.php diff --git a/app/Http/Controllers/MusicCategoryController.php b/app/Http/Controllers/MusicCategoryController.php index 9bfe53f..f914e03 100644 --- a/app/Http/Controllers/MusicCategoryController.php +++ b/app/Http/Controllers/MusicCategoryController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; use App\Models\MusicPlaylist; +use App\Models\MusicCategory; use Illuminate\Support\Str; use Illuminate\Http\Request; @@ -18,22 +19,63 @@ public function index() public function store(Request $request) { - $data = $request->validate([ - 'name' => 'required|string|max:255', - 'description' => 'nullable|string', - 'image_id' => 'nullable|exists:images,id', - 'order' => 'nullable|integer', - 'is_active' => 'nullable|boolean', - ]); - - $data['slug'] = Str::slug($data['name']); - - $category = MusicCategory::create($data); - - return response()->json([ - 'message' => 'Category created successfully', - 'category' => $category->load('image') - ], 201); + try { + $data = $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'image_id' => 'nullable|exists:images,id', + 'order' => 'nullable|integer', + 'is_active' => 'nullable|boolean', + ]); + + // Check for duplicate name + $slug = Str::slug($data['name']); + $existingCategory = MusicCategory::where('slug', $slug)->first(); + + if ($existingCategory) { + return response()->json([ + 'message' => 'A category with this name already exists', + 'errors' => [ + 'name' => ['The category name "' . $data['name'] . '" is already taken. Please use a different name.'] + ] + ], 422); + } + + // Optional: Check for duplicate name with case-insensitive comparison + $existingName = MusicCategory::whereRaw('LOWER(name) = ?', [strtolower($data['name'])])->first(); + if ($existingName) { + return response()->json([ + 'message' => 'A category with a similar name already exists', + 'errors' => [ + 'name' => ['Category "' . $existingName->name . '" already exists. Please use a different name.'] + ] + ], 422); + } + + $data['slug'] = $slug; + $category = MusicCategory::create($data); + + return response()->json([ + 'message' => 'Category created successfully', + 'category' => $category->load('image') + ], 201); + + } catch (\Illuminate\Database\QueryException $e) { + // Handle database duplicate entry error (if unique constraint exists) + if ($e->errorInfo[1] == 1062) { // MySQL duplicate entry error code + return response()->json([ + 'message' => 'A category with this name already exists', + 'errors' => [ + 'name' => ['The category name must be unique.'] + ] + ], 422); + } + + return response()->json([ + 'message' => 'An error occurred while creating the category', + 'error' => $e->getMessage() + ], 500); + } } public function show($id) diff --git a/app/Http/Controllers/MusicController.php b/app/Http/Controllers/MusicController.php index 2a1bbe7..d880ff4 100644 --- a/app/Http/Controllers/MusicController.php +++ b/app/Http/Controllers/MusicController.php @@ -8,7 +8,28 @@ use Illuminate\Support\Facades\Storage; class MusicController extends Controller -{ public function getMusicByPlaylist($playlistId) +{ + + public function index() + { + $userId = auth()->id(); + + $music = Music::with(['image', 'playlist']) + ->where('type', 'public') + ->orWhere(function($query) use ($userId) { + $query->where('type', 'private') + ->where('user_id', $userId); + }) + ->orderBy('created_at', 'desc') + ->get(); + + return response()->json([ + 'data' => $music, + 'total' => $music->count() + ]); + } + +public function getMusicByPlaylist($playlistId) { $playlist = MusicPlaylist::findOrFail($playlistId); @@ -79,83 +100,146 @@ public function updateOrder(Request $request) // ✅ Upload music public function store(Request $request) { - $data = $request->validate([ - 'title' => 'required|string|max:255', - 'artist' => 'nullable|string|max:255', - 'file' => 'required|mimes:mp3,wav,ogg|max:10240', // max 10MB - 'type' => 'nullable|in:public,private', - 'image_id' => 'nullable|exists:images,id', - ]); + try { + $data = $request->validate([ + 'title' => 'required|string|max:255', + 'artist' => 'nullable|string|max:255', + 'file' => 'required|mimes:mp3,wav,ogg|max:20971520', + 'type' => 'nullable|in:public,private', + 'image_id' => 'nullable|exists:images,id', + 'playlist_id' => 'nullable|exists:music_playlists,id', + 'duration' => 'nullable|string|regex:/^(?:\d+:)?[0-5]?\d:[0-5]\d$/', // validates mm:ss or hh:mm:ss + ]); - $path = $request->file('file')->store('music', 'public'); + // Handle file upload + if (!$request->hasFile('file')) { + return response()->json([ + 'message' => 'No file was uploaded' + ], 400); + } - $music = Music::create([ - 'user_id' => auth()->id(), - 'title' => $data['title'], - 'artist' => $data['artist'] ?? null, - 'file_path' => $path, - 'type' => $data['type'] ?? 'private', - 'image_id' => $data['image_id'] ?? null, - ]); + $file = $request->file('file'); + + // Validate file size + if ($file->getSize() > 20971520) { + return response()->json([ + 'message' => 'File size exceeds 10MB limit' + ], 422); + } - return response()->json([ - 'message' => 'Music uploaded successfully', - 'music' => $music->load('image'), - 'url' => asset('storage/' . $path), - ]); + $path = $file->store('music', 'public'); + + if (!$path) { + return response()->json([ + 'message' => 'Failed to store the file' + ], 500); + } + + $music = Music::create([ + 'user_id' => auth()->id(), + 'title' => $data['title'], + 'artist' => $data['artist'] ?? null, + 'file_path' => $path, + 'type' => $data['type'] ?? 'private', + 'image_id' => $data['image_id'] ?? null, + 'playlist_id' => $data['playlist_id'] ?? null, + 'duration' => $data['duration'] ?? null, // Store as string + 'order' => $this->getNextOrderInPlaylist($data['playlist_id'] ?? null), + 'is_active' => true, + ]); + + return response()->json([ + 'message' => 'Music uploaded successfully', + 'music' => $music->load(['image', 'playlist', 'tags']), + 'url' => asset('storage/' . $path), + ], 201); + + } catch (\Illuminate\Validation\ValidationException $e) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => $e->errors() + ], 422); + } catch (\Exception $e) { + return response()->json([ + 'message' => 'An error occurred while uploading the music', + 'error' => $e->getMessage() + ], 500); + } } - // ✅ Get all public + user private music - public function all() + // Helper method to get next order number in playlist + private function getNextOrderInPlaylist($playlistId) { - $userId = auth()->id(); - - $music = Music::where('type', 'public') - ->orWhere(function($query) use ($userId) { - $query->where('type', 'private')->where('user_id', $userId); - }) - ->get(); - - $music = Music::with('image') // eager load image - ->where('type', 'public') - ->orWhere(function($query) use ($userId) { - $query->where('type', 'private')->where('user_id', $userId); - }) - ->get(); - - return response()->json($music); + if (!$playlistId) { + return 0; + } + + $maxOrder = Music::where('playlist_id', $playlistId)->max('order'); + return ($maxOrder ?? -1) + 1; } - // ✅ Update music + // ✅ Update music with string duration public function update(Request $request, $id) { - $music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail(); + try { + $music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail(); - $data = $request->validate([ - 'title' => 'nullable|string|max:255', - 'artist' => 'nullable|string|max:255', - 'type' => 'nullable|in:public,private', - 'file' => 'nullable|mimes:mp3,wav,ogg|max:10240', - 'image_id' => 'nullable|exists:images,id', - ]); + $data = $request->validate([ + 'title' => 'nullable|string|max:255', + 'artist' => 'nullable|string|max:255', + 'type' => 'nullable|in:public,private', + 'file' => 'nullable|mimes:mp3,wav,ogg|max:10240', + 'image_id' => 'nullable|exists:images,id', + 'playlist_id' => 'nullable|exists:music_playlists,id', + 'duration' => 'nullable|string|regex:/^(?:\d+:)?[0-5]?\d:[0-5]\d$/', + 'order' => 'nullable|integer', + 'is_active' => 'nullable|boolean', + ]); - if ($request->hasFile('file')) { - Storage::disk('public')->delete($music->file_path); - $path = $request->file('file')->store('music', 'public'); - $music->file_path = $path; + if ($request->hasFile('file')) { + // Delete old file + Storage::disk('public')->delete($music->file_path); + $path = $request->file('file')->store('music', 'public'); + $music->file_path = $path; + } + + // Update only provided fields + $music->fill($data); + $music->save(); + + return response()->json([ + 'message' => 'Music updated successfully', + 'music' => $music->load(['image', 'playlist', 'tags']), + 'url' => asset('storage/' . $music->file_path), + ]); + + } catch (\Exception $e) { + return response()->json([ + 'message' => 'An error occurred while updating the music', + 'error' => $e->getMessage() + ], 500); } - - $music->fill($data); - $music->save(); - - - return response()->json([ - 'message' => 'Music updated successfully', - 'music' => $music->load('image'), - 'url' => asset('storage/' . $music->file_path), - ]); } + + // ✅ Get all public + user private music + public function show($id) +{ + $userId = auth()->id(); + + $music = Music::with(['image', 'playlist', 'tags']) + ->where(function($query) use ($userId) { + $query->where('type', 'public') + ->orWhere(function($q) use ($userId) { + $q->where('type', 'private') + ->where('user_id', $userId); + }); + }) + ->findOrFail($id); + + return response()->json($music); +} + // ✅ Delete music public function destroy($id) { diff --git a/app/Models/Music.php b/app/Models/Music.php index 11a07ca..b372274 100644 --- a/app/Models/Music.php +++ b/app/Models/Music.php @@ -4,6 +4,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this +use Illuminate\Database\Eloquent\Relations\BelongsToMany; // ← Add this +use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this class Music extends Model { use HasFactory; @@ -19,6 +22,9 @@ class Music extends Model 'order' => 'integer', 'is_active' => 'boolean', ]; + protected $attributes = [ + 'type' => 'public', // Default value +]; // Relation to user (optional) public function user() { @@ -30,7 +36,7 @@ public function playlist(): BelongsTo } public function tags(): BelongsToMany { - return $this->belongsToMany(Tag::class, 'music_tag'); + return $this->belongsToMany(Tag::class, 'music_tags'); } protected $appends = ['url', 'image_url']; diff --git a/app/Models/MusicCategory.php b/app/Models/MusicCategory.php index 3e76707..aefa7ba 100644 --- a/app/Models/MusicCategory.php +++ b/app/Models/MusicCategory.php @@ -3,7 +3,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; - +use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this +use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this class MusicCategory extends Model { protected $table = 'music_categories'; diff --git a/app/Models/MusicPlaylist.php b/app/Models/MusicPlaylist.php index 5473680..f2491be 100644 --- a/app/Models/MusicPlaylist.php +++ b/app/Models/MusicPlaylist.php @@ -3,7 +3,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; - +use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this +use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this class MusicPlaylist extends Model { protected $table = 'music_playlists'; diff --git a/app/Models/Tag.php b/app/Models/Tag.php index f882800..002b395 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -3,7 +3,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; - +use Illuminate\Database\Eloquent\Relations\BelongsTo; // ← Add this +use Illuminate\Database\Eloquent\Relations\HasMany; // ← Add this class Tag extends Model { protected $fillable = ['name']; diff --git a/public/.htaccess b/public/.htaccess index b574a59..8291745 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -1,3 +1,18 @@ + + + php_value post_max_size 128M + php_value upload_max_filesize 128M + php_value max_execution_time 300 + php_value max_input_time 300 + + + + php_value post_max_size 128M + php_value upload_max_filesize 128M + php_value max_execution_time 300 + php_value max_input_time 300 + + Options -MultiViews -Indexes diff --git a/public/phpinfo.php b/public/phpinfo.php new file mode 100644 index 0000000..cf60860 --- /dev/null +++ b/public/phpinfo.php @@ -0,0 +1,3 @@ + diff --git a/routes/api.php b/routes/api.php index af59e18..eeeee92 100644 --- a/routes/api.php +++ b/routes/api.php @@ -15,6 +15,8 @@ use App\Http\Controllers\ImageController; use App\Http\Controllers\MusicController; use App\Http\Controllers\MediaController; +use App\Http\Controllers\MusicCategoryController; +use App\Http\Controllers\MusicPlaylistController; Route::get('/test-hash', function() { $plain = 'amnk1380'; @@ -120,10 +122,10 @@ /// music - Route::post('/music', [MusicController::class, 'store']); - Route::get('/music/all', [MusicController::class, 'all']); - Route::put('/music/{id}', [MusicController::class, 'update']); - Route::delete('/music/{id}', [MusicController::class, 'destroy']); + // Route::post('/music', [MusicController::class, 'store']); + // Route::get('/music/all', [MusicController::class, 'all']); + // Route::put('/music/{id}', [MusicController::class, 'update']); + // Route::delete('/music/{id}', [MusicController::class, 'destroy']); Route::get('/slider', [SliderController::class, 'index']); // list all sliders