Przejdź do treści

Plany scalania

MergeResult to wykonywalny wynik scalania. MergePlan to jego forma przeznaczona do inspekcji. Użyj jej, gdy aplikacja musi wyświetlić, przygotować, serializować lub wybiórczo zastosować zmiany w bazie danych przed zapisaniem ich w docelowej bazie.

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() wczytuje trzy nazwane zrzuty i scala je na poziomie kolumn, tak jak Merql::merge(). Aby zaplanować scalanie wykorzystujące scalanie na poziomie komórek dla kolumn TEXT i JSON, samodzielnie zbuduj wynik i przekaż go do 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);

Plan zawiera:

  • nazwy zrzutów użytych do jego utworzenia,
  • podsumowanie operacji, konfliktów, niezgodności schematu, tabel i grup,
  • stabilne identyfikatory operacji,
  • stabilne identyfikatory grup zmian,
  • kolumny identyfikujące wiersze i wartości tożsamości,
  • wartości wierszy dla bazy, naszej strony, drugiej strony i wyniku dla każdej operacji,
  • opcjonalne metadane aplikacji hosta,
  • skrót planu,
  • wersję schematu.

Stabilne identyfikatory

Identyfikatory operacji są deterministyczne dla tej samej zawartości operacji. Są wyprowadzane z nazwy tabeli, klucza wiersza, typu operacji, źródła operacji, zmienionych kolumn i wartości wynikowych.

Identyfikatory grup zmian są deterministyczne dla tej samej zawartości grupy. Domyślny konstruktor tworzy jedną grupę operacji na każdą operację. Aplikacje hosta mogą tworzyć większe, specyficzne dla domeny grupy z listy operacji i nadal serializować je w tym samym kształcie DTO.

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

Struktura operacji

Każdy element MergePlanOperation zachowuje wystarczający kontekst na potrzeby przeglądu i wycofania.

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

Wybory

Użyj ChangeGroupSelection, gdy UI przygotowuje grupy. Zamień go na OperationSelection podczas stosowania zmian lub generowania artefaktów wycofania.

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

Wybrany wynik to czysty obiekt MergeResult zawierający tylko wybrane operacje. Można go przekazać do Merql::apply() lub Merql::applyGuarded().

Wybieranie brakujących zmian w bazie docelowej

Plan zawiera operacje z obu stron, ponieważ wynik scalania przekształca bazę w stan scalony. Jeśli baza docelowa zawiera już jedną ze stron, wybierz tylko operacje, których jeszcze w niej nie ma. W przypadku bazy docelowej zawierającej zmiany drugiej strony, na przykład działającej bazy danych otrzymującej zmiany z kopii piaskownicy, są to wszystkie operacje ze źródłem ours oraz aktualizacje merged, które łączą zmiany kolumn po obu stronach:

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

Operacje ze źródłem theirs oraz usunięcia merged (wiersze usunięte przez obie strony) są już obecne w tej bazie docelowej.

Plan utworzony ze scalania zawierającego konflikty nadal wymienia operację dla każdego konfliktowego wiersza zaktualizowanego przez obie strony. Wartości wynikowe przechowują naszą wartość w konfliktowej kolumnie, a SelectedMergeResultFactory zwraca czysty wynik dla każdego wyboru. Mimo to pole conflictState operacji i grupy zmian ma obecnie wartość clean (#9), więc samodzielnie sprawdź $plan->conflicts, jak opisano wyżej, lub rozwiąż konflikty za pomocą ConflictResolver i utwórz plan z rozwiązanego wyniku przy użyciu MergePlanBuilder. Informacje o zapisywaniu wybranych zmian z warunkami wstępnymi dla bieżących wierszy znajdziesz w sekcji Zabezpieczone zastosowanie.

Serializacja

Plany scalania i wybory są serializowane w stabilnym formacie JSON. Zapisz serializowany plan, jeśli późniejszy krok zastosowania ma użyć dokładnie tego samego, sprawdzonego zestawu operacji.

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

Aliasowane zrzuty

Plany scalania używają kanonicznych nazw tabel. Jeśli fizyczne nazwy tabel różnią się między środowiskami, przed planowaniem zarejestruj je pod tą samą nazwą kanoniczną.

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

Schemat zapisany w zrzucie używa kanonicznej nazwy tabeli, zachowując kolumny, klucze, kolumny tożsamości, klucze wierszy i filtry tabeli fizycznej.