Lewati ke konten

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.

PHP
$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.

Kode
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.

Kode
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.

Kode
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.

Kode
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:

PHP
$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:

PHP
$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:

PHP
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_delete dengan OursWins menambahkan operasi UPDATE.
  • update_delete dengan TheirsWins menambahkan operasi DELETE.
  • delete_update dengan OursWins menambahkan operasi DELETE.
  • delete_update dengan TheirsWins menambahkan operasi UPDATE.
  • insert_insert menggunakan 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:

PHP
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:

PHP
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.