$settings * @property bool $is_active * @property Carbon|null $last_fetched_at * @property Carbon $created_at * @property Carbon $updated_at * @method static orderBy(string $string, string $string1) * @method static where(string $string, true $true) * @method static findOrFail(mixed $feed_id) */ class Feed extends Model { /** @use HasFactory */ use HasFactory; private const RECENT_FETCH_THRESHOLD_HOURS = 2; private const DAILY_FETCH_THRESHOLD_HOURS = 24; protected $fillable = [ 'name', 'url', 'type', 'language_id', 'description', 'settings', 'is_active', 'last_fetched_at' ]; protected $casts = [ 'settings' => 'array', 'is_active' => 'boolean', 'last_fetched_at' => 'datetime' ]; public function getTypeDisplayAttribute(): string { return match ($this->type) { 'website' => 'Website', 'rss' => 'RSS Feed', default => 'Unknown' }; } public function getStatusAttribute(): string { if (!$this->is_active) { return 'Inactive'; } if (!$this->last_fetched_at) { return 'Never fetched'; } $hoursAgo = $this->last_fetched_at->diffInHours(now()); if ($hoursAgo < self::RECENT_FETCH_THRESHOLD_HOURS) { return 'Recently fetched'; } elseif ($hoursAgo < self::DAILY_FETCH_THRESHOLD_HOURS) { return "Fetched {$hoursAgo}h ago"; } else { return "Fetched " . $this->last_fetched_at->diffForHumans(); } } /** * @return BelongsToMany */ public function channels(): BelongsToMany { return $this->belongsToMany(PlatformChannel::class, 'routes') ->withPivot(['is_active', 'priority', 'filters']) ->withTimestamps(); } /** * @return BelongsToMany */ public function activeChannels(): BelongsToMany { return $this->channels() ->wherePivot('is_active', true) ->orderByPivot('priority', 'desc'); } /** * @return HasMany */ public function articles(): HasMany { return $this->hasMany(Article::class); } /** * @return BelongsTo */ public function language(): BelongsTo { return $this->belongsTo(Language::class); } }