From bebf685f32b31d6e20eb694ea714fb7fc493bf15 Mon Sep 17 00:00:00 2001 From: AmirmahdiNourkazemi Date: Wed, 26 Nov 2025 09:34:33 +0330 Subject: [PATCH] feat: add tags --- app/Http/Controllers/MediaController.php | 45 +++++++++++++++++-- app/Models/Media.php | 5 ++- app/Models/Tag.php | 4 ++ ...25_11_26_053224_create_media_tag_table.php | 29 ++++++++++++ routes/api.php | 2 +- 5 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 database/migrations/2025_11_26_053224_create_media_tag_table.php diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index fc78309..7d7fda5 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -4,6 +4,8 @@ use App\Models\Media; use App\Models\Category; +use App\Models\Tag; + use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; @@ -24,6 +26,9 @@ public function store(Request $request) 'file' => 'nullable|mimes:mp3,wav,mp4,mov|max:50480', 'external_url' => 'nullable|string', 'visibility' => 'nullable|in:public,private', + + 'tags' => 'nullable|array', + 'tags.*' => 'string', ]); $categoryId = $data['category_id'] ?? null; @@ -51,17 +56,26 @@ public function store(Request $request) 'duration' => $data['duration'] ?? null, 'visibility' => $data['visibility'] ?? 'public', ]); + if (!empty($data['tags'])) { + $tagIds = []; + foreach ($data['tags'] as $tagName) { + $tag = Tag::firstOrCreate(['name' => $tagName]); + $tagIds[] = $tag->id; + } + + $media->tags()->sync($tagIds); + } return response()->json([ 'message' => 'Media created successfully', - 'media' => $media->load(['image', 'category']), + 'media' => $media->load(['image', 'category' , 'tags']), ]); } // GET all visible media public function index() { - $media = Media::with(['image', 'category', 'myNote']) + $media = Media::with(['image', 'category', 'myNote' , 'tags']) ->where('visibility', 'public') ->orWhere('user_id', auth()->id()) ->get(); @@ -69,6 +83,17 @@ public function index() return response()->json($media); } + public function show($id) + { + $media = Media::with(['image', 'category', 'myNote' , 'tags']) + ->where('id', $id) + ->where('visibility', 'public') + ->orWhere('user_id', auth()->id()) + ->firstOrFail(); + + return response()->json($media); + } + // UPDATE media public function update(Request $request, $id) { @@ -90,6 +115,8 @@ public function update(Request $request, $id) 'file' => 'nullable|mimes:mp3,wav,mp4,mov|max:50480', 'external_url' => 'nullable|string', 'visibility' => 'nullable|in:public,private', + 'tags' => 'nullable|array', + 'tags.*' => 'string', ]); // --- handle auto-create category --- @@ -113,10 +140,20 @@ public function update(Request $request, $id) // --- update media --- $media->update($data); + if (isset($data['tags'])) { + $tagIds = []; + + foreach ($data['tags'] as $tagName) { + $tag = Tag::firstOrCreate(['name' => $tagName]); + $tagIds[] = $tag->id; + } + + $media->tags()->sync($tagIds); + } return response()->json([ 'message' => 'Media updated successfully', - 'media' => $media->load(['image', 'category']), + 'media' => $media->load(['image', 'category' , 'tags']), ]); } @@ -145,7 +182,7 @@ public function saveMedia($id) // GET saved public function saved() { - return auth()->user()->savedMedia()->with(['image','category' , 'myNote'])->get(); + return auth()->user()->savedMedia()->with(['image','category' , 'myNote' , 'tags'])->get(); } public function storeNote(Request $request, $mediaId) diff --git a/app/Models/Media.php b/app/Models/Media.php index fb6a080..512ceb6 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -28,7 +28,10 @@ public function category() { return $this->belongsTo(Category::class); } - + public function tags() + { + return $this->belongsToMany(Tag::class, 'media_tag'); + } public function notes() { return $this->morphMany(Note::class, 'noteable'); diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 288db31..f882800 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -12,5 +12,9 @@ public function questions() { return $this->belongsToMany(Question::class); } + public function media() + { + return $this->belongsToMany(Media::class); + } } diff --git a/database/migrations/2025_11_26_053224_create_media_tag_table.php b/database/migrations/2025_11_26_053224_create_media_tag_table.php new file mode 100644 index 0000000..b0fd78c --- /dev/null +++ b/database/migrations/2025_11_26_053224_create_media_tag_table.php @@ -0,0 +1,29 @@ +id(); + $table->foreignId('media_id')->constrained()->onDelete('cascade'); + $table->foreignId('tag_id')->constrained()->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('media_tag'); + } +}; diff --git a/routes/api.php b/routes/api.php index 7272f33..0496fcf 100644 --- a/routes/api.php +++ b/routes/api.php @@ -139,7 +139,7 @@ Route::get('/media', [MediaController::class, 'index']); Route::put('/media/{id}', [MediaController::class, 'update']); Route::delete('/media/{id}', [MediaController::class, 'destroy']); - + Route::get('/media/{id}', [MediaController::class, 'show']); Route::post('/media/{id}/save', [MediaController::class, 'saveMedia']); Route::get('/media/saved', [MediaController::class, 'saved']);