api/app/Models/Hunts.php

170 lines
4.9 KiB
PHP

<?php
namespace App\Models;
use App\Enums\HuntDifficulty;
use App\Enums\HuntStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Laravel\Scout\Searchable;
class Hunts extends Model
{
use HasFactory;
use Searchable;
protected $table = 'hunts';
protected $fillable = [
'partner_id',
'title',
'slug',
'description',
'status',
'difficulty',
'city',
'latitude',
'longitude',
'start_radius_m',
'is_public',
'estimated_duration_min',
'start_at',
'end_at',
'image',
'creator_id',
];
protected $casts = [
'is_public' => 'boolean',
'start_at' => 'datetime',
'end_at' => 'datetime',
'latitude' => 'float',
'longitude' => 'float',
'start_radius_m' => 'integer',
'estimated_duration_min' => 'integer',
'status' => HuntStatus::class,
'difficulty' => HuntDifficulty::class,
];
public function steps(): HasMany
{
return $this->hasMany(HuntSteps::class, 'hunt_id');
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'creator_id');
}
public function participants(): BelongsToMany
{
return $this->belongsToMany(User::class, 'hunt_participants', 'hunt_id', 'user_id')
->withPivot(['current_step_number', 'status'])
->withTimestamps();
}
public function totalXp(): int
{
if ($this->relationLoaded('steps')) {
return (int) $this->steps->sum('xp');
}
$stepsSumXp = $this->getAttribute('steps_sum_xp');
if ($stepsSumXp !== null) {
return (int) $stepsSumXp;
}
return (int) $this->steps()->sum('xp');
}
/**
* Configure Meilisearch index settings
*/
public function searchableOptions(): array
{
return [
'filterableAttributes' => [
'city',
'difficulty',
'status',
'is_public',
'creator_id',
'participant_ids',
'start_at_timestamp',
'end_at_timestamp',
'created_at_timestamp',
],
'sortableAttributes' => [
'start_at_timestamp',
'end_at_timestamp',
'created_at_timestamp',
],
];
}
/**
* @return array{
* id: int,
* title: string,
* slug: string,
* description: string|null,
* city: string,
* status: string,
* difficulty: string,
* is_public: bool,
* creator_id: int,
* latitude: float|null,
* longitude: float|null,
* start_radius_m: int,
* start_at: string|null,
* end_at: string|null,
* start_at_timestamp: int|null,
* end_at_timestamp: int|null,
* created_at_timestamp: int,
* participant_ids: array<int>
* }
*/
public function toSearchableArray(): array
{
$status = $this->status instanceof HuntStatus ? $this->status->value : $this->status;
$difficulty = $this->difficulty instanceof HuntDifficulty ? $this->difficulty->value : $this->difficulty;
try {
$participantIds = $this->participants()->pluck('user_id')->toArray();
} catch (\Throwable $e) {
\Illuminate\Support\Facades\Log::error('TO_SEARCHABLE_ARRAY_PARTICIPANTS_ERROR', [
'hunt_id' => $this->id,
'error' => $e->getMessage(),
]);
$participantIds = [];
}
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'description' => $this->description,
'city' => $this->city,
'status' => $status,
'difficulty' => $difficulty,
'is_public' => $this->is_public,
'creator_id' => $this->creator_id,
'latitude' => $this->latitude,
'longitude' => $this->longitude,
'start_radius_m' => $this->start_radius_m,
'start_at' => $this->start_at?->toAtomString(),
'end_at' => $this->end_at?->toAtomString(),
// Timestamps Unix pour les filtres de date dans Meilisearch
'start_at_timestamp' => $this->start_at?->timestamp,
'end_at_timestamp' => $this->end_at?->timestamp,
'created_at_timestamp' => $this->created_at->timestamp,
// IDs des participants pour le filtre de participation
'participant_ids' => $participantIds,
];
}
}