Konflikthantering
En konflikt uppstår när båda sidor av en sammanslagning ändrar samma data på sätt som inte går ihop. merql upptäcker fyra typer av konflikter och erbjuder automatiska och manuella lösningsstrategier.
$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";
}
}
Konflikttyper
update_update
Båda sidorna ändrade samma kolumn i samma rad till olika värden. Detta är den vanligaste konflikttypen.
Base: posts #42 title = "Hello"
Ours: posts #42 title = "Welcome"
Theirs: posts #42 title = "Greetings"
Konflikten rapporterar: tabell posts, radnyckel 42, kolumn title, värde på vår sida Welcome, värde på deras sida Greetings, basvärde Hello.
update_delete
Den ena sidan uppdaterade en rad, den andra tog bort den. merql kan inte avgöra om uppdateringen eller borttagningen ska gälla.
Ours: posts #42 title = "Updated Title" (updated)
Theirs: posts #42 (deleted)
Konflikten rapporterar: tabell posts, radnyckel 42, typ update_delete. Värdet på vår sida innehåller den fullständigt uppdaterade raden. Värdet på deras sida är null.
delete_update
Motsatsen till update_delete. De uppdaterade raden, vi tog bort den.
Ours: posts #42 (deleted)
Theirs: posts #42 title = "Updated Title" (updated)
Konflikten rapporterar: typ delete_update. Värdet på vår sida är null, och värdet på deras sida innehåller den fullständigt uppdaterade raden.
insert_insert
Båda sidorna infogade en rad med samma primärnyckel. Eftersom båda raderna är nya (fanns inte i basen) kan merql inte slå samman dem.
Base: (no row #99)
Ours: posts #99 { title: "My Post" }
Theirs: posts #99 { title: "Their Post" }
Konflikten rapporterar: typ insert_insert. Värdet på vår sida innehåller våra raddata, och värdet på deras sida innehåller deras raddata.
Klassen Conflict
Varje konflikt är ett skrivskyddat objekt med följande åtkomstmetoder:
$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)
Metoden column() returnerar ett kolumnnamn för konflikter av typen update_update och null för strukturella konflikter (update_delete, delete_update, insert_insert).
Avkoda primärnyckeln
Radnyckeln är en kodad sträng. Om du vill få en namngiven primärnyckelarray skickar du med identitetskolumnerna:
$pk = $conflict->primaryKey(['id']);
// ['id' => '42']
$pk = $conflict->primaryKey(['tenant_id', 'user_id']);
// ['tenant_id' => '1', 'user_id' => '99']
Automatisk lösning med ConflictPolicy
ConflictResolver tillämpar en princip för att lösa alla konflikter på en gång:
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);
Uppräkningen ConflictPolicy
| Värde | Beteende |
|---|---|
OursWins |
Använd vårt värde vid update_update. Behåll uppdateringen vid update_delete. Behåll borttagningen vid delete_update. Använd vår infogning vid insert_insert. |
TheirsWins |
Använd deras värde vid update_update. Ta bort vid update_delete. Behåll uppdateringen vid delete_update. Använd deras infogning vid insert_insert. |
Manual |
Returnera resultatet oförändrat. Alla konflikter förblir olösta. |
Så fungerar lösningen
Vid konflikter av typen update_update uppdaterar lösaren den befintliga sammanslagningsåtgärden för raden och ersätter det motstridiga kolumnvärdet med det vinnande värdet.
Vid strukturella konflikter (update_delete, delete_update, insert_insert) lägger lösaren till en ny åtgärd i resultatet:
update_deletemedOursWinslägger till en UPDATE-åtgärd.update_deletemedTheirsWinslägger till en DELETE-åtgärd.delete_updatemedOursWinslägger till en DELETE-åtgärd.delete_updatemedTheirsWinslägger till en UPDATE-åtgärd.insert_insertanvänder det vinnande värdet för raden.
Det lösta MergeResult har en tom konfliktlista och kan tillämpas direkt.
Manuell lösning
Om du vill ha detaljerad kontroll granskar du varje konflikt och skapar en egen lösning:
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";
}
}
Tillämpa med olösta konflikter
Applier vägrar att tillämpa ett sammanslagningsresultat med olösta konflikter. Den kastar ett ConflictException:
use Merql\Exceptions\ConflictException;
try {
$applier->apply($result);
} catch (ConflictException $e) {
echo $e->getMessage();
// "Cannot apply merge with 3 unresolved conflicts"
}
Lös alltid konflikter innan du tillämpar resultatet, antingen med ConflictResolver::resolve() eller genom att manuellt skapa ett rent MergeResult.