'integer', 'speed_rating' => 'integer', 'communication_rating' => 'integer', 'food_condition_rating' => 'integer', 'professionalism_rating' => 'integer', 'is_anonymous' => 'boolean', 'is_restaurant_rating' => 'boolean', 'is_approved' => 'boolean', 'is_featured' => 'boolean', 'tags' => 'array', 'rider_responded_at' => 'datetime', ]; protected $appends = [ 'average_category_rating', 'star_display', ]; /* |-------------------------------------------------------------------------- | Relationships |-------------------------------------------------------------------------- */ public function delivery(): BelongsTo { return $this->belongsTo(Delivery::class, 'delivery_id'); } public function rider(): BelongsTo { return $this->belongsTo(Rider::class, 'rider_id'); } public function customer(): BelongsTo { return $this->belongsTo(config('auth.providers.users.model'), 'customer_id'); } /* |-------------------------------------------------------------------------- | Accessors |-------------------------------------------------------------------------- */ public function getAverageCategoryRatingAttribute(): ?float { $ratings = array_filter([ $this->speed_rating, $this->communication_rating, $this->food_condition_rating, $this->professionalism_rating, ]); if (empty($ratings)) { return null; } return round(array_sum($ratings) / count($ratings), 2); } public function getStarDisplayAttribute(): string { $filled = str_repeat('★', $this->overall_rating); $empty = str_repeat('☆', 5 - $this->overall_rating); return $filled.$empty; } /* |-------------------------------------------------------------------------- | Scopes |-------------------------------------------------------------------------- */ public function scopeApproved($query) { return $query->where('is_approved', true); } public function scopeFeatured($query) { return $query->where('is_featured', true); } public function scopeFromCustomers($query) { return $query->where('is_restaurant_rating', false); } public function scopeFromRestaurants($query) { return $query->where('is_restaurant_rating', true); } public function scopeWithReview($query) { return $query->whereNotNull('review'); } public function scopeHighRated($query, int $minRating = 4) { return $query->where('overall_rating', '>=', $minRating); } public function scopeLowRated($query, int $maxRating = 2) { return $query->where('overall_rating', '<=', $maxRating); } public function scopeRecent($query, int $days = 30) { return $query->where('created_at', '>=', now()->subDays($days)); } /* |-------------------------------------------------------------------------- | Methods |-------------------------------------------------------------------------- */ public function approve(): void { $this->update([ 'is_approved' => true, 'moderation_status' => 'approved', ]); } public function reject(string $reason): void { $this->update([ 'is_approved' => false, 'moderation_status' => 'rejected', 'moderation_notes' => $reason, ]); } public function feature(): void { $this->update(['is_featured' => true]); } public function unfeature(): void { $this->update(['is_featured' => false]); } public function addRiderResponse(string $response): void { $this->update([ 'rider_response' => $response, 'rider_responded_at' => now(), ]); } public function markHelpful(): void { $this->increment('helpful_count'); } public function markNotHelpful(): void { $this->increment('not_helpful_count'); } public function getHelpfulnessScore(): float { $total = $this->helpful_count + $this->not_helpful_count; if ($total === 0) { return 0; } return round(($this->helpful_count / $total) * 100, 2); } public function hasTag(string $tag): bool { return in_array($tag, $this->tags ?? []); } public function getCategoryRatings(): array { return array_filter([ 'speed' => $this->speed_rating, 'communication' => $this->communication_rating, 'food_condition' => $this->food_condition_rating, 'professionalism' => $this->professionalism_rating, ]); } public static function getPositiveTags(): array { return ['friendly', 'fast', 'careful', 'professional', 'polite', 'on_time']; } public static function getNegativeTags(): array { return ['late', 'rude', 'careless', 'unprofessional', 'no_communication']; } }