
# Sujungimo planai

`MergeResult` yra vykdomas sujungimo rezultatas. `MergePlan` yra šio rezultato
patikrinama forma. Naudokite jį, kai programai reikia parodyti, paruošti,
serializuoti arba pasirinktinai pritaikyti duomenų bazės pakeitimus prieš
įrašant juos į tikslinę duomenų bazę.

```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()` įkelia tris nurodytus momentinius vaizdus ir sujungia juos stulpelių
lygmeniu, kaip `Merql::merge()`. Norėdami suplanuoti sujungimą, kuriame TEXT ir
JSON stulpeliams naudojamas sujungimas langelio lygmeniu, patys sukurkite
rezultatą ir perduokite jį `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);
```

Plane pateikiama:

- jam sukurti naudotų momentinių vaizdų pavadinimai,
- operacijų, konfliktų, schemos neatitikimų, lentelių ir grupių suvestinė,
- stabilūs operacijų ID,
- stabilūs pakeitimų grupių ID,
- eilučių tapatybės stulpeliai ir tapatybės reikšmės,
- kiekvienos operacijos bazinės versijos, mūsų pusės, jų pusės ir rezultato eilučių reikšmės,
- pasirenkami pagrindinės programos metaduomenys,
- plano maiša,
- schemos versija.

## Stabilūs ID

Tų pačių operacijos duomenų operacijų ID visada yra vienodi. Jie
apskaičiuojami pagal lentelės pavadinimą, eilutės raktą, operacijos tipą, operacijos šaltinį, pakeistus
stulpelius ir rezultato reikšmes.

Tų pačių grupės duomenų pakeitimų grupės ID visada yra vienodi. Numatytoji
plano kūrimo priemonė kiekvienai operacijai sukuria atskirą operacijų grupę. Pagrindinės programos gali iš operacijų sąrašo kurti
platesnes konkrečios srities grupes ir serializuoti jas tokiu pačiu DTO formatu.

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

## Operacijos struktūra

Kiekvienoje `MergePlanOperation` pateikiama pakankamai konteksto peržiūrai ir atšaukimui.

```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
```

## Pasirinkimai

Naudokite `ChangeGroupSelection`, kai UI paruošia grupes. Taikydami pakeitimus arba generuodami atšaukimo artefaktus, konvertuokite jį į `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),
);
```

Pasirinktas rezultatas yra švarus `MergeResult`, kuriame yra tik pasirinktos
operacijos. Jį galima perduoti `Merql::apply()` arba `Merql::applyGuarded()`.

## Trūkstamų pakeitimų pasirinkimas tikslinėje bazėje

Plane pateikiamos abiejų pusių operacijos, nes sujungimo rezultatas paverčia bazinę
versiją sujungta būsena. Jei tikslinėje bazėje jau yra viena pusė, pasirinkite tik tas
operacijas, kurių joje dar nėra. Jei tikslinėje bazėje yra jų pusės pakeitimai, pavyzdžiui, į tiesioginę
duomenų bazę perkeliami smėliadėžės kopijoje atlikti pakeitimai, pasirinkite visas operacijas,
kurių šaltinis yra `ours`, taip pat `merged` atnaujinimus, kurie apima abiejų pusių stulpelių pakeitimus:

```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),
);
```

Operacijos, kurių šaltinis yra `theirs`, ir `merged` ištrynimai (eilutės, kurias ištrynė abi pusės) tikslinėje bazėje jau yra.

Iš sujungimo su konfliktais sukurtame plane vis tiek pateikiama po operaciją kiekvienai
konfliktuojančiai eilutei, kurią atnaujino abi pusės. Rezultato reikšmėse konfliktuojančiam
stulpeliui saugoma mūsų reikšmė, o `SelectedMergeResultFactory` pateikia švarų rezultatą
bet kuriam pasirinkimui. Operacijos ir pakeitimų grupės `conflictState` net ir tokiu atveju šiuo metu
nurodo `clean` ([#9](https://github.com/inline0/merql/issues/9)), todėl, kaip nurodyta pirmiau, patys patikrinkite `$plan->conflicts` arba išspręskite konfliktus naudodami
`ConflictResolver` ir sukurkite planą iš išspręsto rezultato, naudodami
`MergePlanBuilder`. Kaip pritaikyti pasirinkimą su tiesioginės eilutės išankstinėmis sąlygomis, žr.
[Apsaugotas taikymas](https://merql.inline0.dev/lt/docs/apply/guarded).

## Serializavimas

Sujungimo planai ir pasirinkimai turi stabilų JSON serializavimą. Išsaugokite serializuotą
planą, jei vėlesniame taikymo etape reikia naudoti tiksliai peržiūrėtą operacijų rinkinį.

```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),
);
```

## Pseudonimais pavadinti momentiniai vaizdai

Sujungimo planuose naudojami kanoniniai lentelių pavadinimai. Jei skirtingose
aplinkose fiziniai lentelių pavadinimai skiriasi, prieš planuodami išsaugokite jas tuo pačiu kanoniniu pavadinimu.

```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',
]));
```

Momentinio vaizdo schemoje išsaugomas kanoninis lentelės pavadinimas, kartu išlaikant
fizinės lentelės stulpelius, raktus, tapatybės stulpelius, eilučių raktus ir filtrus.
