Začetek
Ta vodnik opisuje namestitev, povezavo s podatkovno zbirko, zajemanje posnetkov, izračun razlik in izvedbo tristranske združitve.
Namestitev
composer require merql/merql
merql zahteva PHP 8.2+ z ext-mbstring in ext-zlib (oboje zahteva odvisnost pitmaster/pitmaster). Potrebujete tudi razširitev za povezovalnik, ki ga uporabljate: ext-pdo z ext-pdo_sqlite ali ext-pdo_mysql za povezovalnik PDO oziroma ext-mysqli za povezovalnik mysqli. Composer te razširitve samo predlaga, zato preverite, ali je zahtevana nameščena.
Paket namesti CLI kot vendor/bin/merql. Za CLI uporabite merql 0.4.1 ali novejšo različico; v starejših izdajah binarna datoteka pri namestitvi merql kot odvisnosti ni mogla najti samodejnega nalagalnika.
Povezovanje s podatkovno zbirko
merql uporablja majhen vmesnik povezovalnika. Vgrajena povezovalnika sta na voljo za PDO in mysqli, vgrajena gonilnika za narečja pa za MySQL in 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() odpre kateri koli DSN, ki ga podpira PDO, vendar merql vključuje gonilnika za narečji samo za MySQL in SQLite. Pri uporabi drugega pogona, na primer pgsql:, se povezava vzpostavi, vendar Merql::init() sproži Unsupported database driver: pgsql, dokler zanj ne registrirate gonilnika. Glejte Gonilniki za podatkovne zbirke.
Zajemanje posnetka
Posnetek zabeleži vse vrstice vseh tabel (ali njihovo podmnožico) kot prstne odtise in podatke. Prstni odtisi omogočajo hitro zaznavanje sprememb, celotni podatki vrstic pa so na voljo, ko potrebujete razliko.
use Merql\Merql;
Merql::init($connection);
// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');
// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);
Posnetki se privzeto shranijo kot datoteke JSON v .merql/snapshots/. To lokacijo lahko spremenite:
use Merql\Snapshot\SnapshotStore;
SnapshotStore::setDirectory('/var/data/merql-snapshots');
Izračun razlike
Razlika primerja dva posnetka in vrne nabor sprememb z vstavljanji, posodobitvami in brisanji. Oba posnetka morata že obstajati; tukaj je current drugi posnetek, zajet po spremembi podatkovne zbirke.
$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";
}
}
Tristranska združitev
Osrednja operacija. Vzame tri poimenovane posnetke (osnova, naše, njihovo) in združi oba nabora sprememb.
Osnova je stanje, iz katerega sta izšli obe kopiji. Naša in njihova kopija sta stanji po neodvisnih spremembah, zato je treba vsako zajeti iz podatkovne zbirke, v kateri se nahaja. Primer: peskovnik je bil kopiran iz aktivne podatkovne zbirke:
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";
Vseh treh posnetkov ne zajemajte zaporedoma iz ene podatkovne zbirke. Tako bi »njihovo« že vsebovalo spremembe »našega«, vsaka vrstica, ki jo je vstavil »naš«, pa bi bila prikazana kot konflikt insert_insert.
Merql::merge() združuje na ravni stolpcev. Če želite združevati tudi znotraj vrednosti TEXT in JSON, uporabite ThreeWayMerge z CellMergeConfig; glejte Združevanje na ravni celic.
Obravnava konfliktov
Če obe strani spremenita isti stolpec v različni vrednosti, merql javi konflikt. Konflikte lahko razrešite samodejno ali jih pregledate ročno.
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";
}
}
Uporaba združitve
Ko je rezultat združitve čist (brez nerazrešenih konfliktov), ga uporabite v podatkovni zbirki, posredovani ukazu Merql::init().
Operacije v rezultatu združitve pretvorijo osnovno stanje v združeno ter vključujejo spremembe obeh strani. Merql::apply() izvede vsako od njih, zato se brez težav uporabi v podatkovni zbirki, ki še vedno vsebuje osnovno stanje. Če jo uporabite v podatkovni zbirki, ki že vsebuje spremembe ene strani, vstavljanje zadene že obstoječo vrstico, spodleti in povrne celotno združitev. Če želite uveljaviti spremembe ene strani v podatkovni zbirki, ki že vsebuje spremembe druge strani (na primer spremembe v peskovniku prenesti v aktivno zbirko), uporabite samo manjkajoče operacije z ukazom Zaščitena uporaba.
$applied = Merql::apply($result);
echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
foreach ($applied->errors() as $error) {
echo "Error: {$error}\n";
}
}
Izvajalnik ovije vse operacije v transakcijo. Če kateri koli stavek spodleti, se celotna združitev povrne, napaka pa se vrne v errors(). V tem primeru rowsAffected() še vedno šteje stavke, izvedene pred napako, čeprav so bili povrnjeni (#4); najprej preverite hasErrors().
Suhi zagon
Oglejte si SQL, ki bi jih ustvarila združitev, ne da bi jih izvedli.
use Merql\Apply\DryRun;
$statements = DryRun::generate($result);
foreach ($statements as $sql) {
echo "{$sql};\n";
}
Dvosmerni popravek
Če imate samo osnovo in nabor sprememb (brez sočasnih sprememb »našega«), uporabite patch(). To ustreza združitvi, pri kateri se »naše« od osnove ni spremenilo.
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);
Uporaba CLI
Vse te operacije so na voljo tudi v ukazni vrstici. Za vse podrobnosti glejte ReferencoCLI.
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 tristranski združitvi zajemite vsak posnetek iz podatkovne zbirke, ki vsebuje ustrezno stanje, nato pa izvedite združitev:
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
Filtriranje tabel in stolpcev
Posnetki lahko izključijo tabele (kot so tabele predpomnilnika ali sej) in prezrejo spremenljive stolpce (kot so časovni žigi 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']),
]);
Naslednji koraki
- CLI Referenca za celoten vmesnik ukazne vrstice.
- PHP API Referenca za podrobno dokumentacijo vseh razredov in metod.