انتقل إلى المحتوى

الدمج على مستوى الخلية

يكشف الدمج على مستوى الأعمدة ما إذا كان الجانبان قد غيّرا العمود نفسه. أما الدمج على مستوى الخلية فيتعمق أكثر، إذ يدمج المحتوى داخل ذلك العمود. ويمكن لشخصين يعدّلان سطرين مختلفين في حقل TEXT، أو مفتاحين مختلفين في كائن JSON، الدمج بسلاسة.

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;

$config = CellMergeConfig::auto();
$merge = new ThreeWayMerge($config);
$result = $merge->merge($base, $ours, $theirs);

تفعيل الدمج على مستوى الخلية

الدمج على مستوى الخلية اختياري. ولا يعمل إلا ضمن ThreeWayMerge مُنشأ باستخدام CellMergeConfig. ومن دونه، يُعدّ كل عمود غيّره الجانبان إلى قيمتين مختلفتين تعارضًا update_update.

لا تقبل واجهة Merql إعدادات: تنشئ Merql::merge() وMerql::patch() وMerql::plan() الكائن new ThreeWayMerge() من دونها، ويمر أمر merge في CLI عبر Merql::merge(). وهي تدمج دائمًا على مستوى الأعمدة (#7).

لدمج لقطات الحالة التي حفظتها الواجهة أو CLI باستخدام الدمج على مستوى الخلية، حمّلها واستدعِ ThreeWayMerge بنفسك. تكون النتيجة MergeResult عاديًا، لذا تظل الواجهة قادرة على تطبيقها، ويحوّلها MergePlanBuilder إلى خطة دمج:

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Merql;
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-1', $base, $ours, $theirs, $result);

لا تعمل أدوات دمج الخلايا إلا مع الأعمدة التي غيّرها الجانبان. وعندما يغيّر أحد الجانبين عمودًا فقط، تُعتمد قيمته كما هي.

CellMergeConfig

يربط CellMergeConfig الأعمدة أو أنواعها بتطبيقات أدوات دمج الخلايا. ويوفر المُنشئ auto() إعدادات افتراضية مناسبة:

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;
use Merql\CellMerge\TextCellMerger;

// Auto: column types containing "text" (TEXT, MEDIUMTEXT, LONGTEXT, ...) use
// TextCellMerger, column types containing "json" use JsonCellMerger.
$config = CellMergeConfig::auto();

// Custom: assign mergers to specific columns.
$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('settings', new JsonCellMerger())
    ->forType('json', new JsonCellMerger());

أولوية تحديد أداة الدمج:

  1. اسم العمود المؤهل (table.column).
  2. اسم العمود غير المؤهل (column).
  3. مطابقة نمط نوع العمود (str_contains دون تمييز حالة الأحرف).
  4. الافتراضي: OpaqueCellMerger (يُبلغ دائمًا عن تعارض عند اختلاف القيم).

TextCellMerger

تدمج النصوص متعددة الأسطر باستخدام خوارزمية Myers للمقارنة، وهي الخوارزمية نفسها التي يستخدمها git لمحتوى الملفات. يُعامل كل سطر كوحدة، وتُدمج التغييرات في أسطر مختلفة بسلاسة.

PHP
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();

مثال: دمج نصي سليم

الكود
Base:    "line one\nline two\nline three"
Ours:    "LINE ONE\nline two\nline three"    // changed line 1
Theirs:  "line one\nline two\nLINE THREE"    // changed line 3

عُدّلت أسطر مختلفة. ينتج الدمج "LINE ONE\nline two\nLINE THREE" من دون تعارض.

مثال: تعارض نصي

الكود
Base:    "line one\nline two"
Ours:    "changed by us\nline two"     // changed line 1
Theirs:  "changed by them\nline two"   // also changed line 1

غيّر الجانبان السطر نفسه إلى قيمتين مختلفتين. تُرجع أداة الدمج نتيجة تعارض. ويتضمن المحتوى المدمج علامات تعارض مشابهة لتلك في git.

آلية العمل

يفوّض TextCellMerger العمل إلى ThreeWayMerge::merge() في pitmaster، الذي يحسب مقارنة Myers بين الأساس وكل جانب، ثم يجمع نصّي التعديلات. وعندما تتداخل التعديلات في منطقة السطر نفسها، يُبلّغ عن تعارض.

JsonCellMerger

تدمج كائنات JSON بمقارنة المفاتيح العليا كلٌّ على حدة. ويُدمج كل مفتاح وفق قواعد الدمج الثلاثي المعتادة.

PHP
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();

مثال: دمج JSON سليم

JSON
// Base
{"name": "Alice", "role": "editor", "active": true}

// Ours: changed "role"
{"name": "Alice", "role": "admin", "active": true}

// Theirs: changed "name"
{"name": "Bob", "role": "editor", "active": true}

تغيّرت مفاتيح مختلفة. والنتيجة المدمجة هي:

JSON
{"name": "Bob", "role": "admin", "active": true}

مثال: تعارض في JSON

JSON
// Base
{"color": "red"}

// Ours
{"color": "blue"}

// Theirs
{"color": "green"}

غيّر الجانبان مفتاح color نفسه إلى قيمتين مختلفتين. وهذا تعارض. وتختار أداة الدمج نسختنا افتراضيًا، وتُبلغ عن تعارض واحد.

قواعد مستوى المفتاح

الأساس نسختنا نسختهم النتيجة
المفتاح لم يتغير المفتاح لم يتغير اعتماد الأساس
المفتاح لم يتغير تغيّر المفتاح اعتماد نسختهم
تغيّر المفتاح المفتاح لم يتغير اعتماد نسختنا
تغيّر المفتاح (بالقيمة نفسها) تغيّر المفتاح (بالقيمة نفسها) اعتماد التغيير (متفق عليه)
تغيّر المفتاح (بقيمة مختلفة) تغيّر المفتاح (بقيمة مختلفة) تعارض (الافتراضي: نسختنا)
المفتاح موجود المفتاح موجود إزالة المفتاح اعتماد الإزالة
المفتاح موجود إزالة المفتاح المفتاح موجود اعتماد الإزالة
المفتاح غير موجود إضافة المفتاح المفتاح غير موجود اعتماد الإضافة
المفتاح غير موجود المفتاح غير موجود إضافة المفتاح اعتماد الإضافة

تُقارن الكائنات المتداخلة كسلاسل JSON معتمة، لا بشكل تكراري. إذا اختلف كائن متداخل، تُقارن قيمة المفتاح بأكملها، لا مفاتيحه الفرعية.

المتطلبات

يجب أن تكون القيم الثلاث كلها كائنات أو مصفوفات JSON صالحة. إذا لم تكن أي قيمة JSON صالحة، أو كانت قيمة قياسية من JSON (سلسلة أو رقم أو قيمة منطقية)، تعود أداة الدمج إلى تسجيل تعارض.

OpaqueCellMerger

أداة الدمج الافتراضية عند عدم إعداد استراتيجية على مستوى الخلية. تتعامل مع قيمة العمود ككتلة بيانات معتمة، وتُبلغ دائمًا عن تعارض عندما يختلف الجانبان.

PHP
use Merql\CellMerge\OpaqueCellMerger;

$merger = new OpaqueCellMerger();
$result = $merger->merge($base, $ours, $theirs);
// $result->clean is always false when $ours !== $theirs

CellMergeResult

تُرجع جميع أدوات دمج الخلايا قيمة CellMergeResult:

PHP
$result = $merger->merge($base, $ours, $theirs);

$result->clean;     // bool: true if merge resolved cleanly
$result->value;     // mixed: the merged value (or ours on conflict)
$result->conflicts; // int: number of conflicts within the cell

دوال الإنشاء:

PHP
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // n conflicts, defaults to ours

أدوات دمج خلايا مخصصة

نفّذ واجهة CellMerger لإضافة استراتيجية الدمج الخاصة بك:

PHP
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

class YamlCellMerger implements CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult
    {
        // Parse YAML, merge keys, return result.
        $merged = $this->mergeYaml($base, $ours, $theirs);

        if ($merged['clean']) {
            return CellMergeResult::resolved($merged['value']);
        }

        return CellMergeResult::conflict($ours);
    }
}

$config = (new CellMergeConfig())
    ->forColumn('config_data', new YamlCellMerger());