'boolean', 'read_at' => 'datetime', 'expires_at' => 'datetime', ]; // --------------------------------------------------------------- // Relationships // --------------------------------------------------------------- public function user(): BelongsTo { return $this->belongsTo(User::class); } public function achievement(): BelongsTo { return $this->belongsTo(Achievement::class); } // --------------------------------------------------------------- // Scopes // --------------------------------------------------------------- /** Notifications the user hasn't opened yet. */ public function scopeUnread(Builder $query): void { $query->whereNull('read_at'); } /** Not expired (no expiry set, or expiry is in the future). */ public function scopeActive(Builder $query): void { $query->where(function (Builder $q) { $q->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }); } /** Only achievement notifications. */ public function scopeForAchievements(Builder $query): void { $query->where('is_achievement', true); } // --------------------------------------------------------------- // Helpers // --------------------------------------------------------------- public function markAsRead(): void { if (! $this->read_at) { $this->update(['read_at' => now()]); } } public function isExpired(): bool { return $this->expires_at !== null && $this->expires_at->isPast(); } }