Penanganan Konflik
Konflik terjadi ketika kedua sisi dalam penggabungan mengubah data yang sama dengan cara yang tidak kompatibel. merql mendeteksi empat jenis konflik dan menyediakan strategi penyelesaian otomatis dan manual.
$result = $merge->merge($base, $ours, $theirs);
if (!$result->isClean()) {
foreach ($result->conflicts() as $conflict) {
echo "{$conflict->table()}.{$conflict->column()}: "
. "{$conflict->oursValue()} vs {$conflict->theirsValue()}\n";
}
}
Jenis konflik
update_update
Kedua sisi mengubah kolom yang sama pada baris yang sama menjadi nilai yang berbeda. Ini adalah jenis konflik yang paling umum.
Base: posts #42 title = "Hello"
Ours: posts #42 title = "Welcome"
Theirs: posts #42 title = "Greetings"
Konflik melaporkan: tabel posts, kunci baris 42, kolom title, nilai ours Welcome, nilai theirs Greetings, nilai base Hello.
update_delete
Satu sisi memperbarui baris, sedangkan sisi lainnya menghapusnya. merql tidak dapat menentukan apakah pembaruan atau penghapusan yang harus dipertahankan.
Ours: posts #42 title = "Updated Title" (updated)
Theirs: posts #42 (deleted)
Konflik melaporkan: tabel posts, kunci baris 42, jenis update_delete. Nilai ours berisi seluruh baris yang diperbarui. Nilai theirs adalah null.
delete_update
Kebalikan dari update_delete. Theirs memperbarui baris, ours menghapusnya.
Ours: posts #42 (deleted)
Theirs: posts #42 title = "Updated Title" (updated)
Konflik melaporkan: jenis delete_update. Nilai ours adalah null, sedangkan nilai theirs berisi seluruh baris yang diperbarui.
insert_insert
Kedua sisi menyisipkan baris dengan kunci utama yang sama. Karena kedua baris baru (tidak ada di base), merql tidak dapat menggabungkannya.
Base: (no row #99)
Ours: posts #99 { title: "My Post" }
Theirs: posts #99 { title: "Their Post" }
Konflik melaporkan: jenis insert_insert. Nilai ours berisi data baris ours, sedangkan nilai theirs berisi data baris theirs.
Kelas Conflict
Setiap konflik adalah objek readonly dengan accessor berikut:
$conflict->table(); // string: table name
$conflict->rowKey(); // string: encoded row identity key
$conflict->type(); // string: "update_update"|"update_delete"|"delete_update"|"insert_insert"
$conflict->column(); // ?string: column name (null for structural conflicts)
$conflict->oursValue(); // mixed: our value (null for delete side, full row for structural)
$conflict->theirsValue(); // mixed: their value (null for delete side, full row for structural)
$conflict->baseValue(); // mixed: base value (null for inserts)
Metode column() mengembalikan nama kolom untuk konflik update_update dan null untuk konflik struktural (update_delete, delete_update, insert_insert).
Mendekode kunci utama
Kunci baris adalah string yang dikodekan. Untuk mendapatkan larik kunci utama bernama, teruskan kolom identitas:
$pk = $conflict->primaryKey(['id']);
// ['id' => '42']
$pk = $conflict->primaryKey(['tenant_id', 'user_id']);
// ['tenant_id' => '1', 'user_id' => '99']
Resolusi otomatis dengan ConflictPolicy
ConflictResolver menerapkan kebijakan untuk menyelesaikan semua konflik sekaligus:
use Merql\Merge\ConflictPolicy;
use Merql\Merge\ConflictResolver;
// One side wins everywhere.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::OursWins);
$resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);
// Manual: return the result unchanged (inspect conflicts yourself).
$resolved = ConflictResolver::resolve($result, ConflictPolicy::Manual);
Enum ConflictPolicy
| Nilai | Perilaku |
|---|---|
OursWins |
Gunakan nilai ours untuk update_update. Pertahankan update untuk update_delete. Pertahankan delete untuk delete_update. Gunakan sisipan ours untuk insert_insert. |
TheirsWins |
Gunakan nilai theirs untuk update_update. Hapus untuk update_delete. Pertahankan update untuk delete_update. Gunakan sisipan theirs untuk insert_insert. |
Manual |
Kembalikan hasil tanpa perubahan. Semua konflik tetap belum terselesaikan. |
Cara kerja resolusi
Untuk konflik update_update, resolver menambal operasi merge yang ada untuk baris tersebut, dengan mengganti nilai kolom yang berkonflik dengan nilai dari sisi pemenang.
Untuk konflik struktural (update_delete, delete_update, insert_insert), resolver menambahkan operasi baru ke hasil:
update_deletedenganOursWinsmenambahkan operasi UPDATE.update_deletedenganTheirsWinsmenambahkan operasi DELETE.delete_updatedenganOursWinsmenambahkan operasi DELETE.delete_updatedenganTheirsWinsmenambahkan operasi UPDATE.insert_insertmenggunakan nilai baris dari sisi pemenang.
MergeResult yang telah diselesaikan memiliki daftar konflik kosong dan dapat langsung diterapkan.
Penyelesaian manual
Untuk kendali yang lebih terperinci, periksa setiap konflik dan buat penyelesaian Anda sendiri:
foreach ($result->conflicts() as $conflict) {
echo "Table: {$conflict->table()}\n";
echo "Row: {$conflict->rowKey()}\n";
echo "Type: {$conflict->type()}\n";
if ($conflict->column() !== null) {
echo "Column: {$conflict->column()}\n";
echo "Base: " . var_export($conflict->baseValue(), true) . "\n";
echo "Ours: " . var_export($conflict->oursValue(), true) . "\n";
echo "Theirs: " . var_export($conflict->theirsValue(), true) . "\n";
}
}
Menerapkan dengan konflik yang belum diselesaikan
Applier menolak menerapkan hasil merge yang masih memiliki konflik yang belum diselesaikan. Alat ini menampilkan ConflictException:
use Merql\Exceptions\ConflictException;
try {
$applier->apply($result);
} catch (ConflictException $e) {
echo $e->getMessage();
// "Cannot apply merge with 3 unresolved conflicts"
}
Selalu selesaikan konflik sebelum menerapkan, baik dengan ConflictResolver::resolve() maupun dengan membuat MergeResult yang bersih secara manual.