
# Združevanje na ravni celic

Združevanje na ravni stolpcev zazna, kadar obe strani spremenita isti stolpec. Združevanje na ravni celic gre globlje in združuje vsebino znotraj tega stolpca. Dve osebi lahko brez težav združita spremembe različnih vrstic polja TEXT ali različnih ključev objekta JSON.

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

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

## Omogočanje združevanja na ravni celic

Združevanje na ravni celic je izbirno. Deluje samo, če je `ThreeWayMerge` ustvarjen z nastavitvijo `CellMergeConfig`. Brez nje vsak stolpec, ki sta ga obe strani spremenili na različni vrednosti, povzroči spor `update_update`.

Vmesnik `Merql` ne sprejme konfiguracije: `Merql::merge()`, `Merql::patch()` in `Merql::plan()` ustvarijo `new ThreeWayMerge()` brez nje, ukaz `merge` v CLI pa se izvaja prek `Merql::merge()`. Vedno združujejo na ravni stolpcev ([#7](https://github.com/inline0/merql/issues/7)).

Če želite posnetke, shranjene prek vmesnika ali CLI, združiti na ravni celic, jih naložite in sami pokličite `ThreeWayMerge`. Rezultat je običajen `MergeResult`, zato ga lahko vmesnik še vedno uveljavi, `MergePlanBuilder` pa ga pretvori v [načrt združevanja](https://merql.inline0.dev/sl/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);
```

Združevalniki celic se zaženejo samo za stolpce, ki sta jih spremenili obe strani. Če je stolpec spremenila samo ena stran, se njegova vrednost prevzame nespremenjena.

## CellMergeConfig

`CellMergeConfig` povezuje stolpce ali vrste stolpcev z izvedbami združevalnikov celic. Tovarna `auto()` ponuja smiselne privzete vrednosti:

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

Prednostni vrstni red razreševanja združevalnikov:

1. Polno kvalificirano ime stolpca (`table.column`).
2. Nekvalificirano ime stolpca (`column`).
3. Ujemanje vzorca vrste stolpca (brez razlikovanja velikih in malih črk `str_contains`).
4. Privzeto: `OpaqueCellMerger` (ob različnih vrednostih vedno sporoči spor).

## TextCellMerger

Združuje večvrstično besedilo z algoritmom Myers diff, ki ga git uporablja tudi za vsebino datotek. Vsaka vrstica je obravnavana kot enota, spremembe različnih vrstic pa se združijo brez sporov.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Primer: čisto združevanje besedila

```
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
```

Spremenjene so bile različne vrstice. Združitev ustvari `"LINE ONE\nline two\nLINE THREE"` brez sporov.

### Primer: spor pri besedilu

```
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 strani sta isto vrstico spremenili na različni vrednosti. Združevalnik vrne rezultat s sporom. Združena vsebina vključuje oznake sporov, podobne kot pri git.

### Kako deluje

`TextCellMerger` uporabi `ThreeWayMerge::merge()` iz pitmaster, ki izračuna Myersove razlike med osnovo in vsako stranjo, nato pa združi oba skripta sprememb. Če se spremembe prekrivajo na istem območju vrstic, sporoči spor.

## JsonCellMerger

Združuje objekte JSON tako, da primerja vsak vrhnji ključ posebej. Vsak ključ združi po standardnih trosmernih pravilih.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Primer: čisto združevanje 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}
```

Spremenjeni so bili različni ključi. Združeni rezultat je:

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

### Primer: spor pri JSON

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

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

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

Obe strani sta ključ `color` spremenili na različni vrednosti. To je spor. Združevalnik privzeto izbere našo vrednost in sporoči en spor.

### Pravila za ključe

| Osnova | Naši | Njihovi | Rezultat |
|---|---|---|---|
| Ključ nespremenjen | Ključ nespremenjen | Sprejmi osnovo |
| Ključ nespremenjen | Ključ spremenjen | Sprejmi njihovega |
| Ključ spremenjen | Ključ nespremenjen | Sprejmi našega |
| Ključ spremenjen (enako) | Ključ spremenjen (enako) | Sprejmi (ujemanje) |
| Ključ spremenjen (različno) | Ključ spremenjen (različno) | **Spor** (privzeto: naši) |
| Ključ obstaja | Ključ obstaja | Ključ odstranjen | Sprejmi odstranitev |
| Ključ obstaja | Ključ odstranjen | Ključ obstaja | Sprejmi odstranitev |
| Ključ manjka | Ključ dodan | Ključ manjka | Sprejmi dodajanje |
| Ključ manjka | Ključ manjka | Ključ dodan | Sprejmi dodajanje |

Vgnezdeni objekti so primerjani kot neprosojni nizi JSON in ne rekurzivno. Če se vgnezdeni objekt razlikuje, se primerja celotna vrednost tega ključa, ne njegovi podključi.

### Zahteve

Vse tri vrednosti morajo biti veljavni objekti ali polja JSON. Če katera koli vrednost ni veljaven JSON ali je skalar JSON (niz, število, logična vrednost), združevalnik sporoči spor.

## OpaqueCellMerger

Privzeti združevalnik, kadar strategija za raven celic ni nastavljena. Vrednost stolpca obravnava kot neprosojen blok in vedno sporoči spor, kadar se strani razlikujeta.

```php
use Merql\CellMerge\OpaqueCellMerger;

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

## CellMergeResult

Vsi združevalniki celic vrnejo `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
```

Tovarniške metode:

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

## Prilagojeni združevalniki celic

Izvedite vmesnik `CellMerger`, da dodate lastno strategijo združevanja:

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