Aller au contenu

Référence PHP API

Référence complète de toutes les classes et méthodes publiques de merql.

Merql (façade)

Merql\Merql est le point d'entrée statique pour les opérations courantes. Il encapsule les classes de niveau inférieur et gère automatiquement la persistance des instantanés.

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

Initialisez la façade avec une connexion à la base de données. Doit être appelé avant snapshot(), apply() ou applyGuarded() ; rappelez cette méthode pour changer de base de données, par exemple pour créer des instantanés de ours et theirs à partir de deux copies différentes. En l'absence de $driver, la méthode appelle DriverFactory::create(), qui lève une exception pour les moteurs autres que MySQL et SQLite, sauf si un pilote est enregistré. Fournissez un pilote pour remplacer la détection automatique, ou un ensemble de règles d'identité pour remplacer l'identité des lignes par table lors de la capture d'instantanés.

snapshot(string $name, array $tables = []): Snapshot

Capturez l'état actuel de la base de données et enregistrez-le sur disque. Passez un tableau de noms de tables pour créer un instantané de certaines tables, ou omettez-le pour inclure toutes les tables.

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

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

Chargez deux instantanés nommés et calculez les modifications entre eux. init() n'est pas nécessaire, car cette opération utilise les instantanés persistés.

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

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

Fusion à trois voies de trois instantanés nommés. Renvoie un résultat contenant les opérations sans conflit et les éventuels conflits. La fusion s'effectue au niveau des colonnes : la façade construit ThreeWayMerge sans CellMergeConfig, la fusion au niveau des cellules n'est donc pas appliquée. Utilisez directement ThreeWayMerge pour cela.

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

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

Fusion à deux voies : appliquez les modifications à la base. Équivalent à merge($base, $base, $changes). Produit toujours un résultat sans conflit, car il n'y a pas de modifications concurrentes. Au niveau des colonnes uniquement, comme merge().

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

apply(MergeResult $result): ApplyResult

Exécutez un résultat de fusion sans conflit sur la base de données transmise à init(). Nécessite init(). Lève ConflictException si des conflits non résolus subsistent.

Toutes les opérations du résultat sont exécutées. Le résultat complet de merge() ne s'applique donc correctement qu'à une base de données qui contient toujours l'état de base. Pour une base de données qui contient déjà l'un des côtés, appliquez une sélection des opérations qui lui manquent ; voir Application protégée.

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

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

Créez un plan de fusion compatible avec UI à partir de trois instantanés nommés. Le plan comprend des ID d'opération stables, des ID de groupes de modifications, le contexte des opérations, les conflits, les différences de schéma, un résumé, des métadonnées et un hachage du plan. La fusion s'effectue au niveau des colonnes, comme dans merge() ; transmettez un résultat fusionné au niveau des cellules à MergePlanBuilder::build() pour effectuer une fusion à ce niveau.

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

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

Appliquez un résultat de fusion sans conflit avec des préconditions optimistes sur les lignes en temps réel. Pour les mises à jour et les suppressions, les lignes actives doivent correspondre à $expectedLive ; pour les insertions, l'identité cible ne doit pas déjà exister. $expectedLive est un instantané de la base de données cible (theirs, lorsque ours est promu dans la base à partir de laquelle theirs a été créé). Toute instruction qui ne modifie aucune ligne annule la transaction.

PHP
use Merql\Snapshot\SnapshotStore;

$applied = Merql::applyGuarded($selectedResult, SnapshotStore::load('theirs'));

reset(): void

Efface les instances internes de connexion et de Snapshotter. Sert principalement à réinitialiser l'état entre les exécutions de tests.


Connexion

Merql\Connection construit des adaptateurs de connexion avec des valeurs par défaut adaptées. Les connexions PDO utilisent les exceptions en cas d'erreur, le mode de récupération associatif, les préparations non émulées et la récupération des valeurs sous forme de chaînes. L'adaptateur mysqli normalise les valeurs récupérées pour obtenir la même forme chaîne ou 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

Crée une connexion SQLite. Active automatiquement les clés étrangères via PRAGMA foreign_keys = ON.

PHP
$connection = Connection::sqlite('/var/data/app.sqlite');
$connection = Connection::sqlite(':memory:');

fromDsn(string $dsn, string $username = '', string $password = ''): PdoDatabaseConnection

