
# Sapludināšanas plāni

`MergeResult` ir izpildāmais sapludināšanas rezultāts. `MergePlan` ir šī rezultāta pārskatāmā forma. Izmantojiet to, ja lietotnei pirms izmaiņu ierakstīšanas mērķa datubāzē ir jāparāda, jāiestudē, jāserializē vai selektīvi jāpiemēro datubāzes izmaiņas.

```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()` ielādē trīs nosauktos momentuzņēmumus un sapludina tos kolonnu līmenī, tāpat kā `Merql::merge()`. Lai plānotu sapludināšanu, kurā TEXT un JSON kolonnām izmantota šūnu līmeņa sapludināšana, izveidojiet rezultātu paši un nododiet to `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);
```

Plānā ir:

- tā izveidei izmantoto momentuzņēmumu nosaukumi,
- darbību, konfliktu, shēmas neatbilstību, tabulu un grupu kopsavilkums,
- stabili darbību ID,
- stabili izmaiņu grupu ID,
- rindu identitātes kolonnas un identitātes vērtības,
- katras darbības bāzes, mūsu, viņu un rezultāta rindu vērtības,
- neobligāti resurslietotnes metadati,
- plāna jaucējvērtība,
- shēmas versija.

## Stabili ID

Darbību ID vienam un tam pašam darbības saturam ir deterministiski. Tos atvasina no tabulas nosaukuma, rindas atslēgas, darbības veida, darbības avota, mainītajām kolonnām un rezultāta vērtībām.

Izmaiņu grupu ID vienam un tam pašam grupas saturam ir deterministiski. Pēc noklusējuma plāna veidotājs katrai darbībai izveido atsevišķu darbību grupu. Resurslietotnes var izveidot lielākas, konkrētajai jomai pielāgotas grupas no darbību saraksta un serializēt tās tādā pašā DTO formā.

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

## Darbības struktūra

Katrs `MergePlanOperation` ietver pietiekami daudz konteksta pārskatīšanai un atcelšanai.

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

## Atlases

Izmantojiet `ChangeGroupSelection`, ja UI iestudē grupas. Pārveidojiet to par `OperationSelection`, kad lietojat izmaiņas vai ģenerējat atcelšanas artefaktus.

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

Atlasītais rezultāts ir tīrs `MergeResult`, kurā ir tikai atlasītās darbības. To var nodot `Merql::apply()` vai `Merql::applyGuarded()`.

## Atlase tam, kā trūkst mērķa datubāzē

Plānā ir abu pušu darbības, jo sapludināšanas rezultāts pārveido bāzi sapludinātajā stāvoklī. Ja mērķa datubāzē jau ir viena puse, atlasiet tikai tās darbības, kuru tur vēl nav. Mērķim, kurā ir viņu versija, piemēram, reāllaika datubāzei, kurā jāsaņem smilškastes kopijas labojumi, tās ir visas darbības ar avotu `ours`, kā arī `merged` atjauninājumi, kuros apvienotas abu pušu kolonnu izmaiņas:

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

Darbības ar avotu `theirs` un `merged` dzēšanas (rindas, ko izdzēsušas abas puses) jau ir iekļautas šajā mērķa datubāzē.

Plānā, kas izveidots no sapludināšanas ar konfliktiem, joprojām ir norādīta darbība katrai konfliktējošajai rindai, ko atjauninājušas abas puses. Tās rezultāta vērtībās konfliktējošajā kolonnā ir mūsu vērtība, un `SelectedMergeResultFactory` atgriež tīru rezultātu jebkurai atlasei. Darbības un izmaiņu grupas `conflictState` arī šādā gadījumā pašlaik ir `clean` ([#9](https://github.com/inline0/merql/issues/9)), tāpēc pārbaudiet `$plan->conflicts` paši, kā aprakstīts iepriekš, vai atrisiniet konfliktus ar `ConflictResolver` un izveidojiet plānu no atrisinātā rezultāta, izmantojot `MergePlanBuilder`. Skatiet [Droša lietošana](https://merql.inline0.dev/lv/docs/apply/guarded), lai lietotu atlasi ar reāllaika rindu priekšnosacījumiem.

## Serializēšana

Sapludināšanas plāniem un atlasēm ir stabila JSON serializācija. Saglabājiet serializēto plānu, ja vēlākā izmaiņu lietošanas solī jāizmanto tieši tas pats pārskatītais darbību kopums.

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

## Nosaukumu aizstājvārdi momentuzņēmumiem

Sapludināšanas plānos tiek izmantoti kanoniskie tabulu nosaukumi. Ja fizisko tabulu nosaukumi dažādās vidēs atšķiras, pirms plānošanas saglabājiet tās ar vienādu kanonisko nosaukumu.

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

Momentuzņēmumā saglabātajā shēmā tiek izmantots kanoniskais tabulas nosaukums, vienlaikus saglabājot fiziskās tabulas kolonnas, atslēgas, identitātes kolonnas, rindu atslēgas un filtrus.
