
# Fletning på kolonneniveau

Fletning på kolonneniveau er merqls største fordel frem for en naiv sammenligning på rækkeniveau. Når begge sider ændrer den samme række, sammenligner merql hver kolonne for sig i stedet for at markere hele rækken som en konflikt.

## Problemet med fletning på rækkeniveau

Forestil dig en tabel af typen `posts`, hvor to personer redigerer den samme række:

```
Base:    post #42  { title: "Hello",     content: "Body",    status: "draft"   }
Ours:    post #42  { title: "Hello",     content: "Body v2", status: "draft"   }
Theirs:  post #42  { title: "New Title", content: "Body",    status: "publish" }
```

En fletning på rækkeniveau registrerer, at begge sider har ændret rækken, og erklærer en konflikt. Men ændringerne overlapper slet ikke. Vores ændrede `content`, mens deres ændrede `title` og `status`.

## Sådan løser fletning på kolonneniveau problemet

merql anvender de samme trevejsfletteregler på hver enkelt kolonne:

```
title:   base="Hello", ours="Hello",  theirs="New Title"  -> accept theirs
content: base="Body",  ours="Body v2", theirs="Body"      -> accept ours
status:  base="draft", ours="draft",  theirs="publish"    -> accept theirs
```

Resultat: `{ title: "New Title", content: "Body v2", status: "publish" }`. Konfliktfri fletning.

## Sådan fungerer det

Når `ThreeWayMerge` registrerer, at begge sider har opdateret den samme række, overlader den arbejdet til `ColumnMerge::merge()`:

```php
use Merql\Merge\ColumnMerge;

$result = ColumnMerge::merge(
    table: 'posts',
    rowKey: '42',
    base: ['title' => 'Hello', 'content' => 'Body', 'status' => 'draft'],
    ours: ['title' => 'Hello', 'content' => 'Body v2', 'status' => 'draft'],
    theirs: ['title' => 'New Title', 'content' => 'Body', 'status' => 'publish'],
);

// $result['values']    -> merged column values
// $result['conflicts'] -> list of Conflict objects (empty if clean)
```

De samme regler gælder for hver kolonne:

| Base | Vores | Deres | Resultat |
|---|---|---|---|
| A | A | A | Behold base (ingen ændring) |
| A | A | B | Brug deres |
| A | B | A | Brug vores |
| A | B | B | Brug værdien (begge er enige) |
| A | B | C | **Konflikt** (forskellige værdier) |

## Hvornår opstår der stadig konflikter

Der opstår en konflikt på kolonneniveau, når begge sider har ændret den samme kolonne til forskellige værdier:

```
Base:    post #42  { title: "Hello" }
Ours:    post #42  { title: "Welcome" }
Theirs:  post #42  { title: "Greetings" }
```

Begge ændrede `title` til forskellige værdier. Det er en reel konflikt, der kræver en løsning.

```php
$result = ColumnMerge::merge(
    'posts', '42',
    ['title' => 'Hello'],
    ['title' => 'Welcome'],
    ['title' => 'Greetings'],
);

// $result['conflicts'] contains one Conflict:
//   table: "posts", column: "title"
//   oursValue: "Welcome", theirsValue: "Greetings", baseValue: "Hello"
```

## Delvise konflikter

En enkelt række kan indeholde både konfliktfri fletninger og konflikter. Hvis én kolonne flettes uden konflikt, men en anden giver en konflikt, indeholder rækkens `MergeOperation` de flettede værdier for konfliktfri kolonner og vores værdi som standard for kolonner med konflikter. Konflikten rapporteres stadig separat.

```
Base:    { title: "Hello", content: "Body",    status: "draft" }
Ours:    { title: "Welcome", content: "Body v2", status: "draft" }
Theirs:  { title: "Greetings", content: "Body",    status: "publish" }
```

Her giver `title` en konflikt (begge ændrede den forskelligt), `content` flettes uden konflikt (kun vores side ændrede den), og `status` flettes uden konflikt (kun deres side ændrede den). Handlingen indeholder:

```php
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
```

## Værdisammenligning

Kolonneværdier sammenlignes som strenge efter konvertering. `NULL` er en særskilt værdi:

- `NULL` til `NULL` = ingen ændring.
- `NULL` til `"hello"` = opdatering.
- `"hello"` til `NULL` = opdatering.
- `"1"` til `1` = ingen ændring (begge konverteres til strengen `"1"`).

Dette svarer til MerQL-forbindelsesadapterens hentningssemantik: alle databaseværdier returneres som strenge (eller NULL).

## Fletning på celleniveau

Hvis fletning på kolonneniveau stadig giver en konflikt (begge sider har ændret den samme kolonne til forskellige værdier), kan merql gå et niveau dybere med [fletning på celleniveau](https://merql.inline0.dev/da/docs/merge/cell-level). Det gælder TEXT-kolonner (linjevis difference) og JSON-kolonner (nøglevis difference) og løser tilfælde, hvor begge sider har redigeret forskellige dele af den samme kolonneværdi.
