*/ private const MUTABLE = ['legal_hold', 'legal_hold_note', 'legal_hold_set_by', 'legal_hold_set_at', 'retain_until', 'deleted_at', 'payload_path']; protected $fillable = [ 'subject_type', 'subject_id', 'press_release_id', 'event', 'actor_type', 'actor_id', 'reason_category', 'summary', 'payload_path', 'payload_hash', 'payload_bytes', 'retain_until', 'legal_hold', 'legal_hold_note', 'legal_hold_set_by', 'legal_hold_set_at', 'deleted_at', 'created_at', ]; protected function casts(): array { return [ 'event' => ArchiveEvent::class, 'reason_category' => ArchiveReasonCategory::class, 'summary' => 'array', 'retain_until' => 'date', 'legal_hold' => 'boolean', 'legal_hold_set_at' => 'datetime', 'deleted_at' => 'datetime', 'created_at' => 'datetime', ]; } protected static function booted(): void { static::updating(function (self $entry): void { $illegal = array_diff(array_keys($entry->getDirty()), self::MUTABLE); if ($illegal !== []) { throw new LogicException('archive_entries ist append-only; geändert werden dürfen nur legal_hold, retain_until und der Tombstone (versucht: '.implode(', ', $illegal).').'); } }); static::deleting(fn () => throw new LogicException('archive_entries werden nie gelöscht; der Tombstone bleibt.')); } public function isTombstoned(): bool { return $this->deleted_at !== null; } /** * Frist: 31.12. des Bezugsjahres plus die konfigurierten Jahre. */ public static function retentionEndFor(\DateTimeInterface $reference): Carbon { return Carbon::instance($reference)->endOfYear()->addYears((int) config('archive.retention_years', 3))->startOfDay(); } /** * @param Builder $query * @return Builder */ public function scopeDueForPurge(Builder $query): Builder { return $query->whereNull('deleted_at')->where('legal_hold', false)->whereDate('retain_until', '<', today()); } /** * @return MorphTo */ public function subject(): MorphTo { return $this->morphTo(); } /** * @return HasMany */ public function accessLog(): HasMany { return $this->hasMany(ArchiveAccessLog::class, 'entry_id'); } }