مرجع PHP API
مرجع شامل لجميع الأصناف والأساليب العامة في merql.
Merql (واجهة)
Merql\Merql هو نقطة الدخول الثابتة للعمليات الشائعة. يغلّف الأصناف الأدنى مستوى ويدير حفظ اللقطات تلقائيًا.
use Merql\Connection;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
use Merql\Merql;
$connection = Connection::sqlite(':memory:');
Merql::init($connection, identityRules: new IdentityRuleSet([
'options' => IdentityRule::natural(['option_name']),
]));
init(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null): void
هيّئ الواجهة باتصال قاعدة بيانات. يجب استدعاء ذلك قبل snapshot() أو apply() أو applyGuarded()؛ واستدعها مجددًا للتبديل بين قواعد البيانات، مثلًا لالتقاط ours وtheirs من نسختين مختلفتين. من دون $driver، تستدعي DriverFactory::create()، التي تُلقي استثناءً للمحركات غير MySQL وSQLite ما لم يُسجَّل برنامج تشغيل. مرّر برنامج تشغيل لتجاوز الاكتشاف التلقائي، أو مجموعة قواعد هوية لتجاوز هوية الصفوف لكل جدول أثناء التقاط اللقطة.
snapshot(string $name, array $tables = []): Snapshot
التقط الحالة الحالية لقاعدة البيانات واحفظها على القرص. مرّر مصفوفة بأسماء الجداول لالتقاط جداول محددة، أو اتركها لتشمل جميع الجداول.
$snapshot = Merql::snapshot('baseline');
$snapshot = Merql::snapshot('baseline', ['posts', 'users']);
diff(string $base, string $current): Changeset
حمّل لقطتين مسمّاتين واحسب مجموعة التغييرات بينهما. لا يتطلب init() لأنه يعمل انطلاقًا من اللقطات المحفوظة.
$changeset = Merql::diff('before', 'after');
merge(string $base, string $ours, string $theirs): MergeResult
دمج ثلاثي لثلاث لقطات مسمّاة. يعيد نتيجة تضم عمليات نظيفة وأي تعارضات. يجري الدمج على مستوى الأعمدة: تنشئ الواجهة ThreeWayMerge من دون CellMergeConfig، لذا لا يُطبّق الدمج على مستوى الخلايا. استخدم ThreeWayMerge مباشرةً لذلك.
$result = Merql::merge('base', 'ours', 'theirs');
patch(string $base, string $changes): MergeResult
دمج ثنائي: تطبيق التغييرات على الأساس. يعادل merge($base, $base, $changes). ينتج دائمًا نتيجة نظيفة لعدم وجود تعديلات متزامنة. يقتصر على مستوى الأعمدة مثل merge().
$result = Merql::patch('base', 'changes');
apply(MergeResult $result): ApplyResult
نفّذ نتيجة دمج نظيفة على قاعدة البيانات المُمررة إلى init(). يتطلب init(). يُلقي ConflictException إذا بقيت تعارضات غير محلولة.
تُنفَّذ كل عملية في النتيجة، لذا لا يُطبّق كامل ناتج merge() بسلاسة إلا على قاعدة بيانات ما زالت تحتفظ بحالة الأساس. أما قاعدة البيانات التي تحتفظ بالفعل بحالة أحد الجانبين، فطبّق عليها مجموعة العمليات التي لم تُطبّقها؛ راجع التطبيق المحمي.
$applied = Merql::apply($result);
echo $applied->rowsAffected();
plan(string $id, string $base, string $ours, string $theirs, array $metadata = []): MergePlan
أنشئ خطة دمج جاهزة لـUI انطلاقًا من ثلاث لقطات مسمّاة. تتضمن الخطة معرّفات عمليات ثابتة ومعرّفات مجموعات التغيير وسياق العمليات والتعارضات واختلافات المخطط وملخصًا وبيانات وصفية وتجزئة للخطة. الدمج على مستوى الأعمدة كما في merge()؛ مرّر نتيجة دمج الخلايا إلى MergePlanBuilder::build() لإجراء الدمج على مستوى الخلايا.
$plan = Merql::plan('plan-1', 'base', 'ours', 'theirs', [
'source' => 'review-ui',
]);
applyGuarded(MergeResult $result, Snapshot $expectedLive): ApplyResult
طبّق نتيجة دمج نظيفة مع شروط مسبقة تفاؤلية للصفوف الحية. تتطلب عمليات التحديث والحذف أن تطابق الصفوف الحية الحالية $expectedLive؛ وتتطلب عمليات الإدراج ألا تكون الهوية المستهدفة موجودة بالفعل. $expectedLive لقطة من قاعدة البيانات المستهدفة (theirs، عند ترقية ours إلى قاعدة البيانات التي أُخذت منها لقطة theirs). يؤدي أي استعلام لا يؤثر في أي صف إلى التراجع عن المعاملة.
use Merql\Snapshot\SnapshotStore;
$applied = Merql::applyGuarded($selectedResult, SnapshotStore::load('theirs'));
reset(): void
امسح نسختَي الاتصال وSnapshotter الداخليتين. يُستخدم ذلك أساسًا في الاختبارات لإعادة ضبط الحالة بين عمليات التشغيل.
الاتصال
ينشئ Merql\Connection محوّلات اتصال بإعدادات افتراضية مناسبة. تستخدم اتصالات PDO الاستثناءات عند الخطأ ووضع الجلب الترابطي وعدم محاكاة التحضير وجلب القيم كسلاسل. يطبّع محوّل mysqli القيم المسترجعة لتطابق الشكل نفسه: سلسلة أو null.
mysql(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): PdoDatabaseConnection
use Merql\Connection;
$connection = Connection::mysql('127.0.0.1', 'my_app', 'root', 'secret');
mysqli(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): MysqliDatabaseConnection
$connection = Connection::mysqli('127.0.0.1', 'my_app', 'root', 'secret');
sqlite(string $path = ':memory:'): PdoDatabaseConnection
ينشئ اتصال SQLite. ويفعّل المفاتيح الخارجية تلقائيًا عبر PRAGMA foreign_keys = ON.
$connection = Connection::sqlite('/var/data/app.sqlite');
$connection = Connection::sqlite(':memory:');
fromDsn(string $dsn, string $username = '', string $password = ''): PdoDatabaseConnection
اتصل باستخدام أي سلسلة DSN لـPDO. يضم merql برامج تشغيل لـmysql وsqlite فقط؛ تفتح سلاسل DSN الأخرى اتصالًا، لكن Merql::init() وSnapshotter والمطبّقات تُلقي Unsupported database driver ما لم يُسجّل برنامج تشغيل باستخدام DriverFactory::register().
$connection = Connection::fromDsn('mysql:host=localhost;dbname=app', 'root', 'pass');
fromPdo(PDO $pdo): PdoDatabaseConnection
غلّف نسخة PDO موجودة.
fromMysqli(mysqli $mysqli, string $charset = 'utf8mb4'): MysqliDatabaseConnection
غلّف نسخة mysqli موجودة.
عقد المحوّل
تنفّذ جميع المحوّلات Merql\Database\DatabaseConnection:
interface DatabaseConnection
{
public function driverName(): string;
public function execute(string $sql, array $params = []): int;
public function query(string $sql, array $params = []): array;
public function scalar(string $sql, array $params = []): ?string;
public function beginTransaction(): void;
public function commit(): void;
public function rollBack(): void;
public function lastInsertId(): string;
}
تقبل execute() وquery() وscalar() معاملات list<scalar|null> موضعية لعناصر الإحلال ?. تعيد query() القيمة list<array<string, scalar|null>>؛ وتُطبّع القيم لتأخذ الشكل نفسه، سلسلة أو null، عبر محوّلات PDO وmysqli.
ملتقط اللقطات
يلتقط Merql\Snapshot\Snapshotter حالة قاعدة البيانات. وعلى خلاف الواجهة، يمنحك تحكمًا كاملًا في المرشحات ولا يحفظ اللقطات تلقائيًا.
__construct(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null)
يُكتشف برنامج التشغيل تلقائيًا من هوية الاتصال إن لم يُحدَّد. مرّر IdentityRuleSet عندما ينبغي لهوية الصفوف الخاصة بكل جدول تجاوز الإعدادات الافتراضية للمخطط.
capture(string $name, array $tables = [], array $filters = []): Snapshot
التقط الحالة الحالية. مرّر أسماء الجداول لتحديد النطاق. ومرّر كائنات المرشحات لاستبعاد جداول أو أعمدة أو صفوف.
use Merql\Snapshot\Snapshotter;
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Filter\RowFilter;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
$snapshotter = new Snapshotter(
$connection,
identityRules: new IdentityRuleSet([
'wp_options' => IdentityRule::natural(['option_name']),
]),
);
// All tables.
$snapshot = $snapshotter->capture('full');
// Specific tables with filters.
$snapshot = $snapshotter->capture('filtered', ['posts', 'users'], [
TableFilter::exclude(['cache_*', 'sessions']),
ColumnFilter::ignore(['updated_at', 'created_at']),
RowFilter::create(fn(string $table, array $row) => $row['status'] !== 'trash'),
]);
captureAliased(string $name, array $physicalToCanonicalTableMap, array $filters = []): Snapshot
التقط الجداول الفعلية باستخدام أسماء الجداول الأساسية. استخدم ذلك عندما يكون للجدول المنطقي نفسه أسماء فعلية مختلفة بين البيئات.
$snapshot = $snapshotter->captureAliased('sandbox', [
'wp_onumia_abcd_posts' => 'wp_posts',
'wp_onumia_abcd_postmeta' => 'wp_postmeta',
]);
تخزّن اللقطة الناتجة أسماء الجداول الأساسية مع الحفاظ على مخطط الجدول الفعلي وأعمدة الهوية المضبوطة ومفاتيح الصفوف والمرشحات.
fromData(string $name, array $tableData): Snapshot (ثابتة)
أنشئ لقطة من بيانات خام دون اتصال بقاعدة بيانات. مفيد للاختبار أو عند استيراد البيانات من مصدر آخر.
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;
$schema = new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']);
$snapshot = Snapshotter::fromData('test', [
'posts' => new TableSnapshotData($schema, [
['id' => '1', 'title' => 'Hello'],
['id' => '2', 'title' => 'World'],
], ['id']),
]);
اللقطة
Merql\Snapshot\Snapshot كائن قيمة للقراءة فقط يمثل حالة كاملة لقاعدة البيانات.
$snapshot->name; // string: snapshot identifier
$snapshot->tables; // array<string, TableSnapshot>
$snapshot->tableNames(); // list<string>
$snapshot->hasTable('posts'); // bool
$snapshot->getTable('posts'); // ?TableSnapshot
TableSnapshot
تحتوي Merql\Snapshot\TableSnapshot على صفوف جدول واحد وبصماته ومخططه.
$table = $snapshot->getTable('posts');
$table->schema; // TableSchema
$table->identityColumns; // list<string>: columns used for row identity
$table->fingerprints; // array<string, string>: row key to hash
$table->rows; // array<string, array<string, mixed>>
$table->rowKeys(); // list<string>
$table->rowCount(); // int
$table->hasRow('42'); // bool
$table->getRow('42'); // ?array<string, mixed>
$table->getFingerprint('42'); // ?string
TableSnapshotData
Merql\Snapshot\TableSnapshotData هي بيانات إدخال لـ Snapshotter::fromData().
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;
$data = new TableSnapshotData(
schema: new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']),
rows: [
['id' => '1', 'title' => 'First Post'],
],
identityColumns: ['id'],
);
IdentityRule
تحدد Merql\Identity\IdentityRule الأعمدة المستخدمة لإنشاء مفاتيح الصفوف.
use Merql\Identity\IdentityRule;
$primary = IdentityRule::primary(['id']);
$natural = IdentityRule::natural(['option_name']);
$composite = IdentityRule::composite(['object_id', 'term_taxonomy_id']);
$content = IdentityRule::content(['name', 'value']);
$key = $natural->key(['option_name' => 'siteurl']);
// "siteurl"
forSchema(TableSchema $schema): IdentityRule (ثابتة)
حل قاعدة الهوية الافتراضية من المخطط: المفتاح الأساسي، ثم أول مفتاح فريد، ثم الرجوع إلى المحتوى.
toArray(): array
تسلسل القاعدة بصيغة ['type' => string, 'columns' => list<string>].
fromArray(array $data): IdentityRule (ثابتة)
استعادة قاعدة من بيانات متسلسلة.
IdentityRuleSet
تخزن Merql\Identity\IdentityRuleSet تجاوزات الهوية الخاصة بكل جدول.
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
$rules = new IdentityRuleSet([
'wp_options' => IdentityRule::natural(['option_name']),
]);
$rule = $rules->ruleFor($schema);
with(string $table, IdentityRule $rule): IdentityRuleSet
إرجاع مجموعة قواعد جديدة مع إضافة تجاوز لجدول واحد.
conflictsFor(string $table, IdentityRule $rule, array $rows): array
إرجاع إدخالات IdentityConflict عندما تنتج عدة صفوف المفتاح نفسه لقاعدة ما.
toArray(): array
تسلسل قواعد الجداول في مصفوفة آمنة لـ JSON.
fromArray(array $data): IdentityRuleSet (ثابتة)
استعادة مجموعة قواعد من بيانات متسلسلة.
SnapshotStore
تحفظ Merql\Snapshot\SnapshotStore اللقطات وتحملها بصيغة ملفات JSON.
setDirectory(string $directory): void
تعيين مجلد التخزين. القيمة الافتراضية هي .merql/snapshots.
save(Snapshot $snapshot): void
كتابة لقطة على القرص.
load(string $name): Snapshot
تحميل لقطة حسب الاسم. يُطلق SnapshotException إذا لم يُعثر عليها.
exists(string $name): bool
التحقق من وجود ملف لقطة.
delete(string $name): void
إزالة ملف لقطة.
أداة المقارنة
تقارن Merql\Diff\Differ بين لقطتين وتنشئ مجموعة تغييرات.
diff(Snapshot $base, Snapshot $current): Changeset
use Merql\Diff\Differ;
$differ = new Differ();
$changeset = $differ->diff($baseSnapshot, $currentSnapshot);
valuesEqual(mixed $a, mixed $b): bool (ثابتة)
قارن قيمتين بتحويلهما إلى سلاسل نصية. تُعامل NULL كقيمة مستقلة: NULL === NULL تساوي true، وNULL === '' تساوي false.
مجموعة التغييرات
Merql\Diff\Changeset مجموعة للقراءة فقط من العمليات على مستوى الصفوف.
$changeset->inserts(); // list<RowInsert>
$changeset->updates(); // list<RowUpdate>
$changeset->deletes(); // list<RowDelete>
$changeset->isEmpty(); // bool
$changeset->count(); // int: total operations
$changeset->forTable('posts'); // Changeset: filtered to one table
RowInsert, RowUpdate, RowDelete
كائنات قيم للقراءة فقط تمثل تغييرات الصفوف الفردية.
RowInsert
$insert->table; // string
$insert->rowKey; // string
$insert->values; // array<string, mixed>
RowUpdate
$update->table; // string
$update->rowKey; // string
$update->columnDiffs; // list<ColumnDiff>
$update->fullRow; // array<string, mixed>: complete current row
$update->columnDiffMap(); // array<string, ColumnDiff>: keyed by column name
RowDelete
$delete->table; // string
$delete->rowKey; // string
$delete->oldValues; // array<string, mixed>: values before deletion
ColumnDiff
يمثل Merql\Diff\ColumnDiff تغييرًا واحدًا في عمود ضمن تحديث صف.
$diff->column; // string
$diff->oldValue; // mixed
$diff->newValue; // mixed
ThreeWayMerge
Merql\Merge\ThreeWayMerge هي خوارزمية الدمج الأساسية.
__construct(?CellMergeConfig $cellMergeConfig = null)
مرر CellMergeConfig لتمكين الدمج على مستوى الخلايا لأعمدة TEXT وJSON.
use Merql\Merge\ThreeWayMerge;
use Merql\CellMerge\CellMergeConfig;
// Default: column-level merge only.
$merge = new ThreeWayMerge();
// With cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
merge(Snapshot $base, Snapshot $ours, Snapshot $theirs): MergeResult
إجراء دمج ثلاثي الاتجاهات. يحسب مجموعات التغييرات داخليًا (من الأساس إلى تغييراتنا، ومن الأساس إلى تغييراتهم) ثم يدمجها.
$result = $merge->merge($baseSnapshot, $oursSnapshot, $theirsSnapshot);
patch(Snapshot $base, Snapshot $changes): MergeResult
اختصار للدمج ثنائي الاتجاه. يعادل merge($base, $base, $changes).
schemaMismatches(): array
إرجاع SchemaException[] لأي اختلافات في المخطط رُصدت أثناء عملية الدمج الأخيرة (الأعمدة المضافة أو المحذوفة بين اللقطات).
MergeResult
Merql\Merge\MergeResult كائن قيمة للقراءة فقط يحتوي على نتيجة الدمج.
$result->isClean(); // bool: no conflicts
$result->operations(); // list<MergeOperation>
$result->conflicts(); // list<Conflict>
$result->operationCount(); // int
$result->conflictCount(); // int
$result->baseSnapshot(); // ?Snapshot
$result->schemaMismatches(); // list<SchemaException>
$result->hasSchemaMismatches(); // bool
MergePlan
Merql\Plan\MergePlan نموذج مراجعة قابل للتسلسل لعملية دمج.
$plan->id; // string
$plan->baseSnapshot; // string
$plan->oursSnapshot; // string
$plan->theirsSnapshot; // string
$plan->summary; // MergePlanSummary
$plan->changeGroups; // list<MergePlanChangeGroup>
$plan->operations; // list<MergePlanOperation>
$plan->conflicts; // list<MergePlanConflict>
$plan->schemaMismatches; // list<string>
$plan->hash; // string
$plan->metadata; // array<string, mixed>
استخدم MergePlanSerializer::toJson() و::fromJson() لإجراء رحلات ذهاب وإياب مستقرة لـ JSON.
التحديدات
تخزن ChangeGroupSelection معرّفات مجموعات التغييرات المرحلية. حوّلها إلى OperationSelection عند إنشاء نتائج دمج محددة أو خطط تراجع.
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;
$selection = ChangeGroupSelection::fromIds([$groupId]);
$operationSelection = $selection->toOperationSelection($plan);
$selected = (new SelectedMergeResultFactory())
->fromOperationSelection($plan, $operationSelection);
RollbackPlan
تخزن Merql\Rollback\RollbackPlan العمليات العكسية وتوقعات الانحراف لعملية تطبيق محددة.
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Rollback\RollbackPlanSerializer;
$rollback = (new RollbackPlanBuilder())->build(
'rollback-1',
$plan,
$operationSelection,
$liveBeforeRows,
);
$json = RollbackPlanSerializer::toJson($rollback);
استخدم RollbackDrift قبل تطبيق التراجع، ثم RollbackPlanApplier لتنفيذ العمليات العكسية.
MergeOperation
Merql\Merge\MergeOperation عملية واحدة محسومة ناتجة عن الدمج.
$op->type; // string: 'insert' | 'update' | 'delete'
$op->table; // string
$op->rowKey; // string
$op->values; // array<string, mixed>
$op->source; // string: 'ours' | 'theirs' | 'merged'
ثوابت الأنواع:
MergeOperation::TYPE_INSERT; // 'insert'
MergeOperation::TYPE_UPDATE; // 'update'
MergeOperation::TYPE_DELETE; // 'delete'
التعارض
يمثل Merql\Merge\Conflict تعارض دمج لم يُحل.
$conflict->table(); // string
$conflict->rowKey(); // string
$conflict->type(); // string: 'update_update' | 'update_delete' | 'delete_update' | 'insert_insert'
$conflict->column(); // ?string (null for structural conflicts)
$conflict->oursValue(); // mixed
$conflict->theirsValue(); // mixed
$conflict->baseValue(); // mixed
$conflict->primaryKey(['id']); // array<string, string>: decoded from rowKey
أنواع التعارضات
| النوع | المعنى |
|---|---|
update_update |
غيّر الطرفان العمود نفسه إلى قيمتين مختلفتين |
update_delete |
حدّثنا صفًا حذفه الطرف الآخر |
delete_update |
حذفنا صفًا حدّثه الطرف الآخر |
insert_insert |
أدرج الطرفان صفًا لهما المفتاح الأساسي نفسه |
ConflictResolver
تطبق Merql\Merge\ConflictResolver سياسة حل على جميع التعارضات في نتيجة الدمج.
resolve(MergeResult $result, ConflictPolicy $policy): MergeResult (ثابتة)
إرجاع MergeResult جديد مع حل التعارضات وفقًا للسياسة.
use Merql\Merge\ConflictResolver;
use Merql\Merge\ConflictPolicy;
// Accept all "theirs" values when conflicts arise.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);
// Accept all "ours" values.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::OursWins);
// Return unchanged (for manual inspection).
$resolved = ConflictResolver::resolve($result, ConflictPolicy::Manual);
ConflictPolicy
Merql\Merge\ConflictPolicy تعداد مدعوم بثلاث حالات.
use Merql\Merge\ConflictPolicy;
ConflictPolicy::OursWins; // Resolve all conflicts in favor of "ours"
ConflictPolicy::TheirsWins; // Resolve all conflicts in favor of "theirs"
ConflictPolicy::Manual; // Leave conflicts unresolved for manual handling
ColumnMerge
تنفذ Merql\Merge\ColumnMerge منطق الدمج لكل عمود في صف واحد. وتُستخدم داخليًا بواسطة ThreeWayMerge.
merge(string $table, string $rowKey, array $base, array $ours, array $theirs, ?CellMergeConfig $cellMergeConfig = null, array $columnTypes = []): array (ثابتة)
إرجاع ['values' => array, 'conflicts' => list<Conflict>].
use Merql\Merge\ColumnMerge;
$result = ColumnMerge::merge(
'posts',
'42',
['title' => 'Hello', 'status' => 'draft'], // base
['title' => 'Hello', 'status' => 'published'], // ours
['title' => 'New Title', 'status' => 'draft'], // theirs
);
// $result['values'] = ['title' => 'New Title', 'status' => 'published']
// $result['conflicts'] = [] (clean merge)
أداة التطبيق
تنفذ Merql\Apply\Applier نتيجة دمج على قاعدة بيانات.
__construct(DatabaseConnection $connection, ?Driver $driver = null)
apply(MergeResult $result, ?Snapshot $base = null): ApplyResult
تنفذ جميع العمليات ضمن معاملة واحدة. تتراجع عند حدوث أي خطأ وتُبلغ عنه في ApplyResult::errors() بدلًا من إطلاقه. يُطلق ConflictException إذا احتوت النتيجة على تعارضات لم تُحل.
use Merql\Apply\Applier;
$applier = new Applier($connection);
$applied = $applier->apply($result);
echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
print_r($applied->errors());
}
يُعالج ترتيب المفاتيح الخارجية تلقائيًا: تُرتب عمليات الإدراج حسب ترتيب الاعتماد، وعمليات الحذف بترتيب الاعتماد العكسي.
ApplyResult
Merql\Apply\ApplyResult كائن قيمة للقراءة فقط تُرجعه أداة التطبيق.
$applied->rowsAffected(); // int
$applied->errors(); // list<string>
$applied->hasErrors(); // bool
بعد التراجع عن عملية تطبيق، يظل rowsAffected() يحصي العبارات التي نُفذت قبل الإخفاق (#4). تحقق من hasErrors() قبل الاعتماد على العدد.
DryRun
تنشئ Merql\Apply\DryRun عبارات SQL سهلة القراءة دون تنفيذ أي شيء.
generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array (ثابتة)
إرجاع قائمة بسلاسل SQL مع تضمين القيم فيها (للعرض فقط).
use Merql\Apply\DryRun;
$statements = DryRun::generate($result);
foreach ($statements as $sql) {
echo "{$sql};\n";
}
// Output:
// INSERT INTO `posts` (`id`, `title`) VALUES ('10', 'New Post');
// UPDATE `posts` SET `title` = 'Updated' WHERE `id` = '5';
// DELETE FROM `comments` WHERE `id` = '3';
SqlGenerator
تنشئ Merql\Apply\SqlGenerator عبارات SQL ذات معلمات. وتُستخدم داخليًا بواسطة كل من Applier وDryRun.
generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array (ثابتة)
إرجاع list<array{sql: string, params: list<scalar|null>}>.
use Merql\Apply\SqlGenerator;
$statements = SqlGenerator::generate($result);
foreach ($statements as $stmt) {
$connection->execute($stmt['sql'], $stmt['params']);
}
CellMergeConfig
تهيئ Merql\CellMerge\CellMergeConfig استراتيجيات دمج الخلايا لأعمدة أو أنواع أعمدة محددة.
auto(): self (ثابتة)
دالة إنشاء ميسّرة تعيّن TextCellMerger لأعمدة TEXT/LONGTEXT وJsonCellMerger لأعمدة JSON.
use Merql\CellMerge\CellMergeConfig;
$config = CellMergeConfig::auto();
forColumn(string $column, CellMerger $merger): self
تعيين أداة دمج لعمود محدد. استخدم table.column للأسماء المؤهلة أو column لجميع الجداول.
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\TextCellMerger;
$config = (new CellMergeConfig())
->forColumn('posts.content', new TextCellMerger())
->forColumn('description', new TextCellMerger());
forType(string $typePattern, CellMerger $merger): self
تعيين أداة دمج لجميع الأعمدة المطابقة لنمط نوع (مطابقة غير حساسة لحالة الأحرف للسلسلة الفرعية).
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;
$config = (new CellMergeConfig())
->forType('json', new JsonCellMerger());
getMerger(string $table, string $column, string $columnType = ''): CellMerger
البحث عن أداة الدمج لجدول وعمود ونوع محددين. الأولوية: اسم العمود المؤهل، ثم اسم العمود غير المؤهل، ثم نمط النوع، ثم الافتراضي (المعتم).
CellMerger (واجهة)
Merql\CellMerge\CellMerger هي الواجهة لاستراتيجيات دمج الخلايا.
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;
interface CellMerger
{
public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult;
}
تتوفر ثلاثة تطبيقات مدمجة.
TextCellMerger
تدمج Merql\CellMerge\TextCellMerger النصوص متعددة الأسطر باستخدام فرق ثلاثي الاتجاهات سطرًا بسطر (الخوارزمية نفسها التي يستخدمها git لمحتوى الملفات). عندما يعدّل الطرفان سطورًا مختلفة، يتم الدمج بنجاح. وعندما يعدّلان السطر نفسه بطرق مختلفة، يُبلغ عن تعارض.
use Merql\CellMerge\TextCellMerger;
$merger = new TextCellMerger();
$result = $merger->merge(
"line 1\nline 2\nline 3",
"line 1\nline 2 modified\nline 3",
"line 1\nline 2\nline 3 modified",
);
// $result->clean === true
// $result->value === "line 1\nline 2 modified\nline 3 modified"
JsonCellMerger
تدمج Merql\CellMerge\JsonCellMerger قيم JSON بمقارنة المفاتيح ذات المستوى الأعلى كلًّا على حدة. يُدمج كل مفتاح باستخدام منطق ثلاثي الاتجاهات. وتُقارن الكائنات المتداخلة كسلاسل JSON معتمة (ولا يجري دمجها تكراريًا).
use Merql\CellMerge\JsonCellMerger;
$merger = new JsonCellMerger();
$result = $merger->merge(
'{"a": 1, "b": 2}',
'{"a": 10, "b": 2}', // changed "a"
'{"a": 1, "b": 20}', // changed "b"
);
// $result->clean === true
// $result->value === '{"a":10,"b":20}'
OpaqueCellMerger
Merql\CellMerge\OpaqueCellMerger هي القيمة الافتراضية. تتعامل مع القيم كسلاسل معتمة وتُبلغ دائمًا عن تعارض عند اختلافها.
CellMergeResult
Merql\CellMerge\CellMergeResult هو نوع الإرجاع لجميع أدوات دمج الخلايا.
$result->clean; // bool: true if merge succeeded
$result->value; // mixed: merged value (or ours on conflict)
$result->conflicts; // int: number of conflicts
// Factory methods.
CellMergeResult::resolved($value); // clean merge
CellMergeResult::conflict($oursValue, $n); // conflict with count
TableSchema
Merql\Schema\TableSchema تمثيل للقراءة فقط لبنية الجدول.
use Merql\Schema\TableSchema;
$schema = new TableSchema(
name: 'posts',
columns: ['id' => 'int', 'title' => 'varchar(255)', 'content' => 'text'],
primaryKey: ['id'],
uniqueKeys: [['slug']],
);
$schema->name; // string
$schema->columns; // array<string, string>: name to type
$schema->primaryKey; // list<string>
$schema->uniqueKeys; // list<list<string>>
$schema->hasPrimaryKey(); // bool
$schema->hasUniqueKeys(); // bool
$schema->columnNames(); // list<string>
نظام برامج التشغيل
يستخدم merql نظام برامج تشغيل قابلاً للتوصيل لتجريد العمليات الخاصة بكل قاعدة بيانات. تُكتشف برامج التشغيل تلقائيًا من هوية برنامج تشغيل الاتصال.
برنامج التشغيل (واجهة)
تحدد Merql\Driver\Driver العقد الذي تنفذه جميع برامج تشغيل قواعد البيانات.
interface Driver
{
public function quoteIdentifier(string $name): string;
public function listTables(DatabaseConnection $connection): array;
public function readSchema(DatabaseConnection $connection, string $table): TableSchema;
public function readForeignKeys(DatabaseConnection $connection): array;
public function selectAll(string $table): string;
}
التطبيقات المدمجة: MysqlDriver وSqliteDriver.
DriverFactory
تكتشف Merql\Driver\DriverFactory برنامج التشغيل تلقائيًا من الاتصال.
use Merql\Driver\DriverFactory;
$driver = DriverFactory::create($connection);
سجّل برنامج تشغيل مخصصًا لقواعد بيانات أخرى:
DriverFactory::register('pgsql', MyPostgresDriver::class);
المرشحات
TableFilter
تضمّن Merql\Filter\TableFilter الجداول أو تستبعدها باستخدام أنماط glob.
use Merql\Filter\TableFilter;
$filter = TableFilter::exclude(['cache_*', 'sessions', 'temp_*']);
$filter = TableFilter::include(['posts', 'users', 'comments']);
ColumnFilter
تزيل Merql\Filter\ColumnFilter أعمدة محددة من اللقطات.
use Merql\Filter\ColumnFilter;
$filter = ColumnFilter::ignore(['updated_at', 'created_at', 'modified_date']);
RowFilter
تصفّي Merql\Filter\RowFilter الصفوف باستخدام دالة شرطية.
use Merql\Filter\RowFilter;
$filter = RowFilter::create(function (string $table, array $row): bool {
// Exclude trashed posts.
if ($table === 'posts' && ($row['status'] ?? '') === 'trash') {
return false;
}
return true;
});
الاستثناءات
SnapshotException
يُطلق Merql\Exceptions\SnapshotException عندما يتعذر العثور على لقطة أو يكون اسمها غير صالح.
SnapshotException::notFound('baseline', '/path/to/baseline.json');
MergeException
يُطلق Merql\Exceptions\MergeException عند غياب لقطة لازمة لإجراء الدمج.
MergeException::snapshotRequired('base');
ConflictException
يُطلق Merql\Exceptions\ConflictException عند محاولة تطبيق نتيجة دمج تحتوي على تعارضات لم تُحل.
ConflictException::unresolved(3);
// Message: "Cannot apply merge with 3 unresolved conflict(s)"
SchemaException
يُطلق Merql\Exceptions\SchemaException (أو يُجمع) عند اختلاف المخططات بين اللقطات.
SchemaException::mismatch('posts', 'column "tags" exists in current but not in base');
لا توقف حالات عدم تطابق المخطط عملية الدمج. بل تُجمع وتُتاح عبر $result->schemaMismatches() لفحصها.