Connectez-vous à l'aide de n'importe quelle chaîne DSN PDO. merql fournit des pilotes uniquement pour mysql et sqlite ; les autres DSN permettent d'ouvrir une connexion, mais Merql::init(), Snapshotter et les composants d'application lèvent Unsupported database driver, sauf si un pilote est enregistré avec DriverFactory::register().

PHP
$connection = Connection::fromDsn('mysql:host=localhost;dbname=app', 'root', 'pass');

fromPdo(PDO $pdo): PdoDatabaseConnection

Encapsule une instance PDO existante.

fromMysqli(mysqli $mysqli, string $charset = 'utf8mb4'): MysqliDatabaseConnection

Encapsule une instance mysqli existante.

Contrat des adaptateurs

Tous les adaptateurs implémentent 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() et scalar() acceptent des paramètres positionnels list<scalar|null> pour les espaces réservés ?. query() renvoie list<array<string, scalar|null>> ; les valeurs sont normalisées sous la même forme chaîne ou null avec les adaptateurs PDO et mysqli.


Snapshotter

Merql\Snapshot\Snapshotter capture l'état de la base de données. Contrairement à la façade, il vous donne un contrôle total sur les filtres et n'enregistre pas automatiquement les instantanés.

__construct(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null)

Si aucun pilote n'est fourni, il est détecté automatiquement à partir de l'identité de la connexion. Fournissez un IdentityRuleSet si une identité de ligne propre à une table doit remplacer les valeurs par défaut du schéma.

capture(string $name, array $tables = [], array $filters = []): Snapshot

Capturez l'état actuel. Indiquez des noms de tables pour limiter la portée. Passez des objets de filtre pour exclure des tables, des colonnes ou des lignes.

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

Capturez les tables physiques en leur attribuant des noms de table canoniques. Utilisez cette méthode lorsque la même table logique porte des noms physiques différents selon les environnements.

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

L'instantané obtenu enregistre les noms de tables canoniques tout en conservant le schéma des tables physiques, les colonnes d'identité configurées, les clés des lignes et les filtres.

fromData(string $name, array $tableData): Snapshot (statique)

Créez un instantané à partir de données brutes, sans connexion à une base de données. Utile pour les tests ou l'importation de données depuis une autre source.

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']),
]);

Instantané

Merql\Snapshot\Snapshot est un objet valeur en lecture seule qui représente un état complet de base de données.

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 contient les lignes, les empreintes numériques et le schéma d'une table.

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 représente les données d'entrée de 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 définit les colonnes utilisées pour construire les clés des lignes.

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

Résout la règle d'identité par défaut à partir du schéma : clé primaire, première clé unique, puis repli sur le contenu.

toArray(): array

Sérialise la règle en ['type' => string, 'columns' => list<string>].

fromArray(array $data): IdentityRule (statique)

Hydrate une règle à partir de données sérialisées.


IdentityRuleSet

Merql\Identity\IdentityRuleSet stocke les remplacements de règles d'identité propres aux tables.

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

Renvoie un nouvel ensemble de règles auquel une substitution pour une table a été ajoutée.

conflictsFor(string $table, IdentityRule $rule, array $rows): array

Renvoie les entrées IdentityConflict lorsque plusieurs lignes produisent la même clé pour une règle.

toArray(): array

Sérialise les règles de table sous forme de tableau compatible avec JSON.

fromArray(array $data): IdentityRuleSet (statique)

Hydrate un ensemble de règles à partir de données sérialisées.


SnapshotStore

Merql\Snapshot\SnapshotStore enregistre et charge les instantanés sous forme de fichiers JSON.

setDirectory(string $directory): void

Définit le répertoire de stockage. La valeur par défaut est .merql/snapshots.

save(Snapshot $snapshot): void

Écrit un instantané sur le disque.

load(string $name): Snapshot

Charge un instantané par nom. Lève SnapshotException s'il est introuvable.

exists(string $name): bool

Vérifie si un fichier d'instantané existe.

delete(string $name): void

Supprime un fichier d'instantané.


Comparateur

Merql\Diff\Differ compare deux instantanés et produit un ensemble de modifications.

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

Compare deux valeurs après coercition en chaînes. NULL est traité comme une valeur distincte : NULL === NULL est vrai, NULL === '' est faux.


