feat: add detail image media and play list
This commit is contained in:
@@ -30,6 +30,8 @@ public function store(Request $request)
|
||||
'subcategory_ids.*' => 'integer|exists:sub_categories,id',
|
||||
'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',
|
||||
'duration' => 'nullable|integer',
|
||||
'is_premium' => 'nullable|boolean',
|
||||
'file' => 'nullable|file|extensions:mp3,wav,ogg,flac,aac,m4a,opus,mpga,wma,mp4,mov,m4v,webm,mkv,avi,3gp|max:512000',
|
||||
@@ -47,6 +49,7 @@ public function store(Request $request)
|
||||
|
||||
// An uploaded image file takes precedence over a provided image_id.
|
||||
$imageId = $this->uploadedImageId($request) ?? ($data['image_id'] ?? null);
|
||||
$detailImageId = $this->uploadedImageId($request, 'detail_image') ?? ($data['detail_image_id'] ?? null);
|
||||
|
||||
// When a file is uploaded, expose its public URL as external_url too
|
||||
// (older front-end versions read external_url for the playable source).
|
||||
@@ -60,6 +63,7 @@ public function store(Request $request)
|
||||
'file_path' => $path,
|
||||
'external_url' => $externalUrl,
|
||||
'image_id' => $imageId,
|
||||
'detail_image_id' => $detailImageId,
|
||||
'duration' => $data['duration'] ?? null,
|
||||
'visibility' => $data['visibility'] ?? 'public',
|
||||
'is_premium'=> $data['is_premium'] ?? false
|
||||
@@ -80,12 +84,12 @@ public function store(Request $request)
|
||||
}
|
||||
return response()->json([
|
||||
'message' => 'Media created successfully',
|
||||
'media' => $media->load(['image', 'categories', 'subCategories', 'tags']),
|
||||
'media' => $media->load(['image', 'detailImage', 'categories', 'subCategories', 'tags']),
|
||||
]);
|
||||
}
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Media::with(['image', 'categories', 'subCategories', 'myNote', 'tags','comments'])
|
||||
$query = Media::with(['image', 'detailImage', 'categories', 'subCategories', 'myNote', 'tags','comments'])
|
||||
->where(function ($q) {
|
||||
$q->where('visibility', 'public')
|
||||
->orWhere('user_id', auth()->id());
|
||||
@@ -354,6 +358,7 @@ public function show($id)
|
||||
{
|
||||
$media = Media::with([
|
||||
'image',
|
||||
'detailImage',
|
||||
'categories',
|
||||
'subCategories',
|
||||
'myNote',
|
||||
@@ -392,6 +397,7 @@ public function show($id)
|
||||
'updated_at' => $media->updated_at,
|
||||
'is_premium' => $media->is_premium,
|
||||
'image' => $media->image,
|
||||
'detail_image' => $media->detailImage,
|
||||
'categories' => $media->categories,
|
||||
'sub_categories' => $media->subCategories,
|
||||
'tags' => $media->tags,
|
||||
@@ -482,6 +488,8 @@ public function update(Request $request, $id)
|
||||
'is_premium' => 'nullable|boolean',
|
||||
'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',
|
||||
'duration' => 'nullable|integer',
|
||||
'file' => 'nullable|file|extensions:mp3,wav,ogg,flac,aac,m4a,opus,mpga,wma,mp4,mov,m4v,webm,mkv,avi,3gp|max:512000',
|
||||
'external_url' => 'nullable|string',
|
||||
@@ -522,6 +530,16 @@ public function update(Request $request, $id)
|
||||
$updateData['image_id'] = $media->image_id;
|
||||
}
|
||||
|
||||
// --- Handle detail image (shown on the show-by-id screen) ---
|
||||
$uploadedDetailImageId = $this->uploadedImageId($request, 'detail_image');
|
||||
if ($uploadedDetailImageId !== null) {
|
||||
$updateData['detail_image_id'] = $uploadedDetailImageId;
|
||||
} elseif (array_key_exists('detail_image_id', $data)) {
|
||||
$updateData['detail_image_id'] = $data['detail_image_id'];
|
||||
} else {
|
||||
$updateData['detail_image_id'] = $media->detail_image_id;
|
||||
}
|
||||
|
||||
// --- update media ---
|
||||
$media->update($updateData);
|
||||
|
||||
@@ -546,7 +564,7 @@ public function update(Request $request, $id)
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Media updated successfully',
|
||||
'media' => $media->load(['image', 'categories', 'subCategories', 'tags']),
|
||||
'media' => $media->load(['image', 'detailImage', 'categories', 'subCategories', 'tags']),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -583,7 +601,7 @@ public function toggleSaveMedia($id)
|
||||
// GET saved
|
||||
public function saved()
|
||||
{
|
||||
return auth()->user()->savedMedia()->with(['image','categories', 'subCategories', 'myNote' , 'tags'])->get();
|
||||
return auth()->user()->savedMedia()->with(['image','detailImage','categories', 'subCategories', 'myNote' , 'tags'])->get();
|
||||
}
|
||||
|
||||
// RECORD a play for the current user (feeds popular + recently played).
|
||||
@@ -621,7 +639,7 @@ public function popular(Request $request)
|
||||
})
|
||||
->withCount('plays as listeners_count') // distinct users who played
|
||||
->withSum('plays as plays_count', 'play_count') // total plays
|
||||
->with(['image', 'categories', 'subCategories', 'tags'])
|
||||
->with(['image', 'detailImage', 'categories', 'subCategories', 'tags'])
|
||||
->orderByDesc('plays_count')
|
||||
->orderByDesc('listeners_count')
|
||||
->orderByDesc('created_at')
|
||||
@@ -638,7 +656,7 @@ public function recentlyPlayed(Request $request)
|
||||
|
||||
$plays = MediaPlay::where('user_id', auth()->id())
|
||||
->whereNotNull('last_played_at')
|
||||
->with(['media' => fn ($q) => $q->with(['image', 'categories', 'subCategories', 'tags'])])
|
||||
->with(['media' => fn ($q) => $q->with(['image', 'detailImage', 'categories', 'subCategories', 'tags'])])
|
||||
->orderByDesc('last_played_at')
|
||||
->limit($limit)
|
||||
->get();
|
||||
|
||||
@@ -13,7 +13,7 @@ class MusicPlaylistController extends Controller
|
||||
|
||||
public function index(Request $request, $categoryId = null)
|
||||
{
|
||||
$query = MusicPlaylist::with(['categories', 'subcategories', 'image']);
|
||||
$query = MusicPlaylist::with(['categories', 'subcategories', 'image', 'detailImage']);
|
||||
|
||||
$categoryId = $categoryId ?? $request->input('category_id');
|
||||
|
||||
@@ -46,6 +46,8 @@ public function store(Request $request)
|
||||
'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',
|
||||
]);
|
||||
@@ -61,6 +63,7 @@ public function store(Request $request)
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -69,7 +72,7 @@ public function store(Request $request)
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Playlist created successfully',
|
||||
'playlist' => $playlist->load(['categories', 'subcategories', 'image'])
|
||||
'playlist' => $playlist->load(['categories', 'subcategories', 'image', 'detailImage'])
|
||||
], 201);
|
||||
}
|
||||
|
||||
@@ -79,6 +82,7 @@ public function show($id)
|
||||
'categories',
|
||||
'subcategories',
|
||||
'image',
|
||||
'detailImage',
|
||||
'musics' => function($q) {
|
||||
$q->where('music.is_active', true)
|
||||
->with(['image', 'tags'])
|
||||
@@ -127,6 +131,8 @@ public function update(Request $request, $id)
|
||||
'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',
|
||||
]);
|
||||
@@ -139,6 +145,9 @@ public function update(Request $request, $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);
|
||||
|
||||
@@ -152,7 +161,7 @@ public function update(Request $request, $id)
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Playlist updated successfully',
|
||||
'playlist' => $playlist->load(['categories', 'subcategories', 'image'])
|
||||
'playlist' => $playlist->load(['categories', 'subcategories', 'image', 'detailImage'])
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ class Media extends Model
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'image_id',
|
||||
'detail_image_id',
|
||||
'title',
|
||||
'caption',
|
||||
'type',
|
||||
@@ -42,6 +43,12 @@ public function image()
|
||||
return $this->belongsTo(Image::class);
|
||||
}
|
||||
|
||||
// Image shown on the detail (show-by-id) screen; image() is the list thumbnail.
|
||||
public function detailImage()
|
||||
{
|
||||
return $this->belongsTo(Image::class, 'detail_image_id');
|
||||
}
|
||||
|
||||
public function categories()
|
||||
{
|
||||
return $this->belongsToMany(Category::class, 'category_media');
|
||||
|
||||
@@ -15,7 +15,7 @@ class MusicPlaylist extends Model
|
||||
protected $table = 'music_playlists';
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'slug', 'description', 'image_id', 'order', 'is_active'
|
||||
'name', 'slug', 'description', 'image_id', 'detail_image_id', 'order', 'is_active'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@@ -54,6 +54,12 @@ public function image(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Image::class);
|
||||
}
|
||||
|
||||
// Image shown on the detail (show-by-id) screen; image() is the list thumbnail.
|
||||
public function detailImage(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Image::class, 'detail_image_id');
|
||||
}
|
||||
|
||||
public function getActiveMusicsAttribute()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Add a second image reference: image_id = list/thumbnail image,
|
||||
* detail_image_id = image shown on the detail (show-by-id) screen.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('media', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('detail_image_id')->nullable()->after('image_id');
|
||||
$table->foreign('detail_image_id')->references('id')->on('images')->nullOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('music_playlists', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('detail_image_id')->nullable()->after('image_id');
|
||||
$table->foreign('detail_image_id')->references('id')->on('images')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('media', function (Blueprint $table) {
|
||||
$table->dropForeign(['detail_image_id']);
|
||||
$table->dropColumn('detail_image_id');
|
||||
});
|
||||
|
||||
Schema::table('music_playlists', function (Blueprint $table) {
|
||||
$table->dropForeign(['detail_image_id']);
|
||||
$table->dropColumn('detail_image_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user