From d0d48866f0abe39822d9c8ed3b9ac8a71df9a170 Mon Sep 17 00:00:00 2001 From: AmirmahdiNourkazemi Date: Wed, 20 May 2026 15:52:28 +0330 Subject: [PATCH] feat: user rating and comment --- app/Http/Controllers/MediaController.php | 48 +++++++++++++++++++----- app/Http/Controllers/MusicController.php | 41 ++++++++++++++++++-- app/Models/Comment.php | 16 +++++++- app/Models/Media.php | 6 ++- app/Models/Music.php | 6 ++- app/Traits/HasComments.php | 48 +++++++++++++++++++++++- app/Traits/HasRatings.php | 25 ++++++++++++ 7 files changed, 171 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index 5bc147f..93e652f 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -319,9 +319,18 @@ public function filters(Request $request) ]); } - public function show($id) - { - $media = Media::with(['image', 'category', 'myNote', 'tags']) + public function show($id) +{ + $media = Media::with([ + 'image', + 'category', + 'myNote', + 'tags', + 'comments' => function($query) { + $query->with('user')->latest()->limit(10); + }, + 'ratings' + ]) ->where('id', $id) ->where(function($query) { $query->where('visibility', 'public') @@ -329,7 +338,16 @@ public function show($id) }) ->firstOrFail(); - return response()->json([ + // Get user's specific comment + $userComment = $media->userComment(); + + // Get paginated comments + $comments = $media->comments() + ->with('user') + ->latest() + ->paginate(15); + + return response()->json([ 'id' => $media->id, 'title' => $media->title, 'caption' => $media->caption, @@ -340,18 +358,28 @@ public function show($id) 'visibility' => $media->visibility, 'created_at' => $media->created_at, 'updated_at' => $media->updated_at, + 'is_premium' => $media->is_premium, 'image' => $media->image, 'category' => $media->category, 'tags' => $media->tags, 'myNote' => $media->myNote, 'is_saved' => $media->is_saved, - 'is_premium' => $media->is_premium, - 'comments' => $media->comments, - 'average_rating' => $media->average_rating, - 'user_rating' => $media->user_rating, - 'comments_count' => $media->comments()->count(), + 'statistics' => [ + 'average_rating' => $media->average_rating, + 'total_ratings' => $media->ratings_count, + 'total_comments' => $media->comments_count, + 'rating_distribution' => $media->rating_distribution, + ], + 'user_interaction' => [ + 'has_rated' => $media->has_user_rated, + 'user_rating' => $media->user_rating, + 'has_commented' => $media->has_user_commented, + 'user_comment' => $media->user_comment, + 'user_comment_id' => $media->user_comment_id, + ], + 'comments' => $comments, ]); - } +} public function rate(Request $request, $mediaId) { $request->validate([ diff --git a/app/Http/Controllers/MusicController.php b/app/Http/Controllers/MusicController.php index d880ff4..b50d1d7 100644 --- a/app/Http/Controllers/MusicController.php +++ b/app/Http/Controllers/MusicController.php @@ -223,11 +223,19 @@ public function update(Request $request, $id) // ✅ Get all public + user private music - public function show($id) +public function show($id) { $userId = auth()->id(); - $music = Music::with(['image', 'playlist', 'tags']) + $music = Music::with([ + 'image', + 'playlist', + 'tags', + 'comments' => function($query) { + $query->with('user')->latest()->limit(10); + }, + 'ratings' + ]) ->where(function($query) use ($userId) { $query->where('type', 'public') ->orWhere(function($q) use ($userId) { @@ -236,8 +244,33 @@ public function show($id) }); }) ->findOrFail($id); - - return response()->json($music); + + // Get user's specific comment + $userComment = $music->userComment(); + + // Get paginated comments for the response + $comments = $music->comments() + ->with('user') + ->latest() + ->paginate(15); + + return response()->json([ + 'music' => $music, + 'statistics' => [ + 'average_rating' => $music->average_rating, + 'total_ratings' => $music->ratings_count, + 'total_comments' => $music->comments_count, + 'rating_distribution' => $music->rating_distribution, + ], + 'user_interaction' => [ + 'has_rated' => $music->has_user_rated, + 'user_rating' => $music->user_rating, + 'has_commented' => $music->has_user_commented, + 'user_comment' => $music->user_comment, + 'user_comment_id' => $music->user_comment_id, + ], + 'comments' => $comments, + ]); } // ✅ Delete music diff --git a/app/Models/Comment.php b/app/Models/Comment.php index d629b8c..f5286d8 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -12,7 +12,7 @@ class Comment extends Model 'commentable_type', 'content', ]; - + protected $with = ['user']; public function user() { return $this->belongsTo(User::class); @@ -34,4 +34,18 @@ public function music() { return $this->belongsTo(Music::class, 'commentable_id')->where('commentable_type', Music::class); } + + // Check if comment belongs to current user + public function getIsOwnerAttribute() + { + return auth()->check() && $this->user_id === auth()->id(); + } + + // Add timestamps formatted + public function getFormattedCreatedAtAttribute() + { + return $this->created_at->diffForHumans(); + } + + protected $appends = ['is_owner', 'formatted_created_at']; } diff --git a/app/Models/Media.php b/app/Models/Media.php index 3445d44..cf16fd3 100644 --- a/app/Models/Media.php +++ b/app/Models/Media.php @@ -25,7 +25,11 @@ class Media extends Model 'average_rating', 'user_rating', 'ratings_count', - 'comments_count' + 'comments_count', + 'has_user_commented', // Add this + 'user_comment', // Add this + 'user_comment_id', // Add this + 'has_user_rated' // Add this ]; public function image() { diff --git a/app/Models/Music.php b/app/Models/Music.php index 64e8c1e..0dbb00c 100644 --- a/app/Models/Music.php +++ b/app/Models/Music.php @@ -43,7 +43,11 @@ public function tags(): BelongsToMany return $this->belongsToMany(Tag::class, 'music_tags'); } - protected $appends = ['url', 'image_url' , 'average_rating', 'user_rating', 'comments_count']; + protected $appends = ['url', 'image_url' , 'average_rating', 'user_rating', 'comments_count' , 'has_user_commented', // Add this + 'user_comment', // Add this + 'user_comment_id', // Add this + 'has_user_rated' // Add this]; + ]; public function getUrlAttribute() { diff --git a/app/Traits/HasComments.php b/app/Traits/HasComments.php index d3acc5b..0ef401f 100644 --- a/app/Traits/HasComments.php +++ b/app/Traits/HasComments.php @@ -10,11 +10,55 @@ public function comments() { return $this->morphMany(Comment::class, 'commentable'); } - + // Get user's specific comment + public function userComment() + { + if (!auth()->check()) return null; + + return $this->comments() + ->where('user_id', auth()->id()) + ->first(); + } + + // Check if user has commented + public function getHasUserCommentedAttribute() + { + return !is_null($this->userComment()); + } + + // Get user's comment content + public function getUserCommentAttribute() + { + $comment = $this->userComment(); + return $comment ? $comment->content : null; + } + + // Get user's comment id + public function getUserCommentIdAttribute() + { + $comment = $this->userComment(); + return $comment ? $comment->id : null; + } + + // Get latest comments with user info public function getLatestCommentsAttribute() { - return $this->comments()->latest()->limit(5)->get(); + return $this->comments() + ->with('user') + ->latest() + ->limit(10) + ->get(); } + + // Get paginated comments + public function getPaginatedComments($perPage = 15) + { + return $this->comments() + ->with('user') + ->latest() + ->paginate($perPage); + } + public function getCommentsCountAttribute() { diff --git a/app/Traits/HasRatings.php b/app/Traits/HasRatings.php index a38557c..426834b 100644 --- a/app/Traits/HasRatings.php +++ b/app/Traits/HasRatings.php @@ -24,9 +24,34 @@ public function getUserRatingAttribute() ->where('user_id', auth()->id()) ->value('stars'); } + // Get user's rating object + public function userRating() + { + if (!auth()->check()) return null; + + return $this->ratings() + ->where('user_id', auth()->id()) + ->first(); + } + + // Check if user has rated + public function getHasUserRatedAttribute() + { + return !is_null($this->userRating()); + } public function getRatingsCountAttribute() { return $this->ratings()->count(); } + + // Get rating distribution + public function getRatingDistributionAttribute() + { + $distribution = []; + for ($i = 1; $i <= 5; $i++) { + $distribution[$i] = $this->ratings()->where('stars', $i)->count(); + } + return $distribution; + } } \ No newline at end of file