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

مرجع PHP API

مرجع شامل لجميع الأصناف والأساليب العامة في merql.

Merql (واجهة)

Merql\Merql هو نقطة الدخول الثابتة للعمليات الشائعة. يغلّف الأصناف الأدنى مستوى ويدير حفظ اللقطات تلقائيًا.

PHP
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

التقط الحالة الحالية لقاعدة البيانات واحفظها على القرص. مرّر مصفوفة بأسماء الجداول لالتقاط جداول محددة، أو اتركها لتشمل جميع الجداول.

PHP
$snapshot = Merql::snapshot('baseline');
$snapshot = Merql::snapshot('baseline', ['posts', 'users']);

diff(string $base, string $current): Changeset

حمّل لقطتين مسمّاتين واحسب مجموعة التغييرات بينهما. لا يتطلب init() لأنه يعمل انطلاقًا من اللقطات المحفوظة.

PHP
$changeset = Merql::diff('before', 'after');

merge(string $base, string $ours, string $theirs): MergeResult

دمج ثلاثي لثلاث لقطات مسمّاة. يعيد نتيجة تضم عمليات نظيفة وأي تعارضات. يجري الدمج على مستوى الأعمدة: تنشئ الواجهة ThreeWayMerge من دون CellMergeConfig، لذا لا يُطبّق الدمج على مستوى الخلايا. استخدم ThreeWayMerge مباشرةً لذلك.

PHP
$result = Merql::merge('base', 'ours', 'theirs');

patch(string $base, string $changes): MergeResult

دمج ثنائي: تطبيق التغييرات على الأساس. يعادل merge($base, $base, $changes). ينتج دائمًا نتيجة نظيفة لعدم وجود تعديلات متزامنة. يقتصر على مستوى الأعمدة مثل merge().

PHP
$result = Merql::patch('base', 'changes');

apply(MergeResult $result): ApplyResult

نفّذ نتيجة دمج نظيفة على قاعدة البيانات المُمررة إلى init(). يتطلب init(). يُلقي ConflictException إذا بقيت تعارضات غير محلولة.

تُنفَّذ كل عملية في النتيجة، لذا لا يُطبّق كامل ناتج merge() بسلاسة إلا على قاعدة بيانات ما زالت تحتفظ بحالة الأساس. أما قاعدة البيانات التي تحتفظ بالفعل بحالة أحد الجانبين، فطبّق عليها مجموعة العمليات التي لم تُطبّقها؛ راجع التطبيق المحمي.

PHP
$applied = Merql::apply($result);
echo $applied->rowsAffected();

plan(string $id, string $base, string $ours, string $theirs, array $metadata = []): MergePlan

أنشئ خطة دمج جاهزة لـUI انطلاقًا من ثلاث لقطات مسمّاة. تتضمن الخطة معرّفات عمليات ثابتة ومعرّفات مجموعات التغيير وسياق العمليات والتعارضات واختلافات المخطط وملخصًا وبيانات وصفية وتجزئة للخطة. الدمج على مستوى الأعمدة كما في merge()؛ مرّر نتيجة دمج الخلايا إلى MergePlanBuilder::build() لإجراء الدمج على مستوى الخلايا.

PHP
$plan = Merql::plan('plan-1', 'base', 'ours', 'theirs', [
    'source' => 'review-ui',
]);

applyGuarded(MergeResult $result, Snapshot $expectedLive): ApplyResult

طبّق نتيجة دمج نظيفة مع شروط مسبقة تفاؤلية للصفوف الحية. تتطلب عمليات التحديث والحذف أن تطابق الصفوف الحية الحالية $expectedLive؛ وتتطلب عمليات الإدراج ألا تكون الهوية المستهدفة موجودة بالفعل. $expectedLive لقطة من قاعدة البيانات المستهدفة (theirs، عند ترقية ours إلى قاعدة البيانات التي أُخذت منها لقطة theirs). يؤدي أي استعلام لا يؤثر في أي صف إلى التراجع عن المعاملة.

PHP
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

