Ga naar de inhoud

Samenvoegplannen

Een MergeResult is de uitvoerbare uitvoer van een samenvoeging. Een MergePlan is de inspecteerbare vorm van die uitvoer. Gebruik deze wanneer een applicatie databasewijzigingen moet tonen, voorbereiden, serialiseren of selectief toepassen voordat ze naar de doeldatabase worden geschreven.

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() laadt de drie benoemde snapshots en voegt ze samen op kolomniveau, net als Merql::merge(). Bouw zelf het resultaat en geef het door aan MergePlanBuilder om samenvoegen op celniveau toe te passen op TEXT- en JSON-kolommen:

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

Het plan bevat:

  • de snapshotnamen die zijn gebruikt om het plan te bouwen,
  • een overzicht van bewerkingen, conflicten, schemamismatches, tabellen en groepen,
  • stabiele bewerkings-ID's,
  • stabiele ID's voor wijzigingsgroepen,
  • rij-identiteitskolommen en identiteitswaarden,
  • basis-, onze, hun en resultaatrijwaarden voor elke bewerking,
  • optionele metadata van de hostapplicatie,
  • een planhash,
  • een schemaversie.

Stabiele ID's

Bewerkings-ID's zijn deterministisch bij dezelfde bewerkingsinhoud. Ze worden afgeleid van de tabelnaam, rijsleutel, bewerkingstype, bewerkingsbron, gewijzigde kolommen en resultaatwaarden.

ID's van wijzigingsgroepen zijn deterministisch bij dezelfde groepsinhoud. De standaard builder maakt één bewerkingsgroep per bewerking. Hostapplicaties kunnen grotere domeinspecifieke groepen maken op basis van de bewerkingslijst en deze nog steeds serialiseren met dezelfde DTO-vorm.

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

Bewerkingsvorm

Elke MergePlanOperation bevat voldoende context voor beoordeling en terugdraaien.

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

Selecties

Gebruik ChangeGroupSelection wanneer de UI groepen klaarzet. Zet deze om naar een OperationSelection wanneer je wijzigingen toepast of artefacten voor terugdraaien genereert.

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

Het geselecteerde resultaat is een schoon MergeResult dat alleen geselecteerde bewerkingen bevat. Het kan worden doorgegeven aan Merql::apply() of Merql::applyGuarded().

Selecteren wat een doel mist

Een plan bevat de bewerkingen van beide kanten, omdat het samenvoegresultaat de basis omzet naar de samengevoegde staat. Wanneer het doel al één kant bevat, selecteer je alleen de bewerkingen die het nog niet heeft. Voor een doel dat hun kant bevat, zoals een live database die de wijzigingen van een sandboxkopie ontvangt, geldt dit voor elke bewerking met bron ours, plus merged-updates, die de kolomwijzigingen van beide kanten combineren:

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

Bewerkingen met bron theirs en merged-verwijderingen (rijen die beide kanten hebben verwijderd) zijn al aanwezig in dat doel.

Een plan dat is gebouwd op basis van een samenvoeging met conflicten, vermeldt nog steeds een bewerking voor elke conflicterende rij die beide kanten hebben bijgewerkt. De resultaatwaarden bevatten onze waarde in de conflicterende kolom, en SelectedMergeResultFactory retourneert een schoon resultaat voor elke selectie. De conflictState van de bewerking en wijzigingsgroep blijven ook dan clean lezen (#9), dus controleer zelf $plan->conflicts zoals hierboven, of los conflicten op met ConflictResolver en bouw het plan op basis van het opgeloste resultaat met MergePlanBuilder. Zie Beveiligd toepassen voor het schrijven van de selectie met voorwaarden voor live rijen.

Serialisatie

Samenvoegplannen en selecties worden stabiel geserialiseerd naar JSON. Sla het geserialiseerde plan op wanneer een latere toepassingsstap exact dezelfde beoordeelde set bewerkingen moet gebruiken.

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

Aliassnapshots

Samenvoegplannen gebruiken canonieke tabelnamen. Als fysieke tabelnamen tussen omgevingen verschillen, leg ze dan vóór het plannen vast onder dezelfde canonieke naam.

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

Het schema dat in de snapshot is opgeslagen, gebruikt de canonieke tabelnaam en behoudt tegelijkertijd de kolommen, sleutels, identiteitskolommen, rijsleutels en filters van de fysieke tabel.