Плани злиття
MergeResult — це виконуваний результат злиття. MergePlan — форма цього результату, призначена для перегляду. Використовуйте її, коли застосунку потрібно показати, підготувати, серіалізувати або вибірково застосувати зміни бази даних до запису в цільову базу даних.
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:
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 груп змін,
- стовпці ідентифікації рядків і значення ідентифікаторів,
- значення рядків для бази, нашої сторони, їхньої сторони та результату для кожної операції,
- необов’язкові метадані застосунку-хоста,
- хеш плану,
- версію схеми.
Стабільні ID
ID операцій є детермінованими для однакового вмісту операцій. Вони походять від назви таблиці, ключа рядка, типу операції, джерела операції, змінених стовпців і значень результату.
ID груп змін є детермінованими для однакового вмісту груп. Стандартний засіб створення формує одну групу операцій для кожної операції. Застосунки-хости можуть створювати більші групи для певної предметної області на основі списку операцій і все одно серіалізувати їх у тому самому форматі DTO.
foreach ($plan->changeGroups as $group) {
echo $group->id . ': ' . implode(', ', $group->operationIds);
}
Структура операції
Кожен MergePlanOperation містить достатньо контексту для перевірки та відкату.
$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 під час застосування або створення артефактів для відкату.
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().
Вибір відсутнього в цільовій базі
План містить операції обох сторін, оскільки результат злиття перетворює базу на об’єднаний стан. Якщо в цільовій базі вже є стан однієї сторони, виберіть лише ті операції, яких там ще немає. Для цільової бази зі станом їхньої сторони, наприклад робочої бази, до якої переносять зміни тестової копії, це всі операції з джерелом ours, а також оновлення merged, які поєднують зміни стовпців обох сторін:
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 (рядки, видалені обома сторонами), уже є в цільовій базі.
План, створений на основі злиття з конфліктами, усе одно містить операцію для кожного конфліктного рядка, який оновили обидві сторони. У значеннях результату для конфліктного стовпця зберігається значення нашої сторони, а SelectedMergeResultFactory повертає чистий результат для будь-якої вибірки. Навіть у такому разі conflictState операції та групи змін має значення clean (#9), тож перевірте $plan->conflicts самостійно, як описано вище, або розв’яжіть конфлікти за допомогою ConflictResolver та створіть план на основі розв’язаного результату за допомогою MergePlanBuilder. Див. Захищене застосування, щоб записати вибірку з попередніми умовами для поточних рядків.
Серіалізація
Плани злиття та вибірки мають стабільну серіалізацію JSON. Збережіть серіалізований план, якщо на наступному кроці застосування потрібно використати саме той набір операцій, який було перевірено.
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),
);
Знімки з псевдонімами
У планах злиття використовуються канонічні назви таблиць. Якщо фізичні назви таблиць відрізняються в різних середовищах, перед плануванням зафіксуйте їх під однаковою канонічною назвою.
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',
]));
Схема, збережена у знімку, використовує канонічну назву таблиці та зберігає стовпці, ключі, стовпці ідентифікації, ключі рядків і фільтри фізичної таблиці.