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

هوية الصف

تحدد هوية الصف طريقة مطابقة merql للصفوف بين اللقطات. عند مقارنة الأساس ببياناتنا، يحتاج النظام إلى معرفة أن الصف X في الأساس هو الصف المنطقي نفسه Y في بياناتنا، حتى إن تغيرت بعض قيم الأعمدة. وتحدد قاعدة الهوية الأعمدة التي تكوّن مفتاح الصف.

PHP
use Merql\Identity\PrimaryKeyIdentity;

$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"

واجهة RowIdentity

تطبّق جميع استراتيجيات الهوية طريقتين:

PHP
interface RowIdentity
{
    public function key(array $row): string;
    public function columns(): array;
}

تنشئ key() معرّفًا نصيًا فريدًا من بيانات الصف. وتُرجع columns() أسماء الأعمدة المستخدمة لتحديد الهوية.

PrimaryKeyIdentity

الاستراتيجية الافتراضية. تطابق الصفوف باستخدام أعمدة مفاتيحها الأساسية.

PHP
use Merql\Identity\PrimaryKeyIdentity;

// Single-column PK.
$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"

// Composite PK.
$identity = new PrimaryKeyIdentity(['tenant_id', 'user_id']);
$key = $identity->key(['tenant_id' => 1, 'user_id' => 99, 'name' => 'Alice']);
// "1\x1F99" (unit separator between parts)

يستخدم merql هذه الاستراتيجية افتراضيًا عندما يكون للجدول مفتاح أساسي محدد في المخطط.

NaturalKeyIdentity

تطابق الصفوف في الجداول التي لا تملك مفتاحًا أساسيًا يزداد تلقائيًا، باستخدام أعمدة القيود الفريدة. يفيد ذلك عندما يكون المفتاح الأساسي UUID أو عندما يستخدم الجدول قيدًا فريدًا مركبًا بوصفه هويته المنطقية.

PHP
use Merql\Identity\NaturalKeyIdentity;

$identity = new NaturalKeyIdentity(['email']);
$key = $identity->key(['id' => 1, 'email' => 'alice@example.com', 'name' => 'Alice']);
// "alice@example.com"

$identity = new NaturalKeyIdentity(['country', 'postal_code']);
$key = $identity->key(['country' => 'US', 'postal_code' => '90210', 'city' => 'Beverly Hills']);
// "US\x1F90210"

ContentHashIdentity

خيار احتياطي للجداول التي لا تملك مفتاحًا أساسيًا ولا قيودًا فريدة. يحسب تجزئة SHA-256 لجميع قيم الأعمدة. ويُعد الصفان متطابقين إذا تطابقت جميع الأعمدة.

PHP
use Merql\Identity\ContentHashIdentity;

$identity = new ContentHashIdentity(['name', 'value']);
$key = $identity->key(['name' => 'color', 'value' => 'blue']);
// "a3f2..." (SHA-256 hash)

لهوية تجزئة المحتوى قيد: إذا تغير محتوى صف، تتغير تجزئته، فيظهر الصف كأنه حُذف بالتجزئة القديمة وأُدرج بالتجزئة الجديدة بدلًا من تحديثه. وهذه مفاضلة حتمية عند عدم وجود أعمدة هوية مستقرة.

IdentityRule

IdentityRule هو إعداد الهوية القابل للتسلسل، والمستخدم عند التقاط اللقطة وإعداد خطط الدمج.

PHP
use Merql\Identity\IdentityRule;

$rule = IdentityRule::natural(['option_name']);
$key = $rule->key(['option_name' => 'siteurl']);
// "siteurl"

أنواع القواعد:

النوع المعنى
primary أعمدة المفتاح الأساسي لقاعدة البيانات
natural أعمدة أعمال فريدة ومستقرة
composite أعمدة الهوية المنطقية المركبة
content خيار احتياطي لتجزئة المحتوى

تحل IdentityRule::forSchema($schema) القاعدة الافتراضية:

  1. إذا كان للجدول مفتاح أساسي، تُستخدم أعمدته.
  2. إذا كان للجدول مفاتيح فريدة، تُستخدم أعمدة أول مفتاح فريد.
  3. إذا لم يتوفر أي منهما، تُستخدم جميع الأعمدة (خيار تجزئة المحتوى الاحتياطي).

IdentityRuleSet

استخدم IdentityRuleSet عندما يعرف التطبيق المضيف قاعدة هوية أفضل من القاعدة الافتراضية للمخطط.

PHP
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
use Merql\Snapshot\Snapshotter;

