Migration: PM-Datum korrekt übernehmen, UUID stabil, Zwei-Phasen-Runbook
- published_at = Legacy-created_at (Publikationsdatum, Frontend-Sortier- schlüssel) statt updated_at, das bei Alt-Daten oft den Massen-/Migrations- stempel trägt und ein falsches "frisches" Datum erzeugte. - created_at/updated_at per forceFill direkt im Import gesetzt (waren nicht fillable, wurden still verworfen) – Import allein ist jetzt datumssauber. - legacy:fix-timestamps korrigiert zusätzlich published_at (status=published). - PM-UUID bleibt beim Re-Import/Delta-Lauf erhalten (kein Neu-Würfeln). - MIGRATION-STEPS auf Zwei-Phasen-Strategie umgestellt: migrate, Phase 1 mit --force, Phase 2 (Delta) ohne --force, Grandfather-Re-Run, Idempotenz-Tabelle. - Tests: LegacyPressReleaseDateImportTest (published_at-Quelle, Entwurf, Force-Re-Import erhält UUID + Datum). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a2ff47e44e
commit
5a9aab7012
4 changed files with 297 additions and 40 deletions
|
|
@ -56,6 +56,9 @@ class FixLegacyTimestamps extends Command
|
|||
'legacy_table_name' => 'press_release',
|
||||
'legacy_created' => 'created_at',
|
||||
'legacy_updated' => 'updated_at',
|
||||
// Veröffentlichte Meldungen: published_at = Legacy-created_at
|
||||
// (Publikationsdatum, Sortier-/Cluster-Schlüssel im Frontend).
|
||||
'derive_published_at' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
|
@ -111,6 +114,17 @@ class FixLegacyTimestamps extends Command
|
|||
$legacyTable = $config['legacy_table_name'];
|
||||
$legacyCreated = $config['legacy_created'];
|
||||
$legacyUpdated = $config['legacy_updated'];
|
||||
$derivePublishedAt = ! empty($config['derive_published_at']);
|
||||
|
||||
// Bei veröffentlichten Meldungen muss published_at dem Publikationsdatum
|
||||
// (= Legacy-created_at) entsprechen; sonst sortiert/clustert das Frontend
|
||||
// nach einem falschen Datum.
|
||||
$publishedAtMismatch = $derivePublishedAt
|
||||
? " OR (n.status = 'published' AND (n.published_at IS NULL OR n.published_at != lc.{$legacyCreated}))"
|
||||
: '';
|
||||
$publishedAtSet = $derivePublishedAt
|
||||
? ", n.published_at = CASE WHEN n.status = 'published' THEN lc.{$legacyCreated} ELSE n.published_at END"
|
||||
: '';
|
||||
|
||||
if ($isDryRun) {
|
||||
// Nur zählen: wie viele Datensätze hätten falsche Timestamps?
|
||||
|
|
@ -123,7 +137,7 @@ class FixLegacyTimestamps extends Command
|
|||
AND m.legacy_portal = '{$portal}'
|
||||
JOIN `{$legacyDb}`.`{$legacyTable}` lc ON lc.id = m.legacy_id
|
||||
WHERE n.created_at != lc.{$legacyCreated}
|
||||
OR n.updated_at != lc.{$legacyUpdated}
|
||||
OR n.updated_at != lc.{$legacyUpdated}{$publishedAtMismatch}
|
||||
")->cnt ?? 0;
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +150,7 @@ class FixLegacyTimestamps extends Command
|
|||
JOIN `{$legacyDb}`.`{$legacyTable}` lc ON lc.id = m.legacy_id
|
||||
SET
|
||||
n.created_at = lc.{$legacyCreated},
|
||||
n.updated_at = lc.{$legacyUpdated}
|
||||
n.updated_at = lc.{$legacyUpdated}{$publishedAtSet}
|
||||
");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,12 +139,14 @@ class PressReleaseImporter
|
|||
$status = self::STATUS_MAP[$row->status] ?? PressReleaseStatus::Draft;
|
||||
$language = in_array($row->language, ['de', 'en']) ? $row->language : 'de';
|
||||
|
||||
// Beim Update (--force): bestehenden Slug behalten, um Kollisionen zu vermeiden.
|
||||
// Beim Update (--force): bestehende uuid + slug behalten – öffentliche
|
||||
// Referenzen (API-uuid, slug-URLs) dürfen sich beim Re-Import/Delta-Lauf
|
||||
// nicht ändern.
|
||||
$existingPr = $alreadyImported
|
||||
? PressRelease::withoutGlobalScopes()
|
||||
->where('legacy_portal', $legacyPortal)
|
||||
->where('legacy_id', $row->id)
|
||||
->first(['id', 'slug'])
|
||||
->first(['id', 'uuid', 'slug'])
|
||||
: null;
|
||||
|
||||
$slug = $existingPr?->slug ?? $this->uniqueSlug(
|
||||
|
|
@ -154,18 +156,23 @@ class PressReleaseImporter
|
|||
$existingPr?->id,
|
||||
);
|
||||
|
||||
// Veröffentlichungsdatum = Legacy-`created_at` (Anlage-/Publikationsdatum,
|
||||
// im Altsystem der indexierte Sortierschlüssel). Bewusst NICHT `updated_at`:
|
||||
// dort steht der letzte Bearbeitungs-/Massen-Update-Stempel (bei vielen
|
||||
// Alt-Datensätzen das Migrationsdatum), was im Frontend – das nach
|
||||
// `published_at` sortiert/clustert – ein falsches, "frisches" Datum erzeugt.
|
||||
$publishedAt = ($status === PressReleaseStatus::Published)
|
||||
? ($row->updated_at ?? $row->created_at)
|
||||
? $row->created_at
|
||||
: null;
|
||||
|
||||
$pr = PressRelease::withoutTimestamps(function () use (
|
||||
$legacyPortal, $row, $portal, $userId, $companyId, $categoryId,
|
||||
$language, $slug, $status, $publishedAt,
|
||||
$language, $slug, $status, $publishedAt, $existingPr,
|
||||
): PressRelease {
|
||||
return PressRelease::withoutGlobalScopes()->updateOrCreate(
|
||||
$pr = PressRelease::withoutGlobalScopes()->updateOrCreate(
|
||||
['legacy_portal' => $legacyPortal, 'legacy_id' => $row->id],
|
||||
[
|
||||
'uuid' => (string) Str::uuid(),
|
||||
'uuid' => $existingPr?->uuid ?? (string) Str::uuid(),
|
||||
'portal' => $portal->value,
|
||||
'user_id' => $userId,
|
||||
'company_id' => $companyId,
|
||||
|
|
@ -182,10 +189,19 @@ class PressReleaseImporter
|
|||
'teaser_end' => max(0, (int) ($row->teaser_end ?? 0)),
|
||||
'no_export' => (bool) ($row->no_export ?? false),
|
||||
'published_at' => $publishedAt,
|
||||
'created_at' => $row->created_at ?? now(),
|
||||
'updated_at' => $row->updated_at ?? $row->created_at ?? now(),
|
||||
]
|
||||
);
|
||||
|
||||
// created_at/updated_at sind nicht fillable und werden von
|
||||
// updateOrCreate verworfen → direkt setzen, damit das Anlage-/
|
||||
// Bearbeitungsdatum bereits durch den Import korrekt migriert ist
|
||||
// (legacy:fix-timestamps bleibt der schnelle Cross-DB-Resync).
|
||||
$pr->forceFill([
|
||||
'created_at' => $row->created_at ?? now(),
|
||||
'updated_at' => $row->updated_at ?? $row->created_at ?? now(),
|
||||
])->save();
|
||||
|
||||
return $pr;
|
||||
});
|
||||
|
||||
foreach ($images->get($row->id, []) as $img) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue