هوية الصف
تحدد هوية الصف طريقة مطابقة merql للصفوف بين اللقطات. عند مقارنة الأساس ببياناتنا، يحتاج النظام إلى معرفة أن الصف X في الأساس هو الصف المنطقي نفسه Y في بياناتنا، حتى إن تغيرت بعض قيم الأعمدة. وتحدد قاعدة الهوية الأعمدة التي تكوّن مفتاح الصف.
use Merql\Identity\PrimaryKeyIdentity;
$identity = new PrimaryKeyIdentity(['id']);
$key = $identity->key(['id' => 42, 'title' => 'Hello']);
// "42"
واجهة RowIdentity
تطبّق جميع استراتيجيات الهوية طريقتين:
interface RowIdentity
{
public function key(array $row): string;
public function columns(): array;
}
تنشئ key() معرّفًا نصيًا فريدًا من بيانات الصف. وتُرجع columns() أسماء الأعمدة المستخدمة لتحديد الهوية.
PrimaryKeyIdentity
الاستراتيجية الافتراضية. تطابق الصفوف باستخدام أعمدة مفاتيحها الأساسية.
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 أو عندما يستخدم الجدول قيدًا فريدًا مركبًا بوصفه هويته المنطقية.
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 لجميع قيم الأعمدة. ويُعد الصفان متطابقين إذا تطابقت جميع الأعمدة.
use Merql\Identity\ContentHashIdentity;
$identity = new ContentHashIdentity(['name', 'value']);
$key = $identity->key(['name' => 'color', 'value' => 'blue']);
// "a3f2..." (SHA-256 hash)
لهوية تجزئة المحتوى قيد: إذا تغير محتوى صف، تتغير تجزئته، فيظهر الصف كأنه حُذف بالتجزئة القديمة وأُدرج بالتجزئة الجديدة بدلًا من تحديثه. وهذه مفاضلة حتمية عند عدم وجود أعمدة هوية مستقرة.
IdentityRule
IdentityRule هو إعداد الهوية القابل للتسلسل، والمستخدم عند التقاط اللقطة
وإعداد خطط الدمج.
use Merql\Identity\IdentityRule;
$rule = IdentityRule::natural(['option_name']);
$key = $rule->key(['option_name' => 'siteurl']);
// "siteurl"
أنواع القواعد:
| النوع | المعنى |
|---|---|
primary |
أعمدة المفتاح الأساسي لقاعدة البيانات |
natural |
أعمدة أعمال فريدة ومستقرة |
composite |
أعمدة الهوية المنطقية المركبة |
content |
خيار احتياطي لتجزئة المحتوى |
تحل IdentityRule::forSchema($schema) القاعدة الافتراضية:
- إذا كان للجدول مفتاح أساسي، تُستخدم أعمدته.
- إذا كان للجدول مفاتيح فريدة، تُستخدم أعمدة أول مفتاح فريد.
- إذا لم يتوفر أي منهما، تُستخدم جميع الأعمدة (خيار تجزئة المحتوى الاحتياطي).
IdentityRuleSet
استخدم IdentityRuleSet عندما يعرف التطبيق المضيف قاعدة هوية أفضل من
القاعدة الافتراضية للمخطط.
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() مجموعة القواعد نفسها عند استخدام الواجهة الثابتة:
use Merql\Merql;
Merql::init($connection, identityRules: $rules);
Merql::snapshot('live', ['wp_options']);
تُسلسَل مجموعات القواعد في المصفوفات بطريقة ثابتة:
$payload = $rules->toArray();
$rules = IdentityRuleSet::fromArray($payload);
هوية ملتبسة
إذا أنتج صفّان ملتقطان مفتاح الهوية نفسه، تفشل عملية الالتقاط بدلًا من استبدال أحد الصفين بالآخر.
use Merql\Exceptions\SnapshotException;
try {
$snapshotter->capture('live', ['wp_postmeta']);
} catch (SnapshotException $e) {
// The configured identity rule matched multiple rows.
}
يمكن للتطبيقات المضيفة التحقق مسبقًا من الصفوف الخام باستخدام IdentityRuleSet::conflictsFor() عندما تريد الإبلاغ عن الالتباس قبل الالتقاط:
$conflicts = $rules->conflictsFor(
'wp_postmeta',
IdentityRule::natural(['post_id', 'meta_key']),
$rows,
);
بالنسبة إلى جداول البيانات الوصفية WordPress، يكون post_id + meta_key مفيدًا عادةً للتجميع والعرض، لكنه غير آمن بوصفه هوية للصف لأن تكرار مفاتيح البيانات الوصفية أمر مسموح. استخدم المفتاح الأساسي للجدول لهوية الصف، ما لم يتمكن التطبيق المضيف من إثبات تفرد المفتاح الطبيعي.
ترميز مفتاح الصف
مفاتيح الصفوف سلاسل مُرمّزة تجمع قيم عدة أعمدة باستخدام فاصل الوحدات (\x1F). تُهرَّب القيم التي تحتوي على الفاصل أو علامة النسبة المئوية لمنع التصادمات:
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. لتجنب ذلك، استخدم مفاتيح طبيعية (أعمدة فريدة ومستقرة عبر الفروع) للجداول التي يُتوقع فيها إدراج متزامن.
// A natural key on (slug) avoids auto-increment collisions.
$identity = new NaturalKeyIdentity(['slug']);
الهوية في اللقطات
تُخزَّن أعمدة الهوية المختارة أثناء الالتقاط مع كل TableSnapshot:
$tableSnapshot = $snapshot->getTable('posts');
$tableSnapshot->identityColumns;
// ['id']
يستخدم SqlGenerator هذه الأعمدة لإنشاء عبارات WHERE الخاصة بعبارات UPDATE وDELETE، كما تستخدمها خطط الدمج لإظهار قيم الهوية المفكوكة للمراجعة والاختيار والتطبيق المحمي والتراجع.