Перейти к содержимому

Справочник 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(); вызовите ещё раз, чтобы переключить базу данных, например, для создания снимков нашей и другой сторон из двух разных копий. Если не передан $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 на основе трёх именованных снимков. План включает стабильные ID операций, ID групп изменений, контекст операций, конфликты, несоответствия схем, сводку, метаданные и хеш плана. Слияние выполняется на уровне столбцов, как в merge(); передайте результат с объединёнными ячейками в MergePlanBuilder::build() для слияния на уровне ячеек.

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

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

Применяет результат слияния без конфликтов с оптимистической проверкой предварительных условий для строк в базе данных. Для обновления и удаления необходимо, чтобы текущие строки соответствовали $expectedLive; для вставки целевой идентификатор не должен существовать. $expectedLive — это снимок целевой базы данных (другой стороны, если наша версия переносится в базу, из которой был создан снимок другой стороны). Если какой-либо запрос затрагивает ноль строк, транзакция откатывается.

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>>; значения в адаптерах PDO и mysqli приводятся к одному формату — строка или null.


Создатель снимков

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 — истина, NULL === '' — ложь.


Набор изменений

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 хранит ID подготовленных групп изменений. Преобразуйте его в 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 использует подключаемую систему драйверов для абстрагирования операций, специфичных для баз данных. Драйверы определяются автоматически по идентификатору драйвера подключения.

Driver (интерфейс)

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 включает таблицы или исключает их по глоб-шаблонам.

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