Kezdő lépések
Ez az útmutató bemutatja a telepítést, az adatbázishoz való csatlakozást, a pillanatképek rögzítését, az összehasonlítások kiszámítását és a háromutas egyesítés végrehajtását.
Telepítés
composer require merql/merql
A merql használatához PHP 8.2+ szükséges a ext-mbstring és a ext-zlib kiegészítővel (mindkettőt a pitmaster/pitmaster függőség igényli). A használt adapterhez tartozó bővítményre is szükség van: a PDO-adapterhez ext-pdo a ext-pdo_sqlite vagy a ext-pdo_mysql bővítménnyel, a mysqli-adapterhez pedig ext-mysqli kell. A Composer csak javasolja ezeket, ezért ellenőrizze, hogy a szükséges bővítmény telepítve van-e.
A csomag a CLI programot vendor/bin/merql néven telepíti. Használja a merql 0.4.1 verzióját vagy újabbat a CLI eléréséhez; korábbi kiadásokban a bináris nem találta az automatikus betöltőt, ha a merql függőségként volt telepítve.
Kapcsolódás adatbázishoz
A merql egy egyszerű kapcsolatadapter-interfészen keresztül működik. Beépített adaptereket kínál PDO-hoz és mysqli-hez, valamint beépített dialektus-illesztőprogramokat MySQL és SQLite használatához.
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');
A fromDsn() bármely, a PDO által támogatott DSN-t megnyitja, de a merql csak MySQL és SQLite használatához mellékel dialektus-illesztőprogramokat. Más adatbázismotor, például pgsql: esetén a kapcsolat létrejön, de az Merql::init() addig Unsupported database driver: pgsql hibát dob, amíg nem regisztrál hozzá illesztőprogramot. Lásd: Adatbázis-illesztőprogramok.
Pillanatkép rögzítése
A pillanatkép minden tábla minden sorát (vagy a táblák egy részhalmazát) ujjlenyomatként és adatként rögzíti. Az ujjlenyomatok gyors változásészlelést tesznek lehetővé; teljes soradatokra az összehasonlításhoz van szükség.
use Merql\Merql;
Merql::init($connection);
// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');
// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);
A pillanatképek alapértelmezés szerint .merql/snapshots/ alatt, JSON fájlokként maradnak meg. Ezt a helyet módosíthatja:
use Merql\Snapshot\SnapshotStore;
SnapshotStore::setDirectory('/var/data/merql-snapshots');
Összehasonlítás kiszámítása
Az összehasonlítás két pillanatképet vet össze, és beszúrások, frissítések és törlések változáskészletét adja vissza. Mindkét pillanatképnek már léteznie kell; itt a current az adatbázis módosulása után rögzített második pillanatkép.
$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";
}
}
Háromutas egyesítés
A központi művelet. Három elnevezett pillanatképet (alap, saját és másik fél állapota) vesz alapul, majd egyesíti a két módosításkészletet.
Az alap az az állapot, amelyből mindkét példány kiindult. A saját és a másik fél állapota a két példányt mutatja a külön-külön végzett módosítások után, ezért mindegyiket az azt tartalmazó adatbázisból kell rögzíteni. Például, ha egy tesztkörnyezetet egy éles adatbázisból másoltak:
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";
Ne egymás után, ugyanabból az adatbázisból vegye mindhárom pillanatképet. Ekkor a másik fél állapota már tartalmazná a saját módosításait, és minden, általunk beszúrt sor insert_insert ütközésként jelenne meg.
A Merql::merge() oszlopszinten végez egyesítést. Ha TEXT- és JSON-értékeken belül is egyesítést szeretne, használja a ThreeWayMerge-t egy CellMergeConfig-val; lásd: Cellaszintű egyesítés.
Ütközések kezelése
Ha mindkét fél ugyanazt az oszlopot eltérő értékre módosítja, a merql ütközést jelez. Az ütközéseket automatikusan feloldhatja, vagy kézzel megvizsgálhatja.
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";
}
}
Az egyesítés alkalmazása
Ha az egyesítés eredménye tiszta (nincs feloldatlan ütközés), alkalmazza azt a Merql::init() által megadott adatbázisra.
Az egyesítés eredményében szereplő műveletek az alapállapotot alakítják át az egyesített állapottá, és mindkét fél módosításait tartalmazzák. A Merql::apply() mindegyiket végrehajtja, ezért tisztán alkalmazható egy olyan adatbázisra, amely még az alapállapotot tartalmazza. Ha olyan adatbázison alkalmazza, amely már az egyik fél módosításait tartalmazza, az adott fél bármely beszúrása egy már létező sorba ütközik, meghiúsul, és az egész egyesítés visszagördül. Ha az egyik fél módosításait egy olyan adatbázisba szeretné átvinni, amely már a másik fél módosításait tartalmazza (például a tesztkörnyezet módosításait az éles adatbázisba), csak azokat a műveleteket alkalmazza, amelyek még hiányoznak belőle a Feltételes alkalmazás segítségével.
$applied = Merql::apply($result);
echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
foreach ($applied->errors() as $error) {
echo "Error: {$error}\n";
}
}
Az alkalmazó minden műveletet tranzakcióba foglal. Ha bármely utasítás meghiúsul, a teljes egyesítés visszagördül, a hiba pedig a errors() mezőben jelenik meg. Ilyenkor a rowsAffected() továbbra is beleszámítja a hiba előtt lefutott utasításokat, noha azok vissza lettek görgetve (#4); először a hasErrors() értékét ellenőrizze.
Próbaüzem
Tekintse meg az egyesítés által előállított SQL előnézetét végrehajtás nélkül.
use Merql\Apply\DryRun;
$statements = DryRun::generate($result);
foreach ($statements as $sql) {
echo "{$sql};\n";
}
Kétutas javítás
Ha csak egy alapállapot és egy módosításkészlet áll rendelkezésére (nincsenek párhuzamos „saját” módosítások), használja a patch()-t. Ez egy olyan egyesítésnek felel meg, amelyben a saját állapot nem tér el az alaptól.
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);
A CLI használata
Mindezek a műveletek parancssorból is elérhetők. A részletekért lásd a CLI hivatkozást.
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
Háromutas egyesítéshez minden pillanatképet abból az adatbázisból vegyen, amely az adott állapotot tartalmazza, majd végezze el az egyesítést:
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
Táblák és oszlopok szűrése
A pillanatképekből kizárhat táblákat (például gyorsítótár- vagy munkamenettáblákat), és figyelmen kívül hagyhat változó oszlopokat (például a updated_at időbélyegeket).
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']),
]);
Következő lépések
- A teljes parancssori felületért lásd a CLI hivatkozást.
- Az összes osztály és metódus részletes dokumentációjáért lásd a PHP API hivatkozást.