Към съдържанието

Планове за сливане

MergeResult е изпълнимият резултат от сливане. MergePlan е форма на този резултат, предназначена за проверка. Използвайте я, когато дадено приложение трябва да показва, подготвя, сериализира или селективно прилага промени в базата данни, преди да ги запише в целевата база данни.

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() зарежда трите посочени моментни снимки и ги слива на ниво колона, както прави Merql::merge(). За да планирате сливане, което използва сливане на ниво клетка за колони TEXT и JSON, създайте резултата сами и го подайте на 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);

Планът съдържа:

  • имената на моментните снимки, използвани за създаването му,
  • обобщение на операциите, конфликтите, несъответствията в схемата, таблиците и групите,
  • стабилни ID на операциите,
  • стабилни ID на групите промени,
  • колони за идентичност на редовете и стойности за идентичност,
  • стойностите на реда в base, ours, theirs и result за всяка операция,
  • незадължителни метаданни на хост приложението,
  • хеш на плана,
  • версия на схемата.

Стабилни ID

ID на операциите са детерминистични при еднакво съдържание на операцията. Те се извеждат от името на таблицата, ключа на реда, типа операция, източника на операцията, променените колони и стойностите в резултата.

ID на групите промени са детерминистични при еднакво съдържание на групата. Стандартният създател формира по една група операции за всяка операция. Хост приложенията могат да създават по-големи специфични за домейна групи от списъка с операции и пак да ги сериализират със същата форма на DTO.

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

Структура на операцията

Всяка MergePlanOperation съхранява достатъчно контекст за преглед и връщане назад.

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

Избори

Използвайте ChangeGroupSelection, когато UI подготвя групи. Преобразувайте го в 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),
);

Избраният резултат е изчистен MergeResult, който съдържа само избраните операции. Може да бъде подаден на Merql::apply() или Merql::applyGuarded().

Избор на липсващото в целта

Планът съдържа операциите и от двете страни, защото резултатът от сливането превръща base в обединеното състояние. Когато целта вече съдържа едната страна, изберете само операциите, които още липсват в нея. За цел, съдържаща theirs, например активна база данни, която получава редакциите от копие в изолирана среда, това са всички операции с източник ours, както и актуализациите merged, които обединяват промените в колоните от двете страни:

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

Операциите с източник theirs и изтриванията merged (редове, изтрити и от двете страни) вече са налице в тази цел.

План, създаден от сливане с конфликти, пак съдържа операция за всеки конфликтен ред, актуализиран и от двете страни. Стойностите в резултата му съдържат стойността на ours в конфликтната колона, а SelectedMergeResultFactory връща изчистен резултат за всеки избор. И conflictState на операцията, и на групата промени засега е clean дори в този случай (#9), затова проверете сами $plan->conflicts, както е описано по-горе, или разрешете конфликтите с ConflictResolver и създайте плана от разрешения резултат чрез MergePlanBuilder. Вижте Защитено прилагане за записване на избора с предварителни условия за активните редове.

Сериализация

Плановете за сливане и изборите имат стабилна сериализация чрез JSON. Съхранете сериализирания план, когато последващото прилагане трябва да използва точно прегледания набор от операции.

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

Псевдоними на моментни снимки

Плановете за сливане използват канонични имена на таблици. Ако физическите имена на таблиците се различават между средите, заснемете ги под едно и също канонично име преди планирането.

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

Схемата, съхранена в моментната снимка, използва каноничното име на таблицата, като запазва колоните, ключовете, колоните за идентичност, ключовете на редовете и филтрите на физическата таблица.