Pereiti prie turinio

Konfliktų tvarkymas

Konfliktas kyla, kai abi sujungimo pusės pakeičia tuos pačius duomenis nesuderinamais būdais. merql aptinka keturis konfliktų tipus ir siūlo automatines bei rankines jų sprendimo strategijas.

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";
    }
}

Konfliktų tipai

update_update

Abi pusės pakeitė tą patį tos pačios eilutės stulpelį į skirtingas reikšmes. Tai dažniausiai pasitaikantis konflikto tipas.

Kodas
Base:    posts #42  title = "Hello"
Ours:    posts #42  title = "Welcome"
Theirs:  posts #42  title = "Greetings"

Konflikte nurodoma: lentelė posts, eilutės raktas 42, stulpelis title, mūsų reikšmė Welcome, jų reikšmė Greetings, bazinė reikšmė Hello.

update_delete

Viena pusė atnaujino eilutę, o kita ją ištrynė. merql negali nustatyti, kuris veiksmas turi laimėti.

Kodas
Ours:    posts #42  title = "Updated Title"    (updated)
Theirs:  posts #42  (deleted)

Konflikte nurodoma: lentelė posts, eilutės raktas 42, tipas update_delete. Mūsų reikšmėje yra visa atnaujinta eilutė. Jų reikšmė yra null.

delete_update

Priešingas atvejis nei update_delete. Jie atnaujino eilutę, o mes ją ištrynėme.

Kodas
Ours:    posts #42  (deleted)
Theirs:  posts #42  title = "Updated Title"    (updated)

Konflikte nurodoma: tipas delete_update. Mūsų reikšmė yra null, o jų reikšmėje pateikta visa atnaujinta eilutė.

insert_insert

Abi pusės įterpė eilutę su tuo pačiu pirminiu raktu. Kadangi abi eilutės naujos (bazinėje versijoje jų nebuvo), merql negali jų sujungti.

Kodas
Base:    (no row #99)
Ours:    posts #99  { title: "My Post" }
Theirs:  posts #99  { title: "Their Post" }

Konflikte nurodoma: tipas insert_insert. Mūsų reikšmėje pateikti mūsų eilutės duomenys, o jų reikšmėje – jų eilutės duomenys.

Konfliktų klasė

Kiekvienas konfliktas yra tik skaitomas objektas su šiais prieigos metodais:

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)

column() metodas grąžina stulpelio pavadinimą update_update konfliktams, o struktūriniams konfliktams (update_delete, delete_update, insert_insert) – null.

Pirminio rakto dekodavimas

Eilutės raktas yra užkoduota eilutė. Norėdami gauti įvardytą pirminių raktų masyvą, perduokite tapatybės stulpelius:

PHP
$pk = $conflict->primaryKey(['id']);
// ['id' => '42']

$pk = $conflict->primaryKey(['tenant_id', 'user_id']);
// ['tenant_id' => '1', 'user_id' => '99']

Automatinis sprendimas naudojant ConflictPolicy

ConflictResolver taiko strategiją, kad iš karto išspręstų visus konfliktus:

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);

ConflictPolicy išvardijimas

Reikšmė Veikimas
OursWins update_update atveju naudoti mūsų reikšmę. update_delete atveju palikti atnaujinimą. delete_update atveju palikti ištrynimą. insert_insert atveju naudoti mūsų įterpimą.
TheirsWins update_update atveju naudoti jų reikšmę. update_delete atveju ištrinti. delete_update atveju palikti atnaujinimą. insert_insert atveju naudoti jų įterpimą.
Manual Grąžinti nepakeistą rezultatą. Visi konfliktai lieka neišspręsti.

Kaip veikia sprendimas

update_update konfliktų atveju sprendiklis pataiso esamą tos eilutės sujungimo operaciją ir pakeičia konfliktuojančio stulpelio reikšmę laiminčios pusės reikšme.

Struktūrinių konfliktų (update_delete, delete_update, insert_insert) atveju sprendiklis į rezultatą įtraukia naują operaciją:

  • update_delete su OursWins prideda UPDATE operaciją.
  • update_delete su TheirsWins prideda DELETE operaciją.
  • delete_update su OursWins prideda DELETE operaciją.
  • delete_update su TheirsWins prideda UPDATE operaciją.
  • insert_insert naudoja laiminčios pusės eilutės reikšmes.

Išspręstame MergeResult yra tuščias konfliktų sąrašas, todėl jį galima iškart pritaikyti.

Rankinis sprendimas

Norėdami tiksliai kontroliuoti procesą, patikrinkite kiekvieną konfliktą ir sukurkite savo sprendimą:

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";
    }
}

Taikymas esant neišspręstų konfliktų

Applier atsisako taikyti sujungimo rezultatą su neišspręstais konfliktais. Išmetama ConflictException:

PHP
use Merql\Exceptions\ConflictException;

try {
    $applier->apply($result);
} catch (ConflictException $e) {
    echo $e->getMessage();
    // "Cannot apply merge with 3 unresolved conflicts"
}

Prieš taikydami visada išspręskite konfliktus: naudokite ConflictResolver::resolve() arba rankiniu būdu sukurkite švarų MergeResult.