mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 12:11:14 -05:00
- Added rides index view with search and filter options. - Created rides show view to display ride details. - Implemented API routes for rides. - Developed authentication routes for user registration, login, and email verification. - Created tests for authentication, email verification, password reset, and user profile management. - Added feature tests for rides and operators, including creation, updating, deletion, and searching. - Implemented soft deletes and caching for rides and operators. - Enhanced manufacturer and operator model tests for various functionalities.
206 lines
5.2 KiB
PHP
206 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Enums\RideCategory;
|
|
use App\Enums\RideStatus;
|
|
use App\Traits\HasSlugHistory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
class Ride extends Model
|
|
{
|
|
use SoftDeletes, HasSlugHistory;
|
|
|
|
protected $fillable = [
|
|
'name',
|
|
'slug',
|
|
'description',
|
|
'park_id',
|
|
'park_area_id',
|
|
'manufacturer_id',
|
|
'designer_id',
|
|
'ride_model_id',
|
|
'category',
|
|
'status',
|
|
'post_closing_status',
|
|
'status_since',
|
|
'opening_date',
|
|
'closing_date',
|
|
'min_height_in',
|
|
'max_height_in',
|
|
'capacity_per_hour',
|
|
'ride_duration_seconds',
|
|
];
|
|
|
|
protected $casts = [
|
|
'status' => RideStatus::class,
|
|
'category' => RideCategory::class,
|
|
'opening_date' => 'date',
|
|
'closing_date' => 'date',
|
|
'status_since' => 'date',
|
|
'min_height_in' => 'integer',
|
|
'max_height_in' => 'integer',
|
|
'capacity_per_hour' => 'integer',
|
|
'ride_duration_seconds' => 'integer',
|
|
];
|
|
|
|
// Core Relationships (Django Parity)
|
|
public function park(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Park::class);
|
|
}
|
|
|
|
public function parkArea(): BelongsTo
|
|
{
|
|
return $this->belongsTo(ParkArea::class);
|
|
}
|
|
|
|
public function manufacturer(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Manufacturer::class, 'manufacturer_id');
|
|
}
|
|
|
|
public function designer(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Designer::class);
|
|
}
|
|
|
|
public function rideModel(): BelongsTo
|
|
{
|
|
return $this->belongsTo(RideModel::class);
|
|
}
|
|
|
|
// Extended Relationships
|
|
public function coasterStats(): HasOne
|
|
{
|
|
return $this->hasOne(RollerCoasterStats::class);
|
|
}
|
|
|
|
// Photo Relationships (Polymorphic)
|
|
public function photos(): MorphMany
|
|
{
|
|
return $this->morphMany(Photo::class, 'photosable');
|
|
}
|
|
|
|
// Review Relationships
|
|
public function reviews(): MorphMany
|
|
{
|
|
return $this->morphMany(Review::class, 'reviewable');
|
|
}
|
|
|
|
public function approvedReviews(): MorphMany
|
|
{
|
|
return $this->reviews()->where('status', 'approved');
|
|
}
|
|
|
|
// Query Scopes
|
|
public function scopeActive($query)
|
|
{
|
|
return $query->where('status', 'operating');
|
|
}
|
|
|
|
public function scopeByCategory($query, $category)
|
|
{
|
|
return $query->where('category', $category);
|
|
}
|
|
|
|
public function scopeInPark($query, $parkId)
|
|
{
|
|
return $query->where('park_id', $parkId);
|
|
}
|
|
|
|
public function scopeByManufacturer($query, $manufacturerId)
|
|
{
|
|
return $query->where('manufacturer_id', $manufacturerId);
|
|
}
|
|
|
|
// Attributes & Helper Methods
|
|
public function getAverageRatingAttribute(): ?float
|
|
{
|
|
return $this->approvedReviews()->avg('rating');
|
|
}
|
|
|
|
public function getReviewCountAttribute(): int
|
|
{
|
|
return $this->approvedReviews()->count();
|
|
}
|
|
|
|
public function getDisplayNameAttribute(): string
|
|
{
|
|
return $this->name . ' at ' . $this->park->name;
|
|
}
|
|
|
|
public function getIsOperatingAttribute(): bool
|
|
{
|
|
return $this->status === RideStatus::OPERATING;
|
|
}
|
|
|
|
public function getHeightRequirementTextAttribute(): ?string
|
|
{
|
|
if (!$this->min_height_in) {
|
|
return null;
|
|
}
|
|
|
|
$text = "Must be at least {$this->min_height_in}\" tall";
|
|
|
|
if ($this->max_height_in) {
|
|
$text .= " and no taller than {$this->max_height_in}\" tall";
|
|
}
|
|
|
|
return $text;
|
|
}
|
|
|
|
// Review Management Methods
|
|
public function canBeReviewedBy(?int $userId): bool
|
|
{
|
|
if (!$userId) {
|
|
return false;
|
|
}
|
|
|
|
return !$this->reviews()
|
|
->where('user_id', $userId)
|
|
->exists();
|
|
}
|
|
|
|
public function addReview(array $data): Review
|
|
{
|
|
return $this->reviews()->create([
|
|
'user_id' => Auth::id(),
|
|
'rating' => $data['rating'],
|
|
'title' => $data['title'] ?? null,
|
|
'content' => $data['content'],
|
|
'status' => 'pending',
|
|
]);
|
|
}
|
|
|
|
// Cache Management (Future: will use HasCaching trait)
|
|
public function getCacheKey(string $suffix = ''): string
|
|
{
|
|
return "ride:{$this->id}" . ($suffix ? ":{$suffix}" : '');
|
|
}
|
|
|
|
public function clearRelatedCache(): void
|
|
{
|
|
cache()->forget($this->getCacheKey('reviews'));
|
|
cache()->forget($this->getCacheKey('statistics'));
|
|
cache()->forget($this->getCacheKey('photos'));
|
|
}
|
|
|
|
// Statistics Management (Future: will use HasStatistics trait)
|
|
public function updateStatistics(): void
|
|
{
|
|
// Placeholder for future HasStatistics trait integration
|
|
// For now, manually manage statistics
|
|
$totalReviews = $this->reviews()->count();
|
|
$averageRating = $this->reviews()->avg('rating');
|
|
|
|
// Update any related statistics tracking
|
|
$this->clearRelatedCache();
|
|
}
|
|
} |