Ensemble de modifications

Merql\Diff\Changeset est une collection en lecture seule d'opérations au niveau des lignes.

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

Objets valeur en lecture seule représentant des modifications individuelles de lignes.

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 représente une modification de colonne unique au sein d'une mise à jour de ligne.

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

ThreeWayMerge

Merql\Merge\ThreeWayMerge est l'algorithme central de fusion.

__construct(?CellMergeConfig $cellMergeConfig = null)

Fournissez un CellMergeConfig pour activer la fusion au niveau des cellules pour les colonnes TEXT et 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

Effectue une fusion à trois voies. Calcule les ensembles de modifications en interne (base vers ours, base vers theirs), puis les fusionne.

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

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

Raccourci pour une fusion à deux voies. Équivalent à merge($base, $base, $changes).

schemaMismatches(): array

Renvoie SchemaException[] pour toute différence de schéma détectée lors de la dernière fusion (colonnes ajoutées ou supprimées entre les instantanés).


MergeResult

Merql\Merge\MergeResult est un objet valeur en lecture seule contenant le résultat de la fusion.

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 est un modèle sérialisable permettant d'examiner une fusion.

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>

Utilisez MergePlanSerializer::toJson() et ::fromJson() pour des allers-retours JSON stables.

Sélections

ChangeGroupSelection stocke les ID des groupes de modifications préparés. Convertissez-le en OperationSelection lors de la création de résultats de fusion sélectionnés ou de plans de restauration.

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 stocke les opérations inverses et les attentes de dérive pour une application sélectionnée.

PHP
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Rollback\RollbackPlanSerializer;

$rollback = (new RollbackPlanBuilder())->build(
    'rollback-1',
    $plan,
    $operationSelection,
    $liveBeforeRows,
);

$json = RollbackPlanSerializer::toJson($rollback);

Utilisez RollbackDrift avant d'appliquer le retour arrière, puis RollbackPlanApplier pour exécuter les opérations inverses.


MergeOperation

Merql\Merge\MergeOperation est une opération unique résolue issue de la fusion.

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

Constantes de type :

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

Conflit

Merql\Merge\Conflict représente un conflit de fusion non résolu.

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

Types de conflit

Type Signification
update_update Les deux côtés ont modifié la même colonne avec des valeurs différentes
update_delete Notre côté a mis à jour une ligne que leur côté a supprimée
delete_update Notre côté a supprimé une ligne que leur côté a mise à jour
insert_insert Les deux côtés ont inséré une ligne avec la même clé primaire

ConflictResolver

Merql\Merge\ConflictResolver applique une règle de résolution à tous les conflits d'un résultat de fusion.

resolve(MergeResult $result, ConflictPolicy $policy): MergeResult (statique)

Renvoie un nouveau MergeResult dont les conflits sont résolus selon la règle.

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 est une énumération adossée comportant trois cas.

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 applique la logique de fusion colonne par colonne pour une seule ligne. Utilisé en interne par ThreeWayMerge.

merge(string $table, string $rowKey, array $base, array $ours, array $theirs, ?CellMergeConfig $cellMergeConfig = null, array $columnTypes = []): array (statique)

Renvoie ['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)

Appliqueur

Merql\Apply\Applier exécute un résultat de fusion sur une base de données.

__construct(DatabaseConnection $connection, ?Driver $driver = null)

apply(MergeResult $result, ?Snapshot $base = null): ApplyResult

Exécute toutes les opérations dans une seule transaction. Annule la transaction en cas d'erreur et la signale dans ApplyResult::errors() au lieu de lever une exception. Lève ConflictException si le résultat comporte des conflits non résolus.

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());
}

L'ordre des clés étrangères est géré automatiquement : les insertions sont triées selon l'ordre des dépendances, et les suppressions dans l'ordre inverse.


ApplyResult

Merql\Apply\ApplyResult est un objet valeur en lecture seule renvoyé par l'appliqueur.

PHP
$applied->rowsAffected();         // int
$applied->errors();               // list<string>
$applied->hasErrors();            // bool

Après une application annulée, rowsAffected() compte toujours les instructions exécutées avant l'échec (#4). Vérifiez hasErrors() avant de vous fier à ce décompte.


DryRun

