Fletteplaner
En MergeResult er det eksekverbare resultat af en fletning. En MergePlan er en
form af resultatet, der kan inspiceres. Brug den, når et program skal vise,
forberede, serialisere eller anvende databaseændringer selektivt, før de skrives til
måldatabasen.
use Merql\Merql;
$plan = Merql::plan(
id: 'plan-2026-06-06',
base: 'base',
ours: 'ours',
theirs: 'theirs',
metadata: ['sandbox_id' => 'ow_123'],
);
Merql::plan() indlæser de tre navngivne øjebliksbilleder og fletter dem på kolonneniveau,
ligesom Merql::merge(). Hvis du vil planlægge en fletning, der bruger fletning på celleniveau for
TEXT- og JSON-kolonner, skal du selv oprette resultatet og sende det til
MergePlanBuilder:
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);
Planen indeholder:
- navnene på de øjebliksbilleder, der blev brugt til at oprette den,
- en oversigt over handlinger, konflikter, skemauoverensstemmelser, tabeller og grupper,
- stabile handlings-id'er,
- stabile id'er for ændringsgrupper,
- kolonner og værdier for rækkeidentitet,
- rækkeværdier for base, vores side, deres side og resultatet for hver handling,
- valgfrie metadata fra værtsprogrammet,
- en planhash,
- en skemaversion.
Stabile id'er
Handlings-id'er er deterministiske for det samme handlingsindhold. De udledes af tabelnavn, rækkenøgle, handlingstype, handlingskilde, ændrede kolonner og resultatværdier.
Id'er for ændringsgrupper er deterministiske for det samme gruppeindhold. Standardbyggeren opretter én handlingsgruppe pr. handling. Værtsprogrammer kan oprette større domænespecifikke grupper ud fra handlingslisten og stadig serialisere dem med den samme DTO-struktur.
foreach ($plan->changeGroups as $group) {
echo $group->id . ': ' . implode(', ', $group->operationIds);
}
Handlingsstruktur
Hver MergePlanOperation indeholder nok kontekst til gennemgang og tilbagerulning.
$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
Udvalg
Brug ChangeGroupSelection, når UI forbereder grupper. Konvertér den til en
OperationSelection, når du anvender ændringer eller genererer artefakter til tilbagerulning.
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),
);
Det valgte resultat er en ren MergeResult, der kun indeholder de valgte
handlinger. Det kan sendes til Merql::apply() eller Merql::applyGuarded().
Vælg det, et mål mangler
En plan indeholder handlingerne fra begge sider, fordi fletteresultatet ændrer base til den flettede tilstand. Hvis målet allerede indeholder den ene side, skal du kun vælge de handlinger, det endnu ikke har. For et mål, der indeholder deres side, såsom en live-database, der modtager ændringer fra en sandbox-kopi, omfatter det alle handlinger med kilden ours samt merged-opdateringer, der kombinerer begge siders kolonneændringer:
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),
);
Handlinger med kilden theirs samt merged-sletninger (rækker, som begge sider har slettet) findes allerede i målet.
En plan oprettet ud fra en sammenfletning med konflikter viser stadig en operation for hver modstridende række, som begge sider har opdateret. Resultatværdierne indeholder vores værdi i den modstridende kolonne, og SelectedMergeResultFactory giver et rent resultat for ethvert valg. Operationens og ændringsgruppens conflictState viser dog stadig clean (#9), så kontrollér selv $plan->conflicts som ovenfor, eller løs konflikterne med ConflictResolver, og opret planen ud fra det løste resultat med MergePlanBuilder. Se Guarded Apply for at skrive valget med forudsætninger om de aktuelle rækker.
Serialisering
Sammenfletningsplaner og valg har en stabil JSON-serialisering. Gem den serialiserede plan, når et senere anvendelsestrin skal bruge præcis det gennemgåede operationssæt.
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),
);
Aliasserede snapshots
Sammenfletningsplaner bruger kanoniske tabelnavne. Hvis de fysiske tabelnavne er forskellige mellem miljøer, skal du registrere dem under det samme kanoniske navn før planlægningen.
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',
]));
Skemaet i snapshottet bruger det kanoniske tabelnavn, mens den fysiske tabels kolonner, nøgler, identitetskolonner, rækkenøgler og filtre bevares.