Gå til indhold

Kom godt i gang

Denne vejledning gennemgår installation, tilslutning til en database, oprettelse af øjebliksbilleder, beregning af diff og udførelse af en trevejsfletning.

Installation

Shell
composer require merql/merql

merql kræver PHP 8.2+ med ext-mbstring og ext-zlib (begge kræves af afhængigheden pitmaster/pitmaster). Du skal også bruge udvidelsen til den adapter, du bruger: ext-pdo med ext-pdo_sqlite eller ext-pdo_mysql til PDO-adapteren eller ext-mysqli til mysqli-adapteren. Composer foreslår kun disse, så kontrollér, at den nødvendige udvidelse er installeret.

Pakken installerer CLI som vendor/bin/merql. Brug merql 0.4.1 eller nyere til CLI; i tidligere versioner kunne programfilen ikke finde autoloaderen, når merql var installeret som afhængighed.

Opret forbindelse til en database

merql fungerer via et lille interface til forbindelsesadaptere. Der medfølger indbyggede adaptere til PDO og mysqli samt indbyggede dialektdrivere til MySQL og 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() åbner alle DSN'er, som PDO understøtter, men merql leveres kun med dialektdrivere til MySQL og SQLite. Med en anden databasemotor, f.eks. pgsql:, oprettes forbindelsen, men Merql::init() udløser Unsupported database driver: pgsql, indtil du registrerer en driver til den. Se Databasedrivere.

Oprettelse af et øjebliksbillede

Et øjebliksbillede registrerer hver række i hver tabel (eller et udvalg af tabeller) som fingeraftryk og data. Fingeraftryk muliggør hurtig registrering af ændringer; alle rækkedata er tilgængelige, når der er brug for en diff.

PHP
use Merql\Merql;

Merql::init($connection);

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

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

Som standard gemmes øjebliksbilleder som JSON-filer i .merql/snapshots/. Du kan ændre denne placering:

PHP
use Merql\Snapshot\SnapshotStore;

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

Beregning af en diff

En diff sammenligner to øjebliksbilleder og returnerer et ændringssæt med indsættelser, opdateringer og sletninger. Begge øjebliksbilleder skal allerede findes; her er current et andet øjebliksbillede, der er taget, efter at databasen blev ændret.

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

Trevejsfletning

Den centrale handling. Tager tre navngivne øjebliksbilleder (base, ours, theirs) og fletter de to sæt ændringer.

Base er den tilstand, som begge kopier startede fra. Ours og theirs er de to kopier efter uafhængige ændringer, så hver skal hentes fra den database, der indeholder den. Eksempel: en sandkasse blev kopieret fra en live-database:

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

Tag ikke alle tre øjebliksbilleder fra én database i rækkefølge. Theirs ville så allerede indeholde ændringerne fra ours, og hver række, som ours indsatte, ville blive vist som en insert_insert-konflikt.

Merql::merge() fletter på kolonneniveau. Hvis du også vil flette inde i værdier af typen TEXT og JSON, skal du bruge ThreeWayMerge med en CellMergeConfig; se Fletning på celleniveau.

Håndtering af konflikter

Når begge sider ændrer samme kolonne til forskellige værdier, opstår der en konflikt i merql. Du kan løse konflikter automatisk eller undersøge dem manuelt.

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

Anvendelse af fletningen

Når du har et rent fletningsresultat (uden uløste konflikter), skal du anvende det på databasen, der er angivet til Merql::init().

Handlingerne i et fletningsresultat omdanner base til den flettede tilstand og omfatter ændringerne fra begge sider. Merql::apply() udfører dem alle, så resultatet kan anvendes direkte på en database, der stadig er i grundtilstanden. Hvis det anvendes på en database, der allerede indeholder ændringer fra den ene side, vil en indsættelse fra den side ramme en eksisterende række, fejle og rulle hele fletningen tilbage. Hvis du vil overføre ændringer fra den ene side til en database, der allerede indeholder ændringerne fra den anden (f.eks. sandkasseændringer til live), skal du kun anvende de handlinger, den mangler, med Beskyttet anvendelse.

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

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

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

Anvenderen indkapsler alle handlinger i en transaktion. Hvis en sætning fejler, rulles hele fletningen tilbage, og fejlen returneres i errors(). I så fald tæller rowsAffected() stadig de sætninger, der blev kørt før fejlen, selvom de blev rullet tilbage (#4); kontrollér hasErrors() først.

Prøvekørsel

Få vist de SQL, som en fletning ville generere, uden at udføre dem.

PHP
use Merql\Apply\DryRun;

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

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

Tovejspatch

Når du kun har en base og et sæt ændringer (uden samtidige ændringer fra "ours"), skal du bruge patch(). Det svarer til en fletning, hvor ours er uændret i forhold til base.

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

Brug af CLI

Alle disse handlinger er også tilgængelige fra kommandolinjen. Se CLI-reference for alle detaljer.

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

Ved en trevejsfletning skal du hente hvert øjebliksbillede fra databasen, der indeholder den pågældende tilstand, og derefter flette:

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

Filtrering af tabeller og kolonner

Øjebliksbilleder kan udelade tabeller (f.eks. cache- eller sessionstabeller) og ignorere flygtige kolonner (f.eks. tidsstempler af 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æste trin