Konflikthåndtering
Der opstår en konflikt, når begge sider af en fletning ændrer de samme data på måder, der ikke kan forenes. merql registrerer fire typer konflikter og tilbyder automatiske og manuelle løsningsmetoder.
$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
Begge sider ændrede den samme kolonne i den samme række til forskellige værdier. Dette er den mest almindelige konflikttype.
Base: posts #42 title = "Hello"
Ours: posts #42 title = "Welcome"
Theirs: posts #42 title = "Greetings"
Konflikten rapporterer: tabel posts, rækkenøgle 42, kolonne title, vores værdi Welcome, deres værdi Greetings, baseværdi Hello.
update_delete
Den ene side opdaterede en række, mens den anden slettede den. merql kan ikke afgøre, om opdateringen eller sletningen skal gælde.
Ours: posts #42 title = "Updated Title" (updated)
Theirs: posts #42 (deleted)
Konflikten rapporterer: tabel posts, rækkenøgle 42, type update_delete. Vores værdi indeholder hele den opdaterede række. Deres værdi er null.
delete_update
Det omvendte af update_delete. Deres side opdaterede rækken, mens vores side slettede den.
Ours: posts #42 (deleted)
Theirs: posts #42 title = "Updated Title" (updated)
Konflikten rapporterer: type delete_update. Vores værdi er null, og deres værdi indeholder hele den opdaterede række.
insert_insert
Begge sider indsatte en række med den samme primærnøgle. Da begge rækker er nye (de fandtes ikke i base), kan merql ikke flette dem.
Base: (no row #99)
Ours: posts #99 { title: "My Post" }
Theirs: posts #99 { title: "Their Post" }
Konflikten rapporterer: type insert_insert. Vores værdi indeholder vores rækkedata, og deres værdi indeholder deres rækkedata.
Klassen Conflict
Hver konflikt er et skrivebeskyttet objekt med følgende adgangsmetoder:
$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() returnerer et kolonnenavn for konflikter af typen update_update og null for strukturelle konflikter (update_delete, delete_update, insert_insert).
Afkodning af primærnøglen
Rækkenøglen er en kodet streng. Hvis du vil have et primærnøglearray med navngivne nøgler, skal du sende identitetskolonnerne:
$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 anvender en politik til at løse alle konflikter på én gang:
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);
ConflictPolicy-enum
| Værdi | Adfærd |
|---|---|
OursWins |
Brug vores værdi for update_update. Behold opdateringen ved update_delete. Behold sletningen ved delete_update. Brug vores indsættelse ved insert_insert. |
TheirsWins |
Brug deres værdi for update_update. Slet ved update_delete. Behold opdateringen ved delete_update. Brug deres indsættelse ved insert_insert. |
Manual |
Returner resultatet uændret. Alle konflikter forbliver uløste. |
Sådan fungerer løsningen
Ved update_update-konflikter ændrer løseren den eksisterende flettehandling for rækken og erstatter den konfliktfyldte kolonneværdi med den vindende sides værdi.
Ved strukturelle konflikter (update_delete, delete_update, insert_insert) føjer løseren en ny handling til resultatet:
update_deletemedOursWinstilføjer en UPDATE-handling.update_deletemedTheirsWinstilføjer en DELETE-handling.delete_updatemedOursWinstilføjer en DELETE-handling.delete_updatemedTheirsWinstilføjer en UPDATE-handling.insert_insertbruger den vindende sides rækkeværdier.
Den løste MergeResult har en tom konfliktliste og kan anvendes direkte.
Manuel løsning
Hvis du ønsker præcis kontrol, kan du undersøge hver konflikt og opbygge din 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";
}
}
Anvendelse med uløste konflikter
Applier nægter at anvende et fletteresultat med uløste konflikter. Den udløser en ConflictException:
use Merql\Exceptions\ConflictException;
try {
$applier->apply($result);
} catch (ConflictException $e) {
echo $e->getMessage();
// "Cannot apply merge with 3 unresolved conflicts"
}
Løs altid konflikter, før du anvender resultatet, enten med ConflictResolver::resolve() eller ved manuelt at opbygge en ren MergeResult.