
# Liitmisplaanid

`MergeResult` on liitmise käivitatav väljund. `MergePlan` on selle väljundi kontrollitav vorm. Kasutage seda siis, kui rakendus peab enne muudatuste sihtandmebaasi kirjutamist neid kuvama, ette valmistama, serialiseerima või valikuliselt rakendama.

```php
use Merql\Merql;

$plan = Merql::plan(
    id: 'plan-2026-06-06',
    base: 'base',
    ours: 'ours',
    theirs: 'theirs',
    metadata: ['sandbox_id' => 'ow_123'],
);
```

`Merql::plan()` laadib kolm nimega hetktõmmist ja liidab need veerutasemel, nagu `Merql::merge()`. Lahtritaseme liitmist TEXT- ja JSON-veergude jaoks kasutava plaani koostamiseks looge tulemus ise ja edastage see objektile `MergePlanBuilder`:

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Plan\MergePlanBuilder;
use Merql\Snapshot\SnapshotStore;

$base = SnapshotStore::load('base');
$ours = SnapshotStore::load('ours');
$theirs = SnapshotStore::load('theirs');

$result = (new ThreeWayMerge(CellMergeConfig::auto()))->merge($base, $ours, $theirs);
$plan = (new MergePlanBuilder())->build('plan-2026-06-06', $base, $ours, $theirs, $result);
```

Plaan sisaldab järgmist:

- selle koostamiseks kasutatud hetktõmmiste nimed,
- tehete, konfliktide, skeemierinevuste, tabelite ja rühmade kokkuvõte,
- stabiilsed tehte-ID-d,
- stabiilsed muudatuste rühma ID-d,
- rea identiteediveerud ja identiteediväärtused,
- iga tehte rea alus-, meie, nende ja tulemusväärtused,
- hostrakenduse valikulised metaandmed,
- plaani räsi,
- skeemi versioon.

## Stabiilsed ID-d

Tehte ID-d on sama tehte sisu korral deterministlikud. Need tuletatakse tabeli nimest, reavõtmest, tehte tüübist, tehte allikast, muudetud veergudest ja tulemusväärtustest.

Muudatuste rühma ID-d on sama rühma sisu korral deterministlikud. Vaikimisi koostaja loob iga tehte jaoks eraldi tehte rühma. Hostrakendused saavad tehete loendist koostada suuremaid valdkonnapõhiseid rühmi ja serialiseerida need sama DTO-kujuga.

```php
foreach ($plan->changeGroups as $group) {
    echo $group->id . ': ' . implode(', ', $group->operationIds);
}
```

## Tehte kuju

Iga `MergePlanOperation` sisaldab ülevaatuseks ja tagasipööramiseks piisavalt konteksti.

```php
$operation = $plan->operations[0];

$operation->id;              // stable operation ID
$operation->type;            // insert, update, delete
$operation->table;           // canonical table name
$operation->rowKey;          // encoded identity key
$operation->identityColumns; // columns used for identity
$operation->identityValues;  // decoded identity values
$operation->source;          // ours, theirs, or merged
$operation->baseRow;         // row in base snapshot, or null
$operation->oursRow;         // row in ours snapshot, or null
$operation->theirsRow;       // row in theirs snapshot, or null
$operation->resultValues;    // values the merge would write
$operation->changedColumns;  // changed result columns
```

## Valikud

Kasutage `ChangeGroupSelection`, kui UI valmistab rühmad ette. Rakendamisel või tagasipööramise artefaktide loomisel teisendage see objektiks `OperationSelection`.

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;

$selection = ChangeGroupSelection::fromIds([
    $plan->changeGroups[0]->id,
]);

$operationSelection = $selection->toOperationSelection($plan);

$selectedResult = (new SelectedMergeResultFactory())->fromOperationSelection(
    $plan,
    $operationSelection,
    SnapshotStore::load($plan->baseSnapshot),
);
```

Valitud tulemus on puhas `MergeResult`, mis sisaldab ainult valitud tehteid. Selle saab edastada objektile `Merql::apply()` või `Merql::applyGuarded()`.

