fix: music test apis

This commit is contained in:
2026-05-20 08:48:35 +03:30
parent 65db1bd5da
commit e446de79fd
9 changed files with 242 additions and 87 deletions
@@ -2,6 +2,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\MusicPlaylist; use App\Models\MusicPlaylist;
use App\Models\MusicCategory;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -18,6 +19,7 @@ public function index()
public function store(Request $request) public function store(Request $request)
{ {
try {
$data = $request->validate([ $data = $request->validate([
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'description' => 'nullable|string', 'description' => 'nullable|string',
@@ -26,14 +28,54 @@ public function store(Request $request)
'is_active' => 'nullable|boolean', 'is_active' => 'nullable|boolean',
]); ]);
$data['slug'] = Str::slug($data['name']); // 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); $category = MusicCategory::create($data);
return response()->json([ return response()->json([
'message' => 'Category created successfully', 'message' => 'Category created successfully',
'category' => $category->load('image') 'category' => $category->load('image')
], 201); ], 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) public function show($id)
+110 -26
View File
@@ -8,7 +8,28 @@
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class MusicController extends Controller 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); $playlist = MusicPlaylist::findOrFail($playlistId);
@@ -79,15 +100,40 @@ public function updateOrder(Request $request)
// ✅ Upload music // ✅ Upload music
public function store(Request $request) public function store(Request $request)
{ {
try {
$data = $request->validate([ $data = $request->validate([
'title' => 'required|string|max:255', 'title' => 'required|string|max:255',
'artist' => 'nullable|string|max:255', 'artist' => 'nullable|string|max:255',
'file' => 'required|mimes:mp3,wav,ogg|max:10240', // max 10MB 'file' => 'required|mimes:mp3,wav,ogg|max:20971520',
'type' => 'nullable|in:public,private', 'type' => 'nullable|in:public,private',
'image_id' => 'nullable|exists:images,id', '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);
}
$file = $request->file('file');
// Validate file size
if ($file->getSize() > 20971520) {
return response()->json([
'message' => 'File size exceeds 10MB limit'
], 422);
}
$path = $file->store('music', 'public');
if (!$path) {
return response()->json([
'message' => 'Failed to store the file'
], 500);
}
$music = Music::create([ $music = Music::create([
'user_id' => auth()->id(), 'user_id' => auth()->id(),
@@ -96,39 +142,46 @@ public function store(Request $request)
'file_path' => $path, 'file_path' => $path,
'type' => $data['type'] ?? 'private', 'type' => $data['type'] ?? 'private',
'image_id' => $data['image_id'] ?? null, '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([ return response()->json([
'message' => 'Music uploaded successfully', 'message' => 'Music uploaded successfully',
'music' => $music->load('image'), 'music' => $music->load(['image', 'playlist', 'tags']),
'url' => asset('storage/' . $path), '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 // Helper method to get next order number in playlist
public function all() private function getNextOrderInPlaylist($playlistId)
{ {
$userId = auth()->id(); if (!$playlistId) {
return 0;
$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);
} }
// ✅ Update music $maxOrder = Music::where('playlist_id', $playlistId)->max('order');
return ($maxOrder ?? -1) + 1;
}
// ✅ Update music with string duration
public function update(Request $request, $id) public function update(Request $request, $id)
{ {
try {
$music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail(); $music = Music::where('id', $id)->where('user_id', auth()->id())->firstOrFail();
$data = $request->validate([ $data = $request->validate([
@@ -137,24 +190,55 @@ public function update(Request $request, $id)
'type' => 'nullable|in:public,private', 'type' => 'nullable|in:public,private',
'file' => 'nullable|mimes:mp3,wav,ogg|max:10240', 'file' => 'nullable|mimes:mp3,wav,ogg|max:10240',
'image_id' => 'nullable|exists:images,id', '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')) { if ($request->hasFile('file')) {
// Delete old file
Storage::disk('public')->delete($music->file_path); Storage::disk('public')->delete($music->file_path);
$path = $request->file('file')->store('music', 'public'); $path = $request->file('file')->store('music', 'public');
$music->file_path = $path; $music->file_path = $path;
} }
// Update only provided fields
$music->fill($data); $music->fill($data);
$music->save(); $music->save();
return response()->json([ return response()->json([
'message' => 'Music updated successfully', 'message' => 'Music updated successfully',
'music' => $music->load('image'), 'music' => $music->load(['image', 'playlist', 'tags']),
'url' => asset('storage/' . $music->file_path), 'url' => asset('storage/' . $music->file_path),
]); ]);
} catch (\Exception $e) {
return response()->json([
'message' => 'An error occurred while updating the music',
'error' => $e->getMessage()
], 500);
} }
}
// ✅ 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 // ✅ Delete music
public function destroy($id) public function destroy($id)
+7 -1
View File
@@ -4,6 +4,9 @@
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory; 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 class Music extends Model
{ {
use HasFactory; use HasFactory;
@@ -19,6 +22,9 @@ class Music extends Model
'order' => 'integer', 'order' => 'integer',
'is_active' => 'boolean', 'is_active' => 'boolean',
]; ];
protected $attributes = [
'type' => 'public', // Default value
];
// Relation to user (optional) // Relation to user (optional)
public function user() public function user()
{ {
@@ -30,7 +36,7 @@ public function playlist(): BelongsTo
} }
public function tags(): BelongsToMany public function tags(): BelongsToMany
{ {
return $this->belongsToMany(Tag::class, 'music_tag'); return $this->belongsToMany(Tag::class, 'music_tags');
} }
protected $appends = ['url', 'image_url']; protected $appends = ['url', 'image_url'];
+2 -1
View File
@@ -3,7 +3,8 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; 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 class MusicCategory extends Model
{ {
protected $table = 'music_categories'; protected $table = 'music_categories';
+2 -1
View File
@@ -3,7 +3,8 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; 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 class MusicPlaylist extends Model
{ {
protected $table = 'music_playlists'; protected $table = 'music_playlists';
+2 -1
View File
@@ -3,7 +3,8 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; 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 class Tag extends Model
{ {
protected $fillable = ['name']; protected $fillable = ['name'];
+15
View File
@@ -1,3 +1,18 @@
<IfModule mod_php7.c>
php_value post_max_size 128M
php_value upload_max_filesize 128M
php_value max_execution_time 300
php_value max_input_time 300
</IfModule>
<IfModule mod_php8.c>
php_value post_max_size 128M
php_value upload_max_filesize 128M
php_value max_execution_time 300
php_value max_input_time 300
</IfModule>
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
<IfModule mod_negotiation.c> <IfModule mod_negotiation.c>
Options -MultiViews -Indexes Options -MultiViews -Indexes
+3
View File
@@ -0,0 +1,3 @@
<?php
phpinfo();
?>
+6 -4
View File
@@ -15,6 +15,8 @@
use App\Http\Controllers\ImageController; use App\Http\Controllers\ImageController;
use App\Http\Controllers\MusicController; use App\Http\Controllers\MusicController;
use App\Http\Controllers\MediaController; use App\Http\Controllers\MediaController;
use App\Http\Controllers\MusicCategoryController;
use App\Http\Controllers\MusicPlaylistController;
Route::get('/test-hash', function() { Route::get('/test-hash', function() {
$plain = 'amnk1380'; $plain = 'amnk1380';
@@ -120,10 +122,10 @@
/// music /// music
Route::post('/music', [MusicController::class, 'store']); // Route::post('/music', [MusicController::class, 'store']);
Route::get('/music/all', [MusicController::class, 'all']); // Route::get('/music/all', [MusicController::class, 'all']);
Route::put('/music/{id}', [MusicController::class, 'update']); // Route::put('/music/{id}', [MusicController::class, 'update']);
Route::delete('/music/{id}', [MusicController::class, 'destroy']); // Route::delete('/music/{id}', [MusicController::class, 'destroy']);
Route::get('/slider', [SliderController::class, 'index']); // list all sliders Route::get('/slider', [SliderController::class, 'index']); // list all sliders