Merql\Apply\DryRun génère des SQL lisibles sans rien exécuter.

generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array (statique)

Renvoie une liste de chaînes SQL avec les valeurs insérées (à des fins d'affichage uniquement).

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 génère des instructions SQL paramétrées. Utilisé en interne par Applier et DryRun.

generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array (statique)

Renvoie 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 configure des stratégies de fusion au niveau des cellules pour des colonnes ou types de colonnes spécifiques.

auto(): self (statique)

Fabrique pratique qui attribue TextCellMerger aux colonnes TEXT/LONGTEXT et JsonCellMerger aux colonnes JSON.

PHP
use Merql\CellMerge\CellMergeConfig;

$config = CellMergeConfig::auto();

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

Attribue un fusionneur à une colonne spécifique. Utilisez table.column pour les noms qualifiés ou simplement column pour toutes les tables.

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

Attribue un fusionneur à toutes les colonnes correspondant à un modèle de type (correspondance de sous-chaîne sans distinction de casse).

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;

$config = (new CellMergeConfig())
    ->forType('json', new JsonCellMerger());

getMerger(string $table, string $column, string $columnType = ''): CellMerger

Recherche le fusionneur pour une table, une colonne et un type donnés. Priorité : nom de colonne qualifié, nom de colonne non qualifié, modèle de type, valeur par défaut (opaque).


CellMerger (interface)

Merql\CellMerge\CellMerger est l'interface des stratégies de fusion au niveau des cellules.

PHP
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

interface CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult;
}

Trois implémentations intégrées sont fournies.

TextCellMerger

Merql\CellMerge\TextCellMerger fusionne le texte multiligne à l’aide d’une comparaison ligne par ligne à trois voies (le même algorithme que git utilise pour le contenu des fichiers). Lorsque les deux côtés modifient des lignes différentes, la fusion est propre. Lorsqu’ils modifient différemment la même ligne, un conflit est signalé.

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 fusionne les valeurs JSON en comparant indépendamment les clés de premier niveau. Chaque clé est fusionnée selon une logique à trois voies. Les objets imbriqués sont comparés comme des chaînes JSON opaques (sans fusion récursive).

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 est la valeur par défaut. Il traite les valeurs comme des chaînes opaques et signale toujours un conflit lorsqu'elles diffèrent.


CellMergeResult

Merql\CellMerge\CellMergeResult est le type de retour de tous les fusionneurs de cellules.

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 est une représentation en lecture seule de la structure d'une table.

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>

Système de pilotes

merql utilise un système de pilotes extensible pour abstraire les opérations propres à chaque base de données. Les pilotes sont détectés automatiquement à partir de l'identité du pilote de connexion.

Driver (interface)

Merql\Driver\Driver définit le contrat mis en œuvre par tous les pilotes de base de données.

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;
}

Implémentations intégrées : MysqlDriver et SqliteDriver.

DriverFactory

Merql\Driver\DriverFactory détecte automatiquement le pilote à partir d'une connexion.

PHP
use Merql\Driver\DriverFactory;

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

Enregistrez un pilote personnalisé pour d'autres bases de données :

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

Filtres

TableFilter

Merql\Filter\TableFilter inclut ou exclut des tables à l'aide de modèles glob.

PHP
use Merql\Filter\TableFilter;

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

ColumnFilter

Merql\Filter\ColumnFilter retire des instantanés des colonnes spécifiques.

PHP
use Merql\Filter\ColumnFilter;

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

RowFilter

Merql\Filter\RowFilter filtre les lignes à l'aide d'une fonction prédicat.

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;
});

Exceptions

SnapshotException

Merql\Exceptions\SnapshotException est levée lorsqu'un instantané est introuvable ou porte un nom invalide.

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

MergeException

Merql\Exceptions\MergeException est levée lorsqu'un instantané requis pour une fusion est manquant.

PHP
MergeException::snapshotRequired('base');

ConflictException

Merql\Exceptions\ConflictException est levée lorsqu'on tente d'appliquer un résultat de fusion comportant des conflits non résolus.

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

SchemaException

Merql\Exceptions\SchemaException est levée (ou recueillie) lorsque les schémas diffèrent entre les instantanés.

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

Les divergences de schéma n'interrompent pas la fusion. Elles sont recueillies et consultables via $result->schemaMismatches().