Files

249 lines
6.3 KiB
PHP
Raw Permalink Normal View History

2026-03-15 17:08:23 +07:00
<?php
declare(strict_types=1);
namespace Modules\RestaurantDelivery\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\RestaurantDelivery\Traits\HasRestaurant;
use Modules\RestaurantDelivery\Traits\HasUuid;
class DeliveryRating extends Model
{
use HasFactory, HasRestaurant, HasUuid, SoftDeletes;
public const TABLE_NAME = 'restaurant_delivery_ratings';
protected $table = self::TABLE_NAME;
protected $fillable = [
'delivery_id',
'rider_id',
'customer_id',
'restaurant_id',
'overall_rating',
'speed_rating',
'communication_rating',
'food_condition_rating',
'professionalism_rating',
'review',
'is_anonymous',
'tags',
'is_restaurant_rating',
'is_approved',
'is_featured',
'moderation_status',
'moderation_notes',
'rider_response',
'rider_responded_at',
'helpful_count',
'not_helpful_count',
];
protected $casts = [
'overall_rating' => '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'];
}
}