
# Zlučovanie na úrovni buniek

Zlučovanie na úrovni stĺpcov zisťuje, či obe strany zmenili ten istý stĺpec. Zlučovanie na úrovni buniek ide hlbšie a spája obsah daného stĺpca. Dvaja ľudia môžu bez konfliktu zlúčiť úpravy rôznych riadkov poľa TEXT alebo rôznych kľúčov objektu JSON.

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;

$config = CellMergeConfig::auto();
$merge = new ThreeWayMerge($config);
$result = $merge->merge($base, $ours, $theirs);
```

## Zapnutie zlučovania na úrovni buniek

Zlučovanie na úrovni buniek je voliteľné. Spustí sa iba v `ThreeWayMerge` vytvorenom s `CellMergeConfig`. Bez neho sa každý stĺpec, ktorý obe strany zmenili na rôzne hodnoty, považuje za konflikt `update_update`.

Fasáda `Merql` neprijíma konfiguráciu: `Merql::merge()`, `Merql::patch()` a `Merql::plan()` vytvárajú `new ThreeWayMerge()` bez nej a príkaz `merge` v CLI používa `Merql::merge()`. Vždy vykonávajú zlúčenie na úrovni stĺpcov ([#7](https://github.com/inline0/merql/issues/7)).

Ak chcete zlúčiť snímky uložené fasádou alebo príkazom CLI na úrovni buniek, načítajte ich a sami zavolajte `ThreeWayMerge`. Výsledok je bežný `MergeResult`, takže ho fasáda stále môže použiť, a `MergePlanBuilder` z neho vytvorí [plán zlúčenia](https://merql.inline0.dev/sk/docs/merge/plans):

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Merql;
use Merql\Plan\MergePlanBuilder;
use Merql\Snapshot\SnapshotStore;

$base   = SnapshotStore::load('base');
$ours   = SnapshotStore::load('ours');
$theirs = SnapshotStore::load('theirs');

$result = (new ThreeWayMerge(CellMergeConfig::auto()))->merge($base, $ours, $theirs);

$plan = (new MergePlanBuilder())->build('plan-1', $base, $ours, $theirs, $result);
```

Zlučovače buniek sa spúšťajú iba pre stĺpce, ktoré zmenili obe strany. Ak stĺpec zmenila iba jedna strana, jeho hodnota sa prevezme bez úprav.

## CellMergeConfig

`CellMergeConfig` mapuje stĺpce alebo typy stĺpcov na implementácie zlučovačov buniek. Továreň `auto()` poskytuje vhodné predvolené nastavenia:

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;
use Merql\CellMerge\TextCellMerger;

// Auto: column types containing "text" (TEXT, MEDIUMTEXT, LONGTEXT, ...) use
// TextCellMerger, column types containing "json" use JsonCellMerger.
$config = CellMergeConfig::auto();

// Custom: assign mergers to specific columns.
$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('settings', new JsonCellMerger())
    ->forType('json', new JsonCellMerger());
```

Priorita výberu zlučovača:

1. Úplný názov stĺpca (`table.column`).
2. Názov stĺpca bez kvalifikátora (`column`).
3. Zhoda so vzorom typu stĺpca (bez rozlišovania veľkých a malých písmen `str_contains`).
4. Predvolené nastavenie: `OpaqueCellMerger` (pri každom rozdiele vždy nahlási konflikt).

## TextCellMerger

Zlučuje viacriadkový text pomocou Myersovho rozdielového algoritmu, ktorý git používa aj pre obsah súborov. Každý riadok sa považuje za jednotku a zmeny v rôznych riadkoch sa zlúčia bez konfliktu.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Príklad: čisté zlúčenie textu

```
Base:    "line one\nline two\nline three"
Ours:    "LINE ONE\nline two\nline three"    // changed line 1
Theirs:  "line one\nline two\nLINE THREE"    // changed line 3
```

Upravili sa rôzne riadky. Výsledkom zlúčenia je `"LINE ONE\nline two\nLINE THREE"` bez konfliktu.

### Príklad: konflikt v texte

```
Base:    "line one\nline two"
Ours:    "changed by us\nline two"     // changed line 1
Theirs:  "changed by them\nline two"   // also changed line 1
```

Obe strany zmenili ten istý riadok na rôzne hodnoty. Zlučovač vráti výsledok s konfliktom. Zlúčený obsah zahŕňa značky konfliktu podobné tým v git.

### Ako to funguje

`TextCellMerger` využíva `ThreeWayMerge::merge()` z pitmaster, ktorý vypočíta Myersov rozdiel medzi base a každou stranou a potom spojí oba skripty úprav. Ak sa úpravy prekrývajú v tej istej oblasti riadka, nahlási konflikt.

## JsonCellMerger

Zlučuje objekty JSON porovnávaním jednotlivých kľúčov najvyššej úrovne. Každý kľúč sa zlúči podľa štandardných trojstranných pravidiel.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Príklad: čisté zlúčenie JSON

```json
// Base
{"name": "Alice", "role": "editor", "active": true}

