Довідник 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
Виконує тристороннє злиття. Внутрішньо обчислює набори змін (від базового стану до ours, від базового стану до theirs) та об'єднує їх.
$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 |
Сторона ours оновила рядок, який сторона theirs видалила |
delete_update |
Сторона ours видалила рядок, який сторона theirs оновила |
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 — це enum із трьома варіантами.
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
Шукає об'єднувач для заданих таблиці, стовпця й типу. Пріоритет: повна назва стовпця, коротка назва стовпця, шаблон типу, типовий варіант (opaque).
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 використовує систему драйверів із можливістю підключення, щоб абстрагувати операції, специфічні для баз даних. Драйвери автоматично визначаються за ідентифікатором драйвера з'єднання.
Driver (інтерфейс)
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().