Başlarken
Bu kılavuzda kurulum, veritabanına bağlanma, anlık görüntü alma, farkları hesaplama ve üç yönlü birleştirme adımları anlatılır.
Kurulum
composer require merql/merql
merql için PHP 8.2+ ile ext-mbstring ve ext-zlib gerekir (ikisi de pitmaster/pitmaster bağımlılığı tarafından zorunlu tutulur). Kullandığınız adaptörün uzantısına da ihtiyacınız vardır: PDO adaptörü için ext-pdo_sqlite ile birlikte ext-pdo veya ext-pdo_mysql; mysqli adaptörü içinse ext-mysqli gerekir. Composer yalnızca bunları önerir; bu nedenle ihtiyacınız olanın kurulu olduğunu kontrol edin.
Paket, CLI öğesini vendor/bin/merql olarak kurar. CLI için merql 0.4.1 veya sonraki sürümünü kullanın; önceki sürümlerde merql bağımlılık olarak kurulduğunda ikili dosya yükleyiciyi bulamıyordu.
Veritabanına bağlanma
merql, küçük bir bağlantı adaptörü arayüzü üzerinden çalışır. Yerleşik adaptörler PDO ve mysqli için, yerleşik lehçe sürücüleri ise MySQL ve SQLite için sağlanır.
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(), PDO'nun desteklediği her DSN'yi açabilir; ancak merql yalnızca MySQL ve SQLite için lehçe sürücüleriyle birlikte gelir. pgsql: gibi başka bir altyapıda bağlantı açılır, ancak onun için bir sürücü kaydedene kadar Merql::init(), Unsupported database driver: pgsql hatasını verir. Bkz. Veritabanı Sürücüleri.
Anlık görüntü alma
Bir anlık görüntü, her tablonun her satırını (veya tabloların bir alt kümesini) parmak izleri ve veriler olarak kaydeder. Parmak izleri değişikliklerin hızla algılanmasını sağlar; fark gerektiğinde tam satır verileri kullanılabilir.
use Merql\Merql;
Merql::init($connection);
// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');
// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);
Anlık görüntüler varsayılan olarak .merql/snapshots/ içinde JSON dosyaları şeklinde saklanır. Bu konumu değiştirebilirsiniz:
use Merql\Snapshot\SnapshotStore;
SnapshotStore::setDirectory('/var/data/merql-snapshots');
Fark hesaplama
Bir fark, iki anlık görüntüyü karşılaştırır ve ekleme, güncelleme ve silmelerden oluşan bir değişiklik kümesi döndürür. Her iki anlık görüntü de önceden alınmış olmalıdır; burada current, veritabanı değiştikten sonra alınan ikinci anlık görüntüdür.
$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";
}
}
Üç yönlü birleştirme
Temel işlem budur. Adlandırılmış üç anlık görüntüyü (base, ours, theirs) alır ve iki değişiklik kümesini birleştirir.
Base, iki kopyanın da başladığı durumdur. Ours ve theirs, bağımsız olarak değiştirildikten sonraki iki kopyadır; bu nedenle her biri bulunduğu veritabanından alınmalıdır. Örneğin, bir korumalı alan canlı veritabanından kopyalandığında:
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";
Üç anlık görüntünün tamamını tek bir veritabanından sırayla almayın. Theirs, ours'un değişikliklerini zaten içerir ve ours'un eklediği her satır insert_insert çakışması olarak görünür.
Merql::merge() sütun düzeyinde birleştirme yapar. TEXT ve JSON değerlerinin içini de birleştirmek için ThreeWayMerge öğesini bir CellMergeConfig ile kullanın; bkz. Hücre Düzeyinde Birleştirme.
Çakışmaları ele alma
Her iki taraf da aynı sütunu farklı değerlere değiştirdiğinde merql bir çakışma bildirir. Çakışmaları otomatik olarak çözebilir veya elle inceleyebilirsiniz.
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";
}
}
Birleştirmeyi uygulama
Temiz bir birleştirme sonucu (çözülmemiş çakışma yok) elde ettiğinizde, bunu Merql::init() öğesine geçirilen veritabanına uygulayın.
Birleştirme sonucundaki işlemler, base durumunu birleştirilmiş duruma dönüştürür ve her iki tarafın değişikliklerini de içerir. Merql::apply() bunların her birini yürütür; bu nedenle base durumunu hâlâ koruyan bir veritabanına sorunsuz uygulanır. Bir tarafın değişikliklerini zaten içeren bir veritabanına uygulandığında, o tarafın eklemeleri mevcut bir satırla çakışır, işlem başarısız olur ve birleştirmenin tamamı geri alınır. Bir tarafın değişikliklerini diğer tarafın bulunduğu veritabanına (örneğin, korumalı alan düzenlemelerini canlı veritabanına) aktarmak için yalnızca eksik işlemleri Korumalı Uygulama ile uygulayın.
$applied = Merql::apply($result);
echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
foreach ($applied->errors() as $error) {
echo "Error: {$error}\n";
}
}
Uygulayıcı tüm işlemleri bir işlem içine alır. Herhangi bir ifade başarısız olursa birleştirmenin tamamı geri alınır ve hata errors() içinde döndürülür. Bu durumda rowsAffected() başarısızlıktan önce çalıştırılan ifadeleri, geri alınmış olsalar bile saymaya devam eder (#4); önce hasErrors() öğesini kontrol edin.
Deneme çalıştırması
Birleştirmenin oluşturacağı SQL öğesini çalıştırmadan önizleyin.
use Merql\Apply\DryRun;
$statements = DryRun::generate($result);
foreach ($statements as $sql) {
echo "{$sql};\n";
}
İki yönlü yama
Yalnızca bir base ve bir dizi değişikliğiniz varsa (eşzamanlı "ours" değişiklikleri yoksa) patch() kullanın. Bu, ours'un base'den değişmediği birleştirmeyle eşdeğerdir.
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);
CLI kullanımı
Bu işlemlerin tümü komut satırından da kullanılabilir. Tüm ayrıntılar için CLI Başvurusu bölümüne bakın.
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
Üç yönlü birleştirme için her anlık görüntüyü ilgili durumu barındıran veritabanından alın, ardından birleştirin:
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
Tabloları ve sütunları filtreleme
Anlık görüntülerden tabloları (önbellek veya oturum tabloları gibi) çıkarabilir ve değişken sütunları (updated_at zaman damgaları gibi) yok sayabilirsiniz.
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']),
]);
Sonraki adımlar
- Komut satırı arayüzünün tamamı için CLI Başvurusu bölümüne bakın.
- Her sınıf ve yöntemin ayrıntılı belgeleri için PHP API Başvurusu bölümüne bakın.