presseportale/app/Console/Commands/BackfillPublishedEver.php
Kevin Adametz 46b89e1d23 images: Lebenszyklus — Veröffentlichungsgate, published_ever_at, Snapshot der veröffentlichten Fassung, Sofortlöschung, Legacy-Bereinigung, Beweiszugriff, Korrekturweg (Bildkennzeichnung Block E)
- publish()/changeStatusFromAdmin(): kein Bild aus Formular v2 ohne gültige Bestätigung (ImageRightsConfirmationRequiredException, Admin-Seite meldet es); published_ever_at an PM und Bildern; PublishedImageSnapshot (Cover + JSON mit Nachweis/Kennzeichnung)
- deleteFromAdmin() und press-releases:purge-drafts löschen Bilder nie veröffentlichter Meldungen samt Original und Bestätigung sofort (E4, O1)
- press-releases:backfill-published-ever (Status-Log), press-release-images:purge-inactive-legacy (E-B11, Trockenlauf Standard)
- Admin-Route für Beweisoriginal und veröffentlichte Fassung nur mit Zweck, ActivityType EvidenceAccess; Architekturtest: original_path nur in vier Dateien
- ImageCorrectionService + image_declaration_corrections (insert-only), withheld_at blendet das Bild öffentlich aus (publiclyVisible-Scope), Korrekturpanel auf der Admin-Seite
- Systemstatus: fehlgeschlagene Herkunftsprüfungen der letzten 7 Tage

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 09:53:49 +00:00

62 lines
2.6 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Enums\PressReleaseStatus;
use App\Models\PressRelease;
use App\Models\PressReleaseStatusLog;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
/**
* Bildkennzeichnung E.1: `published_ever_at` für den Bestand — jede
* Meldung, die heute veröffentlicht ist, ein `published_at` trägt oder
* laut Status-Log jemals auf `published` stand, war öffentlich. Ihre Bilder
* erben den Zeitpunkt. Idempotent, nur NULL-Zeilen werden gesetzt.
*/
class BackfillPublishedEver extends Command
{
protected $signature = 'press-releases:backfill-published-ever {--dry-run : Nur zählen}';
protected $description = 'Setzt published_ever_at für Meldungen und Bilder, die jemals öffentlich waren';
public function handle(): int
{
$candidates = PressRelease::withoutGlobalScopes()
->whereNull('published_ever_at')
->where(function ($query): void {
$query->whereNotNull('published_at')
->orWhere('status', PressReleaseStatus::Published->value)
->orWhereIn('id', PressReleaseStatusLog::query()->where('to_status', PressReleaseStatus::Published->value)->select('press_release_id'));
});
$count = $candidates->count();
$this->info("Meldungen ohne published_ever_at, die öffentlich waren: {$count}");
if ($this->option('dry-run') || $count === 0) {
return self::SUCCESS;
}
$updated = 0;
$candidates->select(['id', 'published_at', 'created_at'])->chunkById(1000, function ($releases) use (&$updated): void {
foreach ($releases as $release) {
$firstLog = PressReleaseStatusLog::query()
->where('press_release_id', $release->id)
->where('to_status', PressReleaseStatus::Published->value)
->min('created_at');
$at = collect([$release->published_at, $firstLog, $release->created_at])->filter()->min();
DB::table('press_releases')->where('id', $release->id)->update(['published_ever_at' => $at]);
DB::table('press_release_images')->where('press_release_id', $release->id)->whereNull('published_ever_at')
->update(['published_ever_at' => DB::raw('CASE WHEN created_at > '.DB::getPdo()->quote((string) $at).' THEN created_at ELSE '.DB::getPdo()->quote((string) $at).' END')]);
$updated++;
}
});
$this->info("Gesetzt: {$updated} Meldungen samt Bildern.");
return self::SUCCESS;
}
}