// Ours: changed "role"
{"name": "Alice", "role": "admin", "active": true}

// Theirs: changed "name"
{"name": "Bob", "role": "editor", "active": true}
```

Zmenili sa rôzne kľúče. Výsledok zlúčenia:

```json
{"name": "Bob", "role": "admin", "active": true}
```

### Príklad: konflikt JSON

```json
// Base
{"color": "red"}

// Ours
{"color": "blue"}

// Theirs
{"color": "green"}
```

Obe strany zmenili kľúč `color` na rôzne hodnoty. Ide o konflikt. Zlučovač predvolene uprednostní ours a nahlási jeden konflikt.

### Pravidlá na úrovni kľúčov

| Base | Ours | Theirs | Výsledok |
|---|---|---|---|
| Kľúč nezmenený | Kľúč nezmenený | Prijať base |
| Kľúč nezmenený | Kľúč zmenený | Prijať theirs |
| Kľúč zmenený | Kľúč nezmenený | Prijať ours |
| Kľúč zmenený (rovnako) | Kľúč zmenený (rovnako) | Prijať (zhoda) |
| Kľúč zmenený (odlišne) | Kľúč zmenený (odlišne) | **Konflikt** (predvolene: ours) |
| Kľúč existuje | Kľúč existuje | Kľúč odstránený | Prijať odstránenie |
| Kľúč existuje | Kľúč odstránený | Kľúč existuje | Prijať odstránenie |
| Kľúč chýba | Kľúč pridaný | Kľúč chýba | Prijať pridanie |
| Kľúč chýba | Kľúč chýba | Kľúč pridaný | Prijať pridanie |

Vnorené objekty sa porovnávajú ako nepriehľadné reťazce JSON, nie rekurzívne. Ak sa vnorený objekt líši, porovná sa celá hodnota daného kľúča, nie jeho podkľúče.

### Požiadavky

Všetky tri hodnoty musia byť platné objekty alebo polia JSON. Ak niektorá hodnota nie je platná JSON alebo je skalárnou hodnotou JSON (reťazec, číslo, logická hodnota), zlučovač prejde na konflikt.

## OpaqueCellMerger

Predvolený zlučovač, ak nie je nakonfigurovaná žiadna stratégia zlučovania buniek. Hodnotu stĺpca považuje za nepriehľadný blok a vždy nahlási konflikt, keď sa hodnoty oboch strán líšia.

```php
use Merql\CellMerge\OpaqueCellMerger;

$merger = new OpaqueCellMerger();
$result = $merger->merge($base, $ours, $theirs);
// $result->clean is always false when $ours !== $theirs
```

## CellMergeResult

Všetky zlučovače buniek vracajú `CellMergeResult`:

```php
$result = $merger->merge($base, $ours, $theirs);

$result->clean;     // bool: true if merge resolved cleanly
$result->value;     // mixed: the merged value (or ours on conflict)
$result->conflicts; // int: number of conflicts within the cell
```

Metódy továrne:

```php
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // n conflicts, defaults to ours
```

## Vlastné zlučovače buniek

Ak chcete pridať vlastnú stratégiu zlúčenia, implementujte rozhranie `CellMerger`:

```php
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

class YamlCellMerger implements CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult
    {
        // Parse YAML, merge keys, return result.
        $merged = $this->mergeYaml($base, $ours, $theirs);

        if ($merged['clean']) {
            return CellMergeResult::resolved($merged['value']);
        }

        return CellMergeResult::conflict($ours);
    }
}

$config = (new CellMergeConfig())
    ->forColumn('config_data', new YamlCellMerger());
```
