Hoppa till innehåll

Kom igång

Den här guiden går igenom installation, anslutning till en databas, hur du tar snapshots och beräknar jämförelser samt hur du utför en trevägssammanfogning.

Installation

Shell
composer require merql/merql

merql kräver PHP 8.2+ med ext-mbstring och ext-zlib (båda krävs av beroendet pitmaster/pitmaster). Du behöver också tillägget för adaptern du använder: ext-pdo med ext-pdo_sqlite eller ext-pdo_mysql för PDO-adaptern, eller ext-mysqli för mysqli-adaptern. Composer föreslår bara dessa, så kontrollera att det du behöver är installerat.

Paketet installerar CLI som vendor/bin/merql. Använd merql 0.4.1 eller senare för CLI; i tidigare versioner kunde det körbara programmet inte hitta autoloadern när merql installerades som ett beroende.

Ansluta till en databas

merql fungerar via ett litet gränssnitt för anslutningsadaptrar. Inbyggda adaptrar finns för PDO och mysqli, med inbyggda dialektdrivrutiner för MySQL och SQLite.

PHP
use Merql\Connection;

// SQLite (in-memory or file-based, through PDO).
$connection = Connection::sqlite(':memory:');
$connection = Connection::sqlite('/path/to/database.sqlite');

// MySQL through PDO.
$connection = Connection::mysql('127.0.0.1', 'my_database', 'root', 'password');

// MySQL through mysqli.
$connection = Connection::mysqli('127.0.0.1', 'my_database', 'root', 'password');

// Any MySQL or SQLite PDO DSN.
$connection = Connection::fromDsn('mysql:host=127.0.0.1;dbname=my_database;charset=utf8mb4', 'root', 'password');

fromDsn() öppnar alla DSN som PDO stöder, men merql levereras bara med dialektdrivrutiner för MySQL och SQLite. Med en annan databasmotor, till exempel pgsql:, öppnas anslutningen, men Merql::init() ger Unsupported database driver: pgsql tills du registrerar en drivrutin för den. Se Databasdrivrutiner.

Ta en snapshot

En snapshot registrerar varje rad i varje tabell (eller en delmängd av tabeller) som fingeravtryck och data. Fingeravtryck gör det snabbt att upptäcka ändringar; fullständiga raddata finns tillgängliga när en jämförelse behövs.

PHP
use Merql\Merql;

Merql::init($connection);

// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');

// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);

Snapshots sparas som JSON-filer i .merql/snapshots/ som standard. Du kan ändra den här platsen:

PHP
use Merql\Snapshot\SnapshotStore;

SnapshotStore::setDirectory('/var/data/merql-snapshots');

Beräkna en jämförelse

En jämförelse tar två snapshots och returnerar en ändringsuppsättning med infogningar, uppdateringar och raderingar. Båda snapshots måste redan finnas; här är current en andra snapshot som togs efter att databasen ändrats.

PHP
$changeset = Merql::diff('baseline', 'current');

echo "Inserts: " . count($changeset->inserts()) . "\n";
echo "Updates: " . count($changeset->updates()) . "\n";
echo "Deletes: " . count($changeset->deletes()) . "\n";

// Inspect changes for a single table.
$postChanges = $changeset->forTable('posts');

// Check per-column diffs on updates.
foreach ($changeset->updates() as $update) {
    echo "Row {$update->rowKey} in {$update->table}:\n";
    foreach ($update->columnDiffs as $diff) {
        echo "  {$diff->column}: {$diff->oldValue} -> {$diff->newValue}\n";
    }
}

Trevägssammanfogning

Kärnoperationen. Tar tre namngivna snapshots (base, ours, theirs) och sammanfogar de två ändringsuppsättningarna.

Base är det tillstånd som båda kopiorna utgick från. Ours och theirs är kopiorna efter att de ändrats oberoende av varandra, så var och en måste tas från databasen som innehåller den. Till exempel, om en sandlåda kopierats från en aktiv databas:

PHP
use Merql\Connection;
use Merql\Merql;

$live    = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');

Merql::init($live);
Merql::snapshot('base');      // before the sandbox is copied from live

// ... copy live.sqlite to sandbox.sqlite, then both databases change ...

Merql::init($sandbox);
Merql::snapshot('ours');      // the sandbox with our edits
Merql::init($live);
Merql::snapshot('theirs');    // live with its own changes

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