$rules = new IdentityRuleSet([
    'wp_options' => IdentityRule::natural(['option_name']),
    'wp_term_relationships' => IdentityRule::composite(['object_id', 'term_taxonomy_id']),
]);

$snapshotter = new Snapshotter($connection, identityRules: $rules);
$snapshot = $snapshotter->capture('live', ['wp_options', 'wp_term_relationships']);

يحلّ Snapshotter قيمة IdentityRule لكل جدول ملتقط. تُخزَّن الأعمدة المختارة في TableSnapshot::identityColumns وتُستخدم في جميع عمليات المقارنة والدمج والتخطيط وSQL والتراجع اللاحقة.

يمكن أن يتلقى Merql::init() مجموعة القواعد نفسها عند استخدام الواجهة الثابتة:

PHP
use Merql\Merql;

Merql::init($connection, identityRules: $rules);
Merql::snapshot('live', ['wp_options']);

تُسلسَل مجموعات القواعد في المصفوفات بطريقة ثابتة:

PHP
$payload = $rules->toArray();
$rules = IdentityRuleSet::fromArray($payload);

هوية ملتبسة

إذا أنتج صفّان ملتقطان مفتاح الهوية نفسه، تفشل عملية الالتقاط بدلًا من استبدال أحد الصفين بالآخر.

PHP
use Merql\Exceptions\SnapshotException;

try {
    $snapshotter->capture('live', ['wp_postmeta']);
} catch (SnapshotException $e) {
    // The configured identity rule matched multiple rows.
}

يمكن للتطبيقات المضيفة التحقق مسبقًا من الصفوف الخام باستخدام IdentityRuleSet::conflictsFor() عندما تريد الإبلاغ عن الالتباس قبل الالتقاط:

PHP
$conflicts = $rules->conflictsFor(
    'wp_postmeta',
    IdentityRule::natural(['post_id', 'meta_key']),
    $rows,
);

بالنسبة إلى جداول البيانات الوصفية WordPress، يكون post_id + meta_key مفيدًا عادةً للتجميع والعرض، لكنه غير آمن بوصفه هوية للصف لأن تكرار مفاتيح البيانات الوصفية أمر مسموح. استخدم المفتاح الأساسي للجدول لهوية الصف، ما لم يتمكن التطبيق المضيف من إثبات تفرد المفتاح الطبيعي.

ترميز مفتاح الصف

مفاتيح الصفوف سلاسل مُرمّزة تجمع قيم عدة أعمدة باستخدام فاصل الوحدات (\x1F). تُهرَّب القيم التي تحتوي على الفاصل أو علامة النسبة المئوية لمنع التصادمات:

PHP
use Merql\Snapshot\Snapshotter;

// Encode.
$key = Snapshotter::buildRowKey(
    ['tenant_id' => '1', 'user_id' => '99'],
    ['tenant_id', 'user_id'],
);
// "1\x1F99"

// Decode.
$parts = Snapshotter::decodeRowKey($key);
// ['1', '99']

قواعد التهريب:

الأصل بعد التهريب
% %25
\x1F (فاصل الوحدات) %1F

يضمن ذلك ألا تنقسم القيمة التي تحتوي على \x1F إلى أجزاء متعددة من المفتاح. الترميز حتمي وقابل للعكس.

اختلاف الترقيم التلقائي

عندما يُدرج كلا الجانبين صفوفًا جديدة، تختلف معرّفات الترقيم التلقائي بين الفروع. قد يكون الصف #100 لدينا سجلًا مختلفًا تمامًا عن الصف #100 لديهم.

يعالج merql ذلك بمطابقة الصفوف حسب المفتاح الأساسي. إذا أدرج كلا الجانبين صفًا بمعرّف الترقيم التلقائي نفسه، يُصنَّف الأمر على أنه تعارض insert_insert. لتجنب ذلك، استخدم مفاتيح طبيعية (أعمدة فريدة ومستقرة عبر الفروع) للجداول التي يُتوقع فيها إدراج متزامن.

PHP
// A natural key on (slug) avoids auto-increment collisions.
$identity = new NaturalKeyIdentity(['slug']);

الهوية في اللقطات

تُخزَّن أعمدة الهوية المختارة أثناء الالتقاط مع كل TableSnapshot:

PHP
$tableSnapshot = $snapshot->getTable('posts');
$tableSnapshot->identityColumns;
// ['id']

يستخدم SqlGenerator هذه الأعمدة لإنشاء عبارات WHERE الخاصة بعبارات UPDATE وDELETE، كما تستخدمها خطط الدمج لإظهار قيم الهوية المفكوكة للمراجعة والاختيار والتطبيق المحمي والتراجع.