خطط الدمج
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);
تتضمن الخطة:
- أسماء اللقطات المستخدمة لإنشائها،
- ملخصًا للعمليات والتعارضات واختلافات المخطط والجداول والمجموعات،
- معرّفات عمليات ثابتة،
- معرّفات مجموعات تغييرات ثابتة،
- أعمدة هوية الصف وقيم الهوية،
- قيم صف الأساس وours وtheirs والنتيجة لكل عملية،
- بيانات وصفية اختيارية للتطبيق المضيف،
- تجزئة الخطة،
- إصدار المخطط.
المعرّفات الثابتة
تكون معرّفات العمليات حتمية عند تطابق محتوى العملية. وهي مشتقة من اسم الجدول ومفتاح الصف ونوع العملية ومصدرها والأعمدة المتغيرة وقيم النتيجة.
تكون معرّفات مجموعات التغييرات حتمية عند تطابق محتوى المجموعة. ينشئ المُنشئ الافتراضي مجموعة عمليات لكل عملية. ويمكن للتطبيقات المضيفة إنشاء مجموعات أكبر خاصة بالمجال من قائمة العمليات، مع تسلسلها بالشكل نفسه لـ 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().
تحديد ما ينقص الهدف
تحتفظ الخطة بعمليات كلا الطرفين، لأن نتيجة الدمج تحوّل الأساس إلى الحالة المدمجة. عندما يحتوي الهدف على أحد الطرفين بالفعل، حدّد العمليات التي لم تصله بعد فقط. إذا كان الهدف يحتوي على theirs، مثل قاعدة بيانات حية تستقبل تعديلات نسخة معزولة، فهذه كل عملية مصدرها 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 (الصفوف التي حذفها الطرفان)، موجودة بالفعل في ذلك الهدف.
تظل الخطة المبنية من دمج يتضمن تعارضات تعرض عملية لكل صف متعارض حدّثه الطرفان. تحتوي قيم النتائج على قيمة ours في العمود المتعارض، وتعيد 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',
]));
يستخدم المخطط المخزّن في اللقطة اسم الجدول الأساسي، مع الحفاظ على أعمدة الجدول الفعلي ومفاتيحه وأعمدة هويته ومفاتيح صفوفه ومرشحاته.