Lewati ke konten

Memulai

Panduan ini menjelaskan instalasi, koneksi ke database, pengambilan snapshot, penghitungan perbedaan, dan pelaksanaan penggabungan tiga arah.

Instalasi

Shell
composer require merql/merql

merql memerlukan PHP 8.2+ dengan ext-mbstring dan ext-zlib (keduanya diwajibkan oleh dependensi pitmaster/pitmaster). Anda juga memerlukan ekstensi untuk adapter yang digunakan: ext-pdo dengan ext-pdo_sqlite atau ext-pdo_mysql untuk adapter PDO, atau ext-mysqli untuk adapter mysqli. Composer hanya menyarankan ekstensi tersebut, jadi pastikan ekstensi yang diperlukan sudah terpasang.

Paket ini memasang CLI sebagai vendor/bin/merql. Gunakan merql 0.4.1 atau versi lebih baru untuk CLI; pada rilis sebelumnya, biner tidak dapat menemukan autoloader ketika merql dipasang sebagai dependensi.

Menghubungkan ke database

merql bekerja melalui antarmuka adapter koneksi yang sederhana. Adapter bawaan tersedia untuk PDO dan mysqli, dengan driver dialek bawaan untuk MySQL dan 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() membuka DSN apa pun yang didukung PDO, tetapi merql hanya menyertakan driver dialek untuk MySQL dan SQLite. Dengan mesin lain seperti pgsql:, koneksi berhasil dibuka tetapi Merql::init() menghasilkan Unsupported database driver: pgsql sampai Anda mendaftarkan driver untuk mesin tersebut. Lihat Driver Database.

Mengambil snapshot

Snapshot mencatat setiap baris dari setiap tabel (atau subset tabel) sebagai sidik jari dan data. Sidik jari memungkinkan deteksi perubahan dengan cepat; data baris lengkap tersedia saat perbedaan diperlukan.

PHP
use Merql\Merql;

Merql::init($connection);

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

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

Secara default, snapshot disimpan sebagai berkas JSON di .merql/snapshots/. Anda dapat mengubah lokasi ini:

PHP
use Merql\Snapshot\SnapshotStore;

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

Menghitung perbedaan

Perbandingan membandingkan dua snapshot dan menghasilkan set perubahan yang terdiri dari insert, update, dan delete. Kedua snapshot harus sudah ada; di sini current adalah snapshot kedua yang diambil setelah database berubah.

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

Penggabungan tiga arah

Operasi inti. Mengambil tiga snapshot bernama (base, ours, theirs) dan menggabungkan kedua set perubahan.

Base adalah keadaan awal kedua salinan. Ours dan theirs adalah dua salinan setelah berubah secara terpisah, jadi masing-masing harus diambil dari database yang memuatnya. Contohnya, ketika sandbox disalin dari database aktif:

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

Jangan mengambil ketiga snapshot secara berurutan dari satu database. Theirs akan sudah memuat perubahan ours, sehingga setiap baris yang disisipkan ours akan muncul sebagai konflik insert_insert.

Merql::merge() menggabungkan pada tingkat kolom. Untuk juga menggabungkan di dalam nilai TEXT dan JSON, gunakan ThreeWayMerge dengan CellMergeConfig; lihat Penggabungan Tingkat Sel.

Menangani konflik

Ketika kedua sisi mengubah kolom yang sama menjadi nilai yang berbeda, merql menimbulkan konflik. Anda dapat menyelesaikan konflik secara otomatis atau memeriksanya secara manual.

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

Menerapkan penggabungan

Setelah Anda mendapatkan hasil penggabungan yang bersih (tanpa konflik yang belum terselesaikan), terapkan hasil tersebut ke database yang diteruskan ke Merql::init().

Operasi dalam hasil penggabungan mengubah base menjadi keadaan gabungan, dan mencakup perubahan dari kedua sisi. Merql::apply() menjalankan semuanya, sehingga hasilnya dapat diterapkan dengan bersih ke database yang masih menyimpan keadaan base. Jika diterapkan ke database yang sudah berisi perubahan dari salah satu sisi, setiap penyisipan dari sisi tersebut akan mengenai baris yang sudah ada, gagal, dan membatalkan seluruh penggabungan. Untuk menerapkan perubahan dari satu sisi ke database yang sudah berisi perubahan dari sisi lain (misalnya, perubahan sandbox ke live), terapkan hanya operasi yang belum ada dengan Terapkan dengan Pengamanan.

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

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

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

Pelaksana membungkus semua operasi dalam sebuah transaksi. Jika ada pernyataan yang gagal, seluruh penggabungan dibatalkan dan kesalahan dikembalikan di errors(). Dalam kasus itu, rowsAffected() tetap menghitung pernyataan yang dijalankan sebelum kegagalan, meskipun semuanya telah dibatalkan (#4); periksa hasErrors() terlebih dahulu.

Uji coba

Pratinjau SQL yang akan dihasilkan oleh penggabungan tanpa menjalankannya.

PHP
use Merql\Apply\DryRun;

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

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

Patch dua arah

Jika Anda hanya memiliki base dan serangkaian perubahan (tanpa modifikasi bersamaan pada "ours"), gunakan patch(). Ini setara dengan penggabungan ketika ours tidak berubah dari base.

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

Menggunakan CLI

Semua operasi ini juga tersedia melalui baris perintah. Lihat CLI Referensi untuk detail lengkap.

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

Untuk penggabungan tiga arah, ambil setiap snapshot dari database yang menyimpan keadaan tersebut, lalu gabungkan:

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

Memfilter tabel dan kolom

Snapshot dapat mengecualikan tabel (seperti tabel cache atau sesi) dan mengabaikan kolom yang berubah-ubah (seperti stempel waktu 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']),
]);

Langkah berikutnya