
# Vrácení změn

Plány vrácení popisují, jak zrušit vybrané operace sloučení poté, co byly použity. Vytvářejí se před použitím, a to na základě přesných živých řádků, které existují bezprostředně před zápisem.

Nevytvářejte plán vrácení pouze ze základního snímku. Základ je společný předek, nikoli nutně aktuální stav, který se přepisuje.

## Sestavení plánu vrácení

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Snapshot\SnapshotStore;

$selection = ChangeGroupSelection::fromIds([$plan->changeGroups[0]->id]);
$operationSelection = $selection->toOperationSelection($plan);
$expectedLive = SnapshotStore::load($plan->theirsSnapshot);

$liveBeforeRows = [];
foreach ($plan->operations as $operation) {
    if (!$operationSelection->contains($operation->id)) {
        continue;
    }

    $liveBeforeRows[$operation->id] = $expectedLive
        ->getTable($operation->table)
        ?->getRow($operation->rowKey);
}

$rollback = (new RollbackPlanBuilder())->build(
    'rollback-1',
    $plan,
    $operationSelection,
    $liveBeforeRows,
    metadata: ['promotion_id' => 'promotion-1'],
);
```

Plán ukládá:

- ID plánu sloučení,
- ID vybraných operací,
- dotčené tabulky,
- opačné operace,
- původní řádky,
- očekávané výsledné řádky,
- metadata,
- kontrolní součet vrácení,
- verzi schématu.

## Kontrola odchylek

Před použitím vrácení porovnejte aktuální živé řádky s očekávanými výslednými řádky plánu. Pokud se od propagace změnil kterýkoli řádek, standardně se operace přeruší.

```php
use Merql\Rollback\RollbackDrift;

$currentRows = [
    $operationId => $currentLiveRow,
];

$drifted = (new RollbackDrift())->driftedOperationIds($rollback, $currentRows);

if ($drifted !== []) {
    throw new RuntimeException('Rollback drifted: ' . implode(', ', $drifted));
}
```

Kontrola odchylek porovnává otisky řádků, takže na pořadí sloupců nezáleží.

## Použití vrácení změn

Pokud nejsou žádné odchylky, použijte opačné operace.

```php
use Merql\Rollback\RollbackPlanApplier;

$applied = (new RollbackPlanApplier($connection))->apply($rollback);

if ($applied->hasErrors()) {
    foreach ($applied->errors() as $error) {
        echo $error . "\n";
    }
}
```

Vrácení změn použije běžný `MergeResult` vytvořený z opačných operací. Probíhá stejnou transakční cestou aplikátoru jako běžné použití sloučení.

## Serializace

Před použitím sloučení uložte plán vrácení. Tento artefakt je trvalým základem pro pozdější vrácení změn.

```php
use Merql\Rollback\RollbackPlanSerializer;

$json = RollbackPlanSerializer::toJson($rollback);
$restored = RollbackPlanSerializer::fromJson($json);
```

Aplikace by měly serializovaný plán ukládat spolu s operací, která provedla zápis do živé databáze, a s případnými auditními metadaty specifickými pro hostitelské prostředí.