## Sihtandmebaasist puuduvate muudatuste valimine

Plaan sisaldab mõlema poole tehteid, sest liitmise tulemus muudab aluse liidetud olekuks. Kui sihtandmebaas sisaldab juba ühe poole muudatusi, valige ainult need tehted, mida seal veel pole. Kui sihtandmebaas sisaldab nende muudatusi, näiteks kui aktiivne andmebaas saab liivakastikoopia muudatused, hõlmab see kõiki tehteid, mille allikas on `ours`, ning ka tehteid `merged`, mis ühendavad mõlema poole veerumuudatused:

```php
use Merql\Plan\OperationSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;

// Rows with an unresolved conflict stay out of the selection.
$conflicted = [];
foreach ($plan->conflicts as $conflict) {
    $conflicted[$conflict->table . "\0" . $conflict->rowKey] = true;
}

$ids = [];
foreach ($plan->operations as $operation) {
    $missing = $operation->source === 'ours'
        || ($operation->source === 'merged' && $operation->type === 'update');

    if ($missing && !isset($conflicted[$operation->table . "\0" . $operation->rowKey])) {
        $ids[] = $operation->id;
    }
}

$selected = (new SelectedMergeResultFactory())->fromOperationSelection(
    $plan,
    OperationSelection::fromIds($ids),
    SnapshotStore::load($plan->baseSnapshot),
);
```

Selle sihtandmebaasi olemasolevate muudatuste hulka kuuluvad need, mille allikas on `theirs`, ning tehted `merged` (read, mille mõlemad pooled kustutasid).

Konfliktidega liitmisest koostatud plaan loetleb endiselt iga konfliktse rea kohta tehte, kui mõlemad pooled seda uuendasid. Selle tulemusväärtused sisaldavad konfliktse veeru puhul meie väärtust ja `SelectedMergeResultFactory` tagastab iga valiku korral puhta tulemuse. Tehte ja muudatuste rühma `conflictState` on praegu ka sellisel juhul `clean` ([#9](https://github.com/inline0/merql/issues/9)), seega kontrollige ise ülaltoodud viisil `$plan->conflicts` või lahendage konfliktid `ConflictResolver` abil ja koostage plaan lahendatud tulemusest, kasutades `MergePlanBuilder`. Vaadake jaotist [Kaitstud rakendamine](https://merql.inline0.dev/et/docs/apply/guarded), kus kirjeldatakse valiku kirjutamist koos aktiivse rea eeltingimustega.

## Järjestamine

Liitmisplaanide ja valikute serialiseerimine JSON on stabiilne. Salvestage serialiseeritud plaan, kui hilisem rakendamine peab kasutama täpselt üle vaadatud tehete kogumit.

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\MergePlanSerializer;

$json = MergePlanSerializer::toJson($plan);
$restored = MergePlanSerializer::fromJson($json);

$selectionJson = json_encode($selection->toArray(), JSON_THROW_ON_ERROR);
$selection = ChangeGroupSelection::fromArray(
    json_decode($selectionJson, true, flags: JSON_THROW_ON_ERROR),
);
```

## Teise nimega hetktõmmised

Liitmisplaanid kasutavad kanoonilisi tabelinimesid. Kui füüsilised tabelinimed on keskkondades erinevad, jäädvustage tabelid enne plaani koostamist sama kanoonilise nime all.

```php
use Merql\Snapshot\SnapshotStore;
use Merql\Snapshot\Snapshotter;

$snapshotter = new Snapshotter($connection);

SnapshotStore::save($snapshotter->captureAliased('live', [
    'wp_posts' => 'wp_posts',
]));

SnapshotStore::save($snapshotter->captureAliased('sandbox', [
    'wp_onumia_abc_posts' => 'wp_posts',
]));
```

Hetktõmmises salvestatud skeem kasutab kanoonilist tabelinime, säilitades samal ajal füüsilise tabeli veerud, võtmed, identiteediveerud, reavõtmed ja filtrid.
