Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
4.17% covered (danger)
4.17%
1 / 24
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Feed
4.17% covered (danger)
4.17%
1 / 24
16.67% covered (danger)
16.67%
1 / 6
161.74
0.00% covered (danger)
0.00%
0 / 1
 getTypeDisplayAttribute
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 getStatusAttribute
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 channels
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 activeChannels
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 articles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 language
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace App\Models;
4
5use Database\Factories\FeedFactory;
6use Illuminate\Database\Eloquent\Factories\HasFactory;
7use Illuminate\Database\Eloquent\Model;
8use Illuminate\Database\Eloquent\Relations\BelongsTo;
9use Illuminate\Database\Eloquent\Relations\BelongsToMany;
10use Illuminate\Database\Eloquent\Relations\HasMany;
11use Illuminate\Support\Carbon;
12
13/**
14 * @property int $id
15 * @property string $name
16 * @property string $url
17 * @property string $type
18 * @property int $language_id
19 * @property Language|null $language
20 * @property string $description
21 * @property array<string, mixed> $settings
22 * @property bool $is_active
23 * @property Carbon|null $last_fetched_at
24 * @property Carbon $created_at
25 * @property Carbon $updated_at
26 * @method static orderBy(string $string, string $string1)
27 * @method static where(string $string, true $true)
28 * @method static findOrFail(mixed $feed_id)
29 */
30class Feed extends Model
31{
32    /** @use HasFactory<FeedFactory> */
33    use HasFactory;
34    private const RECENT_FETCH_THRESHOLD_HOURS = 2;
35    private const DAILY_FETCH_THRESHOLD_HOURS = 24;
36
37    protected $fillable = [
38        'name',
39        'url',
40        'type',
41        'language_id',
42        'description',
43        'settings',
44        'is_active',
45        'last_fetched_at'
46    ];
47
48    protected $casts = [
49        'settings' => 'array',
50        'is_active' => 'boolean',
51        'last_fetched_at' => 'datetime'
52    ];
53
54    public function getTypeDisplayAttribute(): string
55    {
56        return match ($this->type) {
57            'website' => 'Website',
58            'rss' => 'RSS Feed',
59            default => 'Unknown'
60        };
61    }
62
63    public function getStatusAttribute(): string
64    {
65        if (!$this->is_active) {
66            return 'Inactive';
67        }
68
69        if (!$this->last_fetched_at) {
70            return 'Never fetched';
71        }
72
73        $hoursAgo = $this->last_fetched_at->diffInHours(now());
74
75        if ($hoursAgo < self::RECENT_FETCH_THRESHOLD_HOURS) {
76            return 'Recently fetched';
77        } elseif ($hoursAgo < self::DAILY_FETCH_THRESHOLD_HOURS) {
78            return "Fetched {$hoursAgo}h ago";
79        } else {
80            return "Fetched " . $this->last_fetched_at->diffForHumans();
81        }
82    }
83
84    /**
85     * @return BelongsToMany<PlatformChannel, $this, Route>
86     */
87    public function channels(): BelongsToMany
88    {
89        return $this->belongsToMany(PlatformChannel::class, 'routes')
90            ->using(Route::class)
91            ->withPivot(['is_active', 'priority', 'filters'])
92            ->withTimestamps();
93    }
94
95    /**
96     * @return BelongsToMany<PlatformChannel, $this, Route>
97     */
98    public function activeChannels(): BelongsToMany
99    {
100        return $this->channels()
101            ->wherePivot('is_active', true)
102            ->orderByPivot('priority', 'desc');
103    }
104
105    /**
106     * @return HasMany<Article, $this>
107     */
108    public function articles(): HasMany
109    {
110        return $this->hasMany(Article::class);
111    }
112
113    /**
114     * @return BelongsTo<Language, $this>
115     */
116    public function language(): BelongsTo
117    {
118        return $this->belongsTo(Language::class);
119    }
120}