Začíname
Táto príručka vás prevedie inštaláciou, pripojením k databáze, zachytávaním snímok, výpočtom rozdielov a vykonaním trojstranného zlúčenia.
Inštalácia
composer require merql/merql
merql vyžaduje PHP 8.2+ s ext-mbstring a ext-zlib (obe vyžaduje závislosť pitmaster/pitmaster). Potrebujete aj rozšírenie pre použitý ovládač: ext-pdo s ext-pdo_sqlite alebo ext-pdo_mysql pre ovládač PDO, prípadne ext-mysqli pre ovládač mysqli. Composer tieto rozšírenia iba odporúča, preto skontrolujte, či je potrebné rozšírenie nainštalované.
Balík nainštaluje CLI ako vendor/bin/merql. Pre CLI použite merql 0.4.1 alebo novšiu verziu; v starších vydaniach binárny súbor nedokázal nájsť zavádzač, keď bol merql nainštalovaný ako závislosť.
Pripojenie k databáze
merql používa jednoduché rozhranie adaptéra pripojenia. Obsahuje vstavané adaptéry pre PDO a mysqli a vstavané ovládače dialektov pre MySQL a SQLite.
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() otvára každý DSN podporovaný PDO, ale merql obsahuje ovládače dialektov iba pre MySQL a SQLite. Pri použití iného databázového systému, napríklad pgsql:, sa pripojenie otvorí, no Merql::init() vyvolá Unsupported database driver: pgsql, kým preň nezaregistrujete ovládač. Pozrite si Ovládače databáz.
Zachytenie snímky
Snímka zaznamenáva každý riadok každej tabuľky (alebo podmnožiny tabuliek) vo forme odtlačkov a údajov. Odtlačky umožňujú rýchlo zisťovať zmeny; úplné údaje riadkov sú k dispozícii pri porovnávaní.
use Merql\Merql;
Merql::init($connection);
// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');
// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);
Snímky sa predvolene ukladajú ako súbory JSON v adresári .merql/snapshots/. Toto umiestnenie môžete zmeniť:
use Merql\Snapshot\SnapshotStore;
SnapshotStore::setDirectory('/var/data/merql-snapshots');
Výpočet rozdielu
Porovnanie dvoch snímok vráti súbor zmien obsahujúci vloženia, aktualizácie a odstránenia. Obe snímky už musia existovať; tu je current druhá snímka zachytená po zmene databázy.
$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";
}
}
Trojstranné zlúčenie
Základná operácia. Prevezme tri pomenované snímky (base, ours, theirs) a zlúči dve množiny zmien.
Base je stav, z ktorého vychádzali obe kópie. Ours a theirs sú dve kópie po tom, ako sa nezávisle zmenili, preto treba každú zachytiť z databázy, v ktorej sa nachádza. Napríklad keď bola testovacia kópia vytvorená z produkčnej databázy:
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";
Nevytvárajte všetky tri snímky postupne z jednej databázy. Theirs by už obsahoval zmeny ours a každý riadok vložený v ours by sa zobrazil ako konflikt insert_insert.
Merql::merge() vykonáva zlúčenie na úrovni stĺpcov. Ak chcete zlúčiť aj obsah hodnôt TEXT a JSON, použite ThreeWayMerge s CellMergeConfig; pozrite si Zlučovanie na úrovni buniek.
Riešenie konfliktov
Keď obe strany zmenia ten istý stĺpec na rôzne hodnoty, merql nahlási konflikt. Konflikty môžete vyriešiť automaticky alebo ich preskúmať ručne.
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";
}
}
Použitie zlúčenia
Keď máte čistý výsledok zlúčenia (bez nevyriešených konfliktov), použite ho v databáze zadanej cez Merql::init().
Operácie vo výsledku zlúčenia prevedú base na zlúčený stav a zahŕňajú zmeny oboch strán. Merql::apply() vykoná každú z nich, preto sa dajú bez problémov použiť v databáze, ktorá stále obsahuje stav base. Ak ich použijete v databáze, ktorá už obsahuje zmeny jednej strany, každý jej vložený riadok narazí na už existujúci riadok, operácia zlyhá a celé zlúčenie sa vráti späť. Ak chcete preniesť zmeny jednej strany do databázy, ktorá už obsahuje zmeny druhej strany (napríklad úpravy v testovacom prostredí do produkcie), použite iba operácie, ktoré jej chýbajú, pomocou Kontrolované použitie.
$applied = Merql::apply($result);
echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
foreach ($applied->errors() as $error) {
echo "Error: {$error}\n";
}
}
Aplikátor zabalí všetky operácie do transakcie. Ak ktorýkoľvek príkaz zlyhá, celé zlúčenie sa vráti späť a chyba sa vráti v errors(). V takom prípade rowsAffected() stále počíta príkazy vykonané pred zlyhaním, hoci boli vrátené späť (#4); najprv skontrolujte hasErrors().
Skúšobné spustenie
Zobrazte náhľad SQL, ktoré by zlúčenie vytvorilo, bez jeho vykonania.
use Merql\Apply\DryRun;
$statements = DryRun::generate($result);
foreach ($statements as $sql) {
echo "{$sql};\n";
}
Dvojstranná záplata
Ak máte iba base a množinu zmien (bez súbežných úprav v „ours“), použite patch(). To zodpovedá zlúčeniu, pri ktorom sa „ours“ nezmenilo oproti base.
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);
Používanie CLI
Všetky tieto operácie sú dostupné aj z príkazového riadka. Úplné podrobnosti nájdete v CLI Referencia.
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
Pri trojstrannom zlúčení vytvorte každú snímku z databázy, ktorá obsahuje príslušný stav, a potom ich zlúčte:
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
Filtrovanie tabuliek a stĺpcov
Zo snímok možno vylúčiť tabuľky (napríklad tabuľky vyrovnávacej pamäte alebo relácií) a ignorovať nestále stĺpce (napríklad časové značky updated_at).
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']),
]);
Ďalšie kroky
- CLI Referencia pre úplné rozhranie príkazového riadka.
- PHP API Referencia pre podrobnú dokumentáciu každej triedy a metódy.