
# 冲突处理

合并双方以不兼容的方式修改了相同数据时，就会发生冲突。merql 可检测四种冲突类型，并提供自动和手动解决策略。

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

## 冲突类型

### update_update

双方将同一行的同一列改成了不同的值。这是最常见的冲突类型。

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

冲突报告包含：表 `posts`、行键 `42`、列 `title`、我方值 `Welcome`、对方值 `Greetings`、基线值 `Hello`。

### update_delete

一方更新了某行，另一方删除了该行。merql 无法判断应该保留更新还是删除。

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

冲突报告包含：表 `posts`、行键 `42`、类型 `update_delete`。我方值包含完整的更新后行。对方值为 `null`。

### delete_update

与 update_delete 相反。对方更新了该行，我方删除了该行。

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

冲突报告包含：类型 `delete_update`。我方值为 `null`，对方值包含完整的更新后行。

### insert_insert

双方都插入了主键相同的行。由于这两行都是新行（基线中不存在），merql 无法合并它们。

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

冲突报告包含：类型 `insert_insert`。我方值包含我方行数据，对方值包含对方行数据。

## Conflict 类

每个冲突都是只读对象，提供以下访问器：

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

对于 `update_update` 冲突，`column()` 方法会返回列名；对于结构性冲突（`update_delete`、`delete_update`、`insert_insert`），则返回 `null`。

### 解码主键

行键是经过编码的字符串。要获取带名称的主键数组，请传入标识列：

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

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

## 使用 ConflictPolicy 自动解决

`ConflictResolver` 会应用策略，一次性解决所有冲突：

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

| 值 | 行为 |
|---|---|
| `OursWins` | update_update 时使用我方值。update_delete 时保留更新。delete_update 时保留删除。insert_insert 时采用我方插入。 |
| `TheirsWins` | update_update 时使用对方值。update_delete 时删除。delete_update 时保留更新。insert_insert 时采用对方插入。 |
| `Manual` | 原样返回结果。所有冲突仍未解决。 |

### 解决方式

对于 `update_update` 冲突，解决器会修补该行现有的合并操作，将冲突列的值替换为胜出一方的值。

对于结构性冲突（`update_delete`、`delete_update`、`insert_insert`），解决器会向结果添加新操作：

- `update_delete` 和 `OursWins` 会添加 UPDATE 操作。
- `update_delete` 和 `TheirsWins` 会添加 DELETE 操作。
- `delete_update` 和 `OursWins` 会添加 DELETE 操作。
- `delete_update` 和 `TheirsWins` 会添加 UPDATE 操作。
- `insert_insert` 会采用胜出一方的行值。

解决后的 `MergeResult` 冲突列表为空，可以直接应用。

## 手动解决

如需精细控制，请检查各项冲突并自行构建解决方案：

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

## 应用含未解决冲突的结果

`Applier` 不会应用仍有未解决冲突的合并结果。它会抛出 `ConflictException`：

```php
use Merql\Exceptions\ConflictException;

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

应用前务必使用 `ConflictResolver::resolve()` 解决冲突，或手动构建一个无冲突的 `MergeResult`。