echo "Operations: {$result->operationCount()}\n";
echo "Conflicts:  {$result->conflictCount()}\n";
echo "Clean:      " . ($result->isClean() ? 'yes' : 'no') . "\n";

Ta inte alla tre snapshots efter varandra från en och samma databas. Då skulle theirs redan innehålla ours ändringar, och varje rad som ours infogade skulle visas som en insert_insert-konflikt.

Merql::merge() sammanfogar på kolumnnivå. Om du även vill sammanfoga inuti värden av typen TEXT och JSON använder du ThreeWayMerge med en CellMergeConfig; se Sammanfogning på cellnivå.

Hantera konflikter

När båda sidor ändrar samma kolumn till olika värden rapporterar merql en konflikt. Du kan lösa konflikter automatiskt eller granska dem manuellt.

PHP
use Merql\Merge\ConflictPolicy;
use Merql\Merge\ConflictResolver;

if (!$result->isClean()) {
    // Auto-resolve: one side wins.
    $resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);

    // Or inspect each conflict.
    foreach ($result->conflicts() as $conflict) {
        echo "Conflict on {$conflict->table()} row {$conflict->rowKey()}:\n";
        echo "  Type:   {$conflict->type()}\n";
        echo "  Column: {$conflict->column()}\n";
        echo "  Ours:   " . var_export($conflict->oursValue(), true) . "\n";
        echo "  Theirs: " . var_export($conflict->theirsValue(), true) . "\n";
    }
}

Tillämpa sammanfogningen

När du har ett rent sammanfogningsresultat (utan olösta konflikter) tillämpar du det på databasen som skickas till Merql::init().

Åtgärderna i ett sammanfogningsresultat omvandlar base till det sammanfogade tillståndet och omfattar ändringar från båda sidor. Merql::apply() kör varenda åtgärd, så sammanfogningen tillämpas utan problem på en databas som fortfarande innehåller basens tillstånd. Om den tillämpas på en databas som redan innehåller ändringar från ena sidan misslyckas en infogning från den sidan eftersom raden redan finns, och hela sammanfogningen återställs. Om du vill föra över den ena sidans ändringar till en databas som redan innehåller den andra sidans ändringar (till exempel sandlådeändringar till den aktiva databasen) tillämpar du bara de åtgärder som saknas med Skyddad tillämpning.

PHP
$applied = Merql::apply($result);

echo "Rows affected: {$applied->rowsAffected()}\n";

if ($applied->hasErrors()) {
    foreach ($applied->errors() as $error) {
        echo "Error: {$error}\n";
    }
}

Tillämpningsfunktionen kapslar in alla åtgärder i en transaktion. Om någon sats misslyckas återställs hela sammanfogningen och felet returneras i errors(). I så fall räknar rowsAffected() fortfarande satserna som kördes före felet, trots att de återställdes (#4); kontrollera hasErrors() först.

Torrkörning

Förhandsgranska de SQL som en sammanfogning skulle generera utan att köra dem.

PHP
use Merql\Apply\DryRun;

$statements = DryRun::generate($result);

foreach ($statements as $sql) {
    echo "{$sql};\n";
}

Tvåvägspatch

Om du bara har en bas och en uppsättning ändringar (inga samtidiga ändringar i "ours") använder du patch(). Det motsvarar en sammanfogning där ours är oförändrad från base.

PHP
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);

Använda CLI

Alla dessa åtgärder finns också tillgängliga via kommandoraden. Se CLI-referensen för fullständig information.

Shell
export MERQL_DB_DSN="sqlite:mydb.sqlite"

vendor/bin/merql snapshot base
# ... change the database ...
vendor/bin/merql snapshot current
vendor/bin/merql diff base current

Vid en trevägssammanfogning tar du varje snapshot från databasen som innehåller det tillståndet och sammanfogar sedan:

Shell
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base
MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs
vendor/bin/merql merge base ours theirs --dry-run

Filtrera tabeller och kolumner

Snapshots kan utesluta tabeller (som cache- eller sessionstabeller) och ignorera instabila kolumner (som tidsstämplar av typen updated_at).

PHP
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Snapshot\Snapshotter;

$snapshotter = new Snapshotter($connection);

$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['sessions', 'cache_*']),
    ColumnFilter::ignore(['updated_at', 'modified_date']),
]);

Nästa steg