PHP
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

PHP
$connection = Connection::mysqli('127.0.0.1', 'my_app', 'root', 'secret');

sqlite(string $path = ':memory:'): PdoDatabaseConnection

ينشئ اتصال SQLite. ويفعّل المفاتيح الخارجية تلقائيًا عبر PRAGMA foreign_keys = ON.

PHP
$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().

PHP
$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:

PHP
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

التقط الحالة الحالية. مرّر أسماء الجداول لتحديد النطاق. ومرّر كائنات المرشحات لاستبعاد جداول أو أعمدة أو صفوف.

PHP
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

التقط الجداول الفعلية باستخدام أسماء الجداول الأساسية. استخدم ذلك عندما يكون للجدول المنطقي نفسه أسماء فعلية مختلفة بين البيئات.

PHP
$snapshot = $snapshotter->captureAliased('sandbox', [
    'wp_onumia_abcd_posts' => 'wp_posts',
    'wp_onumia_abcd_postmeta' => 'wp_postmeta',
]);

تخزّن اللقطة الناتجة أسماء الجداول الأساسية مع الحفاظ على مخطط الجدول الفعلي وأعمدة الهوية المضبوطة ومفاتيح الصفوف والمرشحات.

fromData(string $name, array $tableData): Snapshot (ثابتة)

أنشئ لقطة من بيانات خام دون اتصال بقاعدة بيانات. مفيد للاختبار أو عند استيراد البيانات من مصدر آخر.

PHP
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 كائن قيمة للقراءة فقط يمثل حالة كاملة لقاعدة البيانات.

PHP
$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 على صفوف جدول واحد وبصماته ومخططه.

PHP
$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().

PHP
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 الأعمدة المستخدمة لإنشاء مفاتيح الصفوف.

PHP
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 تجاوزات الهوية الخاصة بكل جدول.

PHP
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

PHP
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 مجموعة للقراءة فقط من العمليات على مستوى الصفوف.

PHP
$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

PHP
$insert->table;                    // string
$insert->rowKey;                   // string
$insert->values;                   // array<string, mixed>

RowUpdate

PHP
$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

PHP
$delete->table;                    // string
$delete->rowKey;                   // string
$delete->oldValues;                // array<string, mixed>: values before deletion

ColumnDiff

يمثل Merql\Diff\ColumnDiff تغييرًا واحدًا في عمود ضمن تحديث صف.

PHP
$diff->column;                     // string
$diff->oldValue;                   // mixed
$diff->newValue;                   // mixed

ThreeWayMerge

Merql\Merge\ThreeWayMerge هي خوارزمية الدمج الأساسية.

__construct(?CellMergeConfig $cellMergeConfig = null)

مرر CellMergeConfig لتمكين الدمج على مستوى الخلايا لأعمدة TEXT وJSON.

PHP
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

إجراء دمج ثلاثي الاتجاهات. يحسب مجموعات التغييرات داخليًا (من الأساس إلى تغييراتنا، ومن الأساس إلى تغييراتهم) ثم يدمجها.

PHP
$result = $merge->merge($baseSnapshot, $oursSnapshot, $theirsSnapshot);

patch(Snapshot $base, Snapshot $changes): MergeResult

اختصار للدمج ثنائي الاتجاه. يعادل merge($base, $base, $changes).

schemaMismatches(): array

إرجاع SchemaException[] لأي اختلافات في المخطط رُصدت أثناء عملية الدمج الأخيرة (الأعمدة المضافة أو المحذوفة بين اللقطات).


MergeResult

Merql\Merge\MergeResult كائن قيمة للقراءة فقط يحتوي على نتيجة الدمج.

PHP
$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 نموذج مراجعة قابل للتسلسل لعملية دمج.

PHP
$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 عند إنشاء نتائج دمج محددة أو خطط تراجع.

PHP
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 العمليات العكسية وتوقعات الانحراف لعملية تطبيق محددة.

PHP
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 عملية واحدة محسومة ناتجة عن الدمج.

PHP
$op->type;                         // string: 'insert' | 'update' | 'delete'
$op->table;                        // string
$op->rowKey;                       // string
$op->values;                       // array<string, mixed>
$op->source;                       // string: 'ours' | 'theirs' | 'merged'

ثوابت الأنواع:

PHP
MergeOperation::TYPE_INSERT;       // 'insert'
MergeOperation::TYPE_UPDATE;       // 'update'
MergeOperation::TYPE_DELETE;       // 'delete'

التعارض

يمثل Merql\Merge\Conflict تعارض دمج لم يُحل.

PHP
$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 جديد مع حل التعارضات وفقًا للسياسة.

PHP
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 تعداد مدعوم بثلاث حالات.

PHP
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>].

PHP
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 إذا احتوت النتيجة على تعارضات لم تُحل.

PHP
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 كائن قيمة للقراءة فقط تُرجعه أداة التطبيق.

PHP
$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 مع تضمين القيم فيها (للعرض فقط).

PHP
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>}>.

PHP
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.

PHP
use Merql\CellMerge\CellMergeConfig;

$config = CellMergeConfig::auto();

forColumn(string $column, CellMerger $merger): self

تعيين أداة دمج لعمود محدد. استخدم table.column للأسماء المؤهلة أو column لجميع الجداول.

PHP
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

تعيين أداة دمج لجميع الأعمدة المطابقة لنمط نوع (مطابقة غير حساسة لحالة الأحرف للسلسلة الفرعية).

PHP
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 هي الواجهة لاستراتيجيات دمج الخلايا.

PHP
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 لمحتوى الملفات). عندما يعدّل الطرفان سطورًا مختلفة، يتم الدمج بنجاح. وعندما يعدّلان السطر نفسه بطرق مختلفة، يُبلغ عن تعارض.

PHP
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 معتمة (ولا يجري دمجها تكراريًا).

PHP
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 هو نوع الإرجاع لجميع أدوات دمج الخلايا.

PHP
$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 تمثيل للقراءة فقط لبنية الجدول.

PHP
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 العقد الذي تنفذه جميع برامج تشغيل قواعد البيانات.

PHP
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 برنامج التشغيل تلقائيًا من الاتصال.

PHP
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);

سجّل برنامج تشغيل مخصصًا لقواعد بيانات أخرى:

PHP
DriverFactory::register('pgsql', MyPostgresDriver::class);

المرشحات

TableFilter

تضمّن Merql\Filter\TableFilter الجداول أو تستبعدها باستخدام أنماط glob.

PHP
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['cache_*', 'sessions', 'temp_*']);
$filter = TableFilter::include(['posts', 'users', 'comments']);

ColumnFilter

تزيل Merql\Filter\ColumnFilter أعمدة محددة من اللقطات.

PHP
use Merql\Filter\ColumnFilter;

$filter = ColumnFilter::ignore(['updated_at', 'created_at', 'modified_date']);

RowFilter

تصفّي Merql\Filter\RowFilter الصفوف باستخدام دالة شرطية.

PHP
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 عندما يتعذر العثور على لقطة أو يكون اسمها غير صالح.

PHP
SnapshotException::notFound('baseline', '/path/to/baseline.json');

MergeException

يُطلق Merql\Exceptions\MergeException عند غياب لقطة لازمة لإجراء الدمج.

PHP
MergeException::snapshotRequired('base');

ConflictException

يُطلق Merql\Exceptions\ConflictException عند محاولة تطبيق نتيجة دمج تحتوي على تعارضات لم تُحل.

PHP
ConflictException::unresolved(3);
// Message: "Cannot apply merge with 3 unresolved conflict(s)"

SchemaException

يُطلق Merql\Exceptions\SchemaException (أو يُجمع) عند اختلاف المخططات بين اللقطات.

PHP
SchemaException::mismatch('posts', 'column "tags" exists in current but not in base');

لا توقف حالات عدم تطابق المخطط عملية الدمج. بل تُجمع وتُتاح عبر $result->schemaMismatches() لفحصها.