
# PHP API uzziņu rokasgrāmata

Pilnīga uzziņu rokasgrāmata par visām merql publiskajām klasēm un metodēm.

## Merql (fasāde)

`Merql\Merql` ir statiska ieejas vieta biežākajām darbībām. Tā apvieno zemāka līmeņa klases un automātiski pārvalda momentuzņēmumu saglabāšanu.

```php
use Merql\Connection;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;
use Merql\Merql;

$connection = Connection::sqlite(':memory:');
Merql::init($connection, identityRules: new IdentityRuleSet([
    'options' => IdentityRule::natural(['option_name']),
]));
```

### `init(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null): void`

Inicializējiet fasādi ar datubāzes savienojumu. Tas jāizsauc pirms `snapshot()`, `apply()` vai `applyGuarded()`; izsauciet to vēlreiz, lai pārslēgtu datubāzes, piemēram, lai izveidotu mūsu un viņu puses momentuzņēmumus no divām dažādām kopijām. Ja nav `$driver`, tiek izsaukts `DriverFactory::create()`, kas rada kļūdu citiem dzinējiem, nevis MySQL un SQLite, ja vien nav reģistrēts draiveris. Nododiet draiveri, lai aizstātu automātisko noteikšanu, vai identitātes noteikumu kopu, lai momentuzņēmuma izveides laikā aizstātu rindu identitāti katrai tabulai.

### `snapshot(string $name, array $tables = []): Snapshot`

Uzņemiet pašreizējā datubāzes stāvokļa momentuzņēmumu un saglabājiet to diskā. Nododiet tabulu nosaukumu masīvu, lai izveidotu momentuzņēmumus tikai konkrētām tabulām, vai izlaidiet to, lai iekļautu visas tabulas.

```php
$snapshot = Merql::snapshot('baseline');
$snapshot = Merql::snapshot('baseline', ['posts', 'users']);
```

### `diff(string $base, string $current): Changeset`

Ielādējiet divus nosauktus momentuzņēmumus un aprēķiniet izmaiņu kopu starp tiem. `init()` nav nepieciešams, jo darbība izmanto saglabātos momentuzņēmumus.

```php
$changeset = Merql::diff('before', 'after');
```

### `merge(string $base, string $ours, string $theirs): MergeResult`

Trīspusēja sapludināšana starp trim nosauktiem momentuzņēmumiem. Atgriež rezultātu ar konfliktus neradošām darbībām un visiem konfliktiem. Sapludināšana notiek kolonnu līmenī: fasāde izveido `ThreeWayMerge` bez `CellMergeConfig`, tāpēc šūnu līmeņa sapludināšana netiek veikta. Šūnu līmeņa sapludināšanai izmantojiet tieši [`ThreeWayMerge`](#threewaymerge).

```php
$result = Merql::merge('base', 'ours', 'theirs');
```

### `patch(string $base, string $changes): MergeResult`

Divpusēja sapludināšana: piemērot izmaiņas bāzei. Līdzvērtīga `merge($base, $base, $changes)`. Vienmēr iegūst tīru rezultātu, jo vienlaicīgu labojumu nav. Notiek tikai kolonnu līmenī, tāpat kā `merge()`.

```php
$result = Merql::patch('base', 'changes');
```

### `apply(MergeResult $result): ApplyResult`

Izpildiet tīras sapludināšanas rezultātu datubāzē, kas nodota funkcijai `init()`. Nepieciešams `init()`. Ja joprojām ir neatrisināti konflikti, rodas `ConflictException`.

Tiek izpildīta katra rezultāta darbība, tāpēc viss `merge()` rezultāts tiek piemērots bez konfliktiem tikai datubāzei, kurā joprojām ir bāzes stāvoklis. Datubāzei, kurā jau ir vienas puses stāvoklis, piemērojiet trūkstošo darbību atlasi; skatiet [Guarded Apply](https://merql.inline0.dev/lv/docs/apply/guarded).

```php
$applied = Merql::apply($result);
echo $applied->rowsAffected();
```

### `plan(string $id, string $base, string $ours, string $theirs, array $metadata = []): MergePlan`

Izveidojiet sapludināšanas plānu, kas gatavs UI, no trim nosauktiem momentuzņēmumiem. Plānā ir stabili darbību ID, izmaiņu grupu ID, darbību konteksts, konflikti, shēmas neatbilstības, kopsavilkums, metadati un plāna jaucējvērtība. Sapludināšana notiek kolonnu līmenī, tāpat kā `merge()`; šūnu līmeņa sapludināšanai nododiet šūnu līmenī sapludinātu rezultātu funkcijai `MergePlanBuilder::build()`.

```php
$plan = Merql::plan('plan-1', 'base', 'ours', 'theirs', [
    'source' => 'review-ui',
]);
```

### `applyGuarded(MergeResult $result, Snapshot $expectedLive): ApplyResult`

Piemērojiet tīras sapludināšanas rezultātu, izmantojot optimistiskus priekšnosacījumus par aktuālajām rindām. Lai atjauninātu vai dzēstu, pašreizējām rindām jāsakrīt ar `$expectedLive`; lai ievietotu, mērķa identitāte nedrīkst jau pastāvēt. `$expectedLive` ir mērķa datubāzes momentuzņēmums (viņu puses momentuzņēmums, kad mūsu puses izmaiņas tiek pārnestas uz datubāzi, no kuras tika izveidots viņu puses momentuzņēmums). Ja kāds priekšraksts neietekmē nevienu rindu, transakcija tiek atcelta.

```php
use Merql\Snapshot\SnapshotStore;

$applied = Merql::applyGuarded($selectedResult, SnapshotStore::load('theirs'));
```

### `reset(): void`

Notīriet iekšējos savienojuma un Snapshotter eksemplārus. Galvenokārt izmanto testos, lai atiestatītu stāvokli starp palaišanas reizēm.

---

## Savienojums

`Merql\Connection` izveido savienojuma adapterus ar pārdomātiem noklusējuma iestatījumiem. PDO savienojumos kļūdas gadījumā tiek izmantoti izņēmumi, asociatīvais datu izgūšanas režīms, tiek atspējota emulēta sagatavošana un izgūtās vērtības tiek pārveidotas virknēs. mysqli adapteris normalizē izgūtās vērtības tā, lai tās būtu virknes vai null.

### `mysql(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): PdoDatabaseConnection`

```php
use Merql\Connection;

$connection = Connection::mysql('127.0.0.1', 'my_app', 'root', 'secret');
```

### `mysqli(string $host, string $database, string $username, string $password = '', int $port = 3306, string $charset = 'utf8mb4'): MysqliDatabaseConnection`

```php
$connection = Connection::mysqli('127.0.0.1', 'my_app', 'root', 'secret');
```

### `sqlite(string $path = ':memory:'): PdoDatabaseConnection`

Izveido SQLite savienojumu. Automātiski iespējo ārējās atslēgas, izmantojot `PRAGMA foreign_keys = ON`.

```php
$connection = Connection::sqlite('/var/data/app.sqlite');
$connection = Connection::sqlite(':memory:');
```

### `fromDsn(string $dsn, string $username = '', string $password = ''): PdoDatabaseConnection`

Izveidojiet savienojumu, izmantojot jebkuru PDO DSN virkni. merql ietver draiverus tikai `mysql` un `sqlite`; ar citiem DSN savienojums tiek izveidots, bet `Merql::init()`, `Snapshotter` un applieri rada `Unsupported database driver`, ja vien draiveris nav reģistrēts ar `DriverFactory::register()`.

```php
$connection = Connection::fromDsn('mysql:host=localhost;dbname=app', 'root', 'pass');
```

### `fromPdo(PDO $pdo): PdoDatabaseConnection`

Aptiniet esošu PDO instanci.

### `fromMysqli(mysqli $mysqli, string $charset = 'utf8mb4'): MysqliDatabaseConnection`

Aptiniet esošu mysqli instanci.

### Adaptera līgums

Visi adapteri implementē `Merql\Database\DatabaseConnection`:

```php
interface DatabaseConnection
{
    public function driverName(): string;
    public function execute(string $sql, array $params = []): int;
    public function query(string $sql, array $params = []): array;
    public function scalar(string $sql, array $params = []): ?string;
    public function beginTransaction(): void;
    public function commit(): void;
    public function rollBack(): void;
    public function lastInsertId(): string;
}
```

`execute()`, `query()` un `scalar()` pieņem pozicionālus `list<scalar|null>` parametrus vietturiem `?`. `query()` atgriež `list<array<string, scalar|null>>`; gan PDO, gan mysqli adapteros vērtības tiek normalizētas vienādā formā — virkne vai null.

---

## Snapshotter

`Merql\Snapshot\Snapshotter` uzņem datubāzes stāvokļa momentuzņēmumu. Atšķirībā no fasādes, tas sniedz pilnīgu kontroli pār filtriem un neveic automātisku saglabāšanu.

### `__construct(DatabaseConnection $connection, ?Driver $driver = null, ?IdentityRuleSet $identityRules = null)`

Ja draiveris nav norādīts, tas tiek automātiski noteikts pēc savienojuma identitātes. Nododiet `IdentityRuleSet`, ja tabulu līmeņa rindu identitātei jāaizstāj shēmas noklusējuma iestatījumi.

### `capture(string $name, array $tables = [], array $filters = []): Snapshot`

Uzņemiet pašreizējā stāvokļa momentuzņēmumu. Norādiet tabulu nosaukumus, lai ierobežotu tvērumu. Norādiet filtru objektus, lai izslēgtu tabulas, kolonnas vai rindas.

```php
use Merql\Snapshot\Snapshotter;
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Filter\RowFilter;
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;

$snapshotter = new Snapshotter(
    $connection,
    identityRules: new IdentityRuleSet([
        'wp_options' => IdentityRule::natural(['option_name']),
    ]),
);

// All tables.
$snapshot = $snapshotter->capture('full');

// Specific tables with filters.
$snapshot = $snapshotter->capture('filtered', ['posts', 'users'], [
    TableFilter::exclude(['cache_*', 'sessions']),
    ColumnFilter::ignore(['updated_at', 'created_at']),
    RowFilter::create(fn(string $table, array $row) => $row['status'] !== 'trash'),
]);
```

### `captureAliased(string $name, array $physicalToCanonicalTableMap, array $filters = []): Snapshot`

Uzņemiet fizisko tabulu momentuzņēmumu, izmantojot kanoniskos tabulu nosaukumus. Izmantojiet to, ja vienai loģiskai tabulai dažādās vidēs ir dažādi fiziskie nosaukumi.

```php
$snapshot = $snapshotter->captureAliased('sandbox', [
    'wp_onumia_abcd_posts' => 'wp_posts',
    'wp_onumia_abcd_postmeta' => 'wp_postmeta',
]);
```

Iegūtajā momentuzņēmumā tiek saglabāti kanoniskie tabulu nosaukumi, vienlaikus saglabājot fizisko tabulu shēmu, konfigurētās identitātes kolonnas, rindu atslēgas un filtrus.

### `fromData(string $name, array $tableData): Snapshot` (statisks)

Izveidojiet momentuzņēmumu no neapstrādātiem datiem bez datubāzes savienojuma. Noder testēšanai vai datu importēšanai no cita avota.

```php
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;

$schema = new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']);

$snapshot = Snapshotter::fromData('test', [
    'posts' => new TableSnapshotData($schema, [
        ['id' => '1', 'title' => 'Hello'],
        ['id' => '2', 'title' => 'World'],
    ], ['id']),
]);
```

---

## Momentuzņēmums

`Merql\Snapshot\Snapshot` ir tikai lasāms vērtību objekts, kas attēlo pilnu datubāzes stāvokli.

```php
$snapshot->name;                    // string: snapshot identifier
$snapshot->tables;                  // array<string, TableSnapshot>
$snapshot->tableNames();            // list<string>
$snapshot->hasTable('posts');       // bool
$snapshot->getTable('posts');       // ?TableSnapshot
```

---

## TableSnapshot

`Merql\Snapshot\TableSnapshot` glabā vienas tabulas rindas, kontrolsummas un shēmu.

```php
$table = $snapshot->getTable('posts');

$table->schema;                    // TableSchema
$table->identityColumns;           // list<string>: columns used for row identity
$table->fingerprints;              // array<string, string>: row key to hash
$table->rows;                      // array<string, array<string, mixed>>

$table->rowKeys();                 // list<string>
$table->rowCount();                // int
$table->hasRow('42');              // bool
$table->getRow('42');              // ?array<string, mixed>
$table->getFingerprint('42');      // ?string
```

---

## TableSnapshotData

`Merql\Snapshot\TableSnapshotData` ir ievaddati funkcijai `Snapshotter::fromData()`.

```php
use Merql\Snapshot\TableSnapshotData;
use Merql\Schema\TableSchema;

$data = new TableSnapshotData(
    schema: new TableSchema('posts', ['id' => 'int', 'title' => 'varchar'], ['id']),
    rows: [
        ['id' => '1', 'title' => 'First Post'],
    ],
    identityColumns: ['id'],
);
```

---

## IdentityRule

`Merql\Identity\IdentityRule` nosaka kolonnas, ko izmanto rindu atslēgu izveidei.

```php
use Merql\Identity\IdentityRule;

$primary = IdentityRule::primary(['id']);
$natural = IdentityRule::natural(['option_name']);
$composite = IdentityRule::composite(['object_id', 'term_taxonomy_id']);
$content = IdentityRule::content(['name', 'value']);

$key = $natural->key(['option_name' => 'siteurl']);
// "siteurl"
```

### `forSchema(TableSchema $schema): IdentityRule` (statisks)

Nosakiet noklusējuma identitātes noteikumu no shēmas: primārā atslēga, pirmā unikālā atslēga un pēc tam satura rezerves variants.

### `toArray(): array`

Serializējiet noteikumu kā `['type' => string, 'columns' => list<string>]`.

### `fromArray(array $data): IdentityRule` (statisks)

Izveidojiet noteikumu no serializētiem datiem.

---

## IdentityRuleSet

`Merql\Identity\IdentityRuleSet` glabā tabulu līmeņa identitātes aizstājējus.

```php
use Merql\Identity\IdentityRule;
use Merql\Identity\IdentityRuleSet;

$rules = new IdentityRuleSet([
    'wp_options' => IdentityRule::natural(['option_name']),
]);

$rule = $rules->ruleFor($schema);
```

### `with(string $table, IdentityRule $rule): IdentityRuleSet`

Atgrieziet jaunu noteikumu kopu, kurai pievienots vienas tabulas aizstājējs.

### `conflictsFor(string $table, IdentityRule $rule, array $rows): array`

Atgrieziet `IdentityConflict` ierakstus, ja vairāku rindu atslēga atbilst vienam noteikumam.

### `toArray(): array`

Serializējiet tabulu noteikumus JSON drošā masīvā.

### `fromArray(array $data): IdentityRuleSet` (statisks)

Izveidojiet noteikumu kopu no serializētiem datiem.

---

## SnapshotStore

`Merql\Snapshot\SnapshotStore` saglabā un ielādē momentuzņēmumus kā JSON failus.

### `setDirectory(string $directory): void`

Iestatiet krātuves direktoriju. Noklusējums ir `.merql/snapshots`.

### `save(Snapshot $snapshot): void`

Ierakstiet momentuzņēmumu diskā.

### `load(string $name): Snapshot`

Ielādējiet momentuzņēmumu pēc nosaukuma. Ja tas nav atrasts, rodas `SnapshotException`.

### `exists(string $name): bool`

Pārbaudiet, vai pastāv momentuzņēmuma fails.

### `delete(string $name): void`

Noņemiet momentuzņēmuma failu.

---

## Izmaiņu noteicējs

`Merql\Diff\Differ` salīdzina divus momentuzņēmumus un izveido izmaiņu kopu.

### `diff(Snapshot $base, Snapshot $current): Changeset`

```php
use Merql\Diff\Differ;

$differ = new Differ();
$changeset = $differ->diff($baseSnapshot, $currentSnapshot);
```

### `valuesEqual(mixed $a, mixed $b): bool` (statisks)

Salīdziniet divas vērtības, pārvēršot tās virknēs. NULL tiek uzskatīts par atsevišķu vērtību: `NULL === NULL` ir patiess, `NULL === ''` ir aplams.

---

## Izmaiņu kopa

`Merql\Diff\Changeset` ir tikai lasāma rindu līmeņa darbību kolekcija.

```php
$changeset->inserts();             // list<RowInsert>
$changeset->updates();             // list<RowUpdate>
$changeset->deletes();             // list<RowDelete>
$changeset->isEmpty();             // bool
$changeset->count();               // int: total operations
$changeset->forTable('posts');     // Changeset: filtered to one table
```

---

## RowInsert, RowUpdate, RowDelete

Tikai lasāmi vērtību objekti, kas attēlo atsevišķas rindu izmaiņas.

### RowInsert

```php
$insert->table;                    // string
$insert->rowKey;                   // string
$insert->values;                   // array<string, mixed>
```

### RowUpdate

```php
$update->table;                    // string
$update->rowKey;                   // string
$update->columnDiffs;              // list<ColumnDiff>
$update->fullRow;                  // array<string, mixed>: complete current row
$update->columnDiffMap();          // array<string, ColumnDiff>: keyed by column name
```

### RowDelete

```php
$delete->table;                    // string
$delete->rowKey;                   // string
$delete->oldValues;                // array<string, mixed>: values before deletion
```

---

## ColumnDiff

`Merql\Diff\ColumnDiff` attēlo vienas kolonnas izmaiņu rindas atjauninājumā.

```php
$diff->column;                     // string
$diff->oldValue;                   // mixed
$diff->newValue;                   // mixed
```

---

## ThreeWayMerge

`Merql\Merge\ThreeWayMerge` ir galvenais sapludināšanas algoritms.

### `__construct(?CellMergeConfig $cellMergeConfig = null)`

Nododiet `CellMergeConfig`, lai iespējotu šūnu līmeņa sapludināšanu TEXT un JSON kolonnām.

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

// Default: column-level merge only.
$merge = new ThreeWayMerge();

// With cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
```

### `merge(Snapshot $base, Snapshot $ours, Snapshot $theirs): MergeResult`

Veiciet trīspusēju sapludināšanu. Aprēķina izmaiņu kopas iekšēji (no bāzes līdz mūsu pusei, no bāzes līdz viņu pusei) un tās sapludina.

```php
$result = $merge->merge($baseSnapshot, $oursSnapshot, $theirsSnapshot);
```

### `patch(Snapshot $base, Snapshot $changes): MergeResult`

Divpusējas sapludināšanas saīsinājums. Līdzvērtīgs `merge($base, $base, $changes)`.

### `schemaMismatches(): array`

Atgriež `SchemaException[]` par visām shēmas atšķirībām, kas konstatētas pēdējās sapludināšanas laikā (kolonnas, kas pievienotas vai noņemtas starp momentuzņēmumiem).

---

## MergeResult

`Merql\Merge\MergeResult` ir tikai lasāms vērtību objekts, kurā iekļauts sapludināšanas rezultāts.

```php
$result->isClean();                // bool: no conflicts
$result->operations();             // list<MergeOperation>
$result->conflicts();              // list<Conflict>
$result->operationCount();         // int
$result->conflictCount();          // int
$result->baseSnapshot();           // ?Snapshot
$result->schemaMismatches();       // list<SchemaException>
$result->hasSchemaMismatches();    // bool
```

---

## MergePlan

`Merql\Plan\MergePlan` ir serializējams sapludināšanas pārskatīšanas modelis.

```php
$plan->id;                        // string
$plan->baseSnapshot;              // string
$plan->oursSnapshot;              // string
$plan->theirsSnapshot;            // string
$plan->summary;                   // MergePlanSummary
$plan->changeGroups;              // list<MergePlanChangeGroup>
$plan->operations;                // list<MergePlanOperation>
$plan->conflicts;                 // list<MergePlanConflict>
$plan->schemaMismatches;          // list<string>
$plan->hash;                      // string
$plan->metadata;                  // array<string, mixed>
```

Izmantojiet `MergePlanSerializer::toJson()` un `::fromJson()`, lai nodrošinātu stabilu JSON turp un atpakaļ pārveidi.

## Atlases

`ChangeGroupSelection` glabā iestudēto izmaiņu grupu ID. Pārvērtiet to par `OperationSelection`, veidojot atlasītos sapludināšanas rezultātus vai atgriešanas plānus.

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;

$selection = ChangeGroupSelection::fromIds([$groupId]);
$operationSelection = $selection->toOperationSelection($plan);

$selected = (new SelectedMergeResultFactory())
    ->fromOperationSelection($plan, $operationSelection);
```

## RollbackPlan

`Merql\Rollback\RollbackPlan` glabā apgrieztās darbības un novirzes sagaidāmās vērtības atlasītai piemērošanai.

```php
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Rollback\RollbackPlanSerializer;

$rollback = (new RollbackPlanBuilder())->build(
    'rollback-1',
    $plan,
    $operationSelection,
    $liveBeforeRows,
);

$json = RollbackPlanSerializer::toJson($rollback);
```

Pirms atgriešanas piemērošanas izmantojiet `RollbackDrift`, pēc tam `RollbackPlanApplier`, lai izpildītu apgrieztās darbības.

---

## MergeOperation

`Merql\Merge\MergeOperation` ir viena atrisināta darbība no sapludināšanas.

```php
$op->type;                         // string: 'insert' | 'update' | 'delete'
$op->table;                        // string
$op->rowKey;                       // string
$op->values;                       // array<string, mixed>
$op->source;                       // string: 'ours' | 'theirs' | 'merged'
```

Tipu konstantes:

```php
MergeOperation::TYPE_INSERT;       // 'insert'
MergeOperation::TYPE_UPDATE;       // 'update'
MergeOperation::TYPE_DELETE;       // 'delete'
```

---

## Konflikts

`Merql\Merge\Conflict` attēlo neatrisinātu sapludināšanas konfliktu.

```php
$conflict->table();                // string
$conflict->rowKey();               // string
$conflict->type();                 // string: 'update_update' | 'update_delete' | 'delete_update' | 'insert_insert'
$conflict->column();               // ?string (null for structural conflicts)
$conflict->oursValue();            // mixed
$conflict->theirsValue();          // mixed
$conflict->baseValue();            // mixed
$conflict->primaryKey(['id']);     // array<string, string>: decoded from rowKey
```

### Konfliktu veidi

| Veids | Nozīme |
|---|---|
| `update_update` | Abas puses mainīja vienu un to pašu kolonnu uz dažādām vērtībām |
| `update_delete` | Mūsu puse atjaunināja rindu, ko viņu puse dzēsa |
| `delete_update` | Mūsu puse dzēsa rindu, ko viņu puse atjaunināja |
| `insert_insert` | Abas puses ievietoja rindu ar vienu un to pašu primāro atslēgu |

---

## ConflictResolver

`Merql\Merge\ConflictResolver` visiem sapludināšanas rezultāta konfliktiem piemēro atrisināšanas politiku.

### `resolve(MergeResult $result, ConflictPolicy $policy): MergeResult` (statisks)

Atgriež jaunu `MergeResult`, kurā konflikti ir atrisināti saskaņā ar politiku.

```php
use Merql\Merge\ConflictResolver;
use Merql\Merge\ConflictPolicy;

// Accept all "theirs" values when conflicts arise.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);

// Accept all "ours" values.
$resolved = ConflictResolver::resolve($result, ConflictPolicy::OursWins);

// Return unchanged (for manual inspection).
$resolved = ConflictResolver::resolve($result, ConflictPolicy::Manual);
```

---

## ConflictPolicy

`Merql\Merge\ConflictPolicy` ir backed enum ar trim variantiem.

```php
use Merql\Merge\ConflictPolicy;

ConflictPolicy::OursWins;          // Resolve all conflicts in favor of "ours"
ConflictPolicy::TheirsWins;        // Resolve all conflicts in favor of "theirs"
ConflictPolicy::Manual;            // Leave conflicts unresolved for manual handling
```

---

## ColumnMerge

`Merql\Merge\ColumnMerge` veic vienas rindas kolonnu līmeņa sapludināšanu. Iekšēji to izmanto `ThreeWayMerge`.

### `merge(string $table, string $rowKey, array $base, array $ours, array $theirs, ?CellMergeConfig $cellMergeConfig = null, array $columnTypes = []): array` (statisks)

Atgriež `['values' => array, 'conflicts' => list<Conflict>]`.

```php
use Merql\Merge\ColumnMerge;

$result = ColumnMerge::merge(
    'posts',
    '42',
    ['title' => 'Hello', 'status' => 'draft'],     // base
    ['title' => 'Hello', 'status' => 'published'],  // ours
    ['title' => 'New Title', 'status' => 'draft'],   // theirs
);

// $result['values'] = ['title' => 'New Title', 'status' => 'published']
// $result['conflicts'] = [] (clean merge)
```

---

## Applier

`Merql\Apply\Applier` izpilda sapludināšanas rezultātu datubāzē.

### `__construct(DatabaseConnection $connection, ?Driver $driver = null)`

### `apply(MergeResult $result, ?Snapshot $base = null): ApplyResult`

Izpilda visas darbības vienā transakcijā. Ja rodas kļūda, transakciju atceļ un par to ziņo laukā `ApplyResult::errors()`, nevis rada izņēmumu. Ja rezultātā ir neatrisināti konflikti, rodas `ConflictException`.

```php
use Merql\Apply\Applier;

$applier = new Applier($connection);
$applied = $applier->apply($result);

echo "Rows affected: {$applied->rowsAffected()}\n";
if ($applied->hasErrors()) {
    print_r($applied->errors());
}
```

Ārējo atslēgu secība tiek apstrādāta automātiski: ievietošanas darbības tiek sakārtotas atkarību secībā, bet dzēšanas darbības — apgrieztā atkarību secībā.

---

## ApplyResult

`Merql\Apply\ApplyResult` ir tikai lasāms vērtību objekts, ko atgriež applier.

```php
$applied->rowsAffected();         // int
$applied->errors();               // list<string>
$applied->hasErrors();            // bool
```

Pēc atceltas piemērošanas `rowsAffected()` joprojām uzskaita priekšrakstus, kas tika izpildīti pirms kļūmes ([#4](https://github.com/inline0/merql/issues/4)). Pirms paļaušanās uz šo skaitu pārbaudiet `hasErrors()`.

---

## DryRun

`Merql\Apply\DryRun` ģenerē cilvēkam lasāmus SQL, neko neizpildot.

### `generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array` (statisks)

Atgriež SQL virkņu sarakstu ar ievietotām vērtībām (tikai attēlošanai).

```php
use Merql\Apply\DryRun;

$statements = DryRun::generate($result);

foreach ($statements as $sql) {
    echo "{$sql};\n";
}

// Output:
// INSERT INTO `posts` (`id`, `title`) VALUES ('10', 'New Post');
// UPDATE `posts` SET `title` = 'Updated' WHERE `id` = '5';
// DELETE FROM `comments` WHERE `id` = '3';
```

---

## SqlGenerator

`Merql\Apply\SqlGenerator` ģenerē parametrizētus SQL priekšrakstus. Iekšēji tos izmanto gan `Applier`, gan `DryRun`.

### `generate(MergeResult $result, ?Snapshot $base = null, array $fkDependencies = [], ?Driver $driver = null): array` (statisks)

Atgriež `list<array{sql: string, params: list<scalar|null>}>`.

```php
use Merql\Apply\SqlGenerator;

$statements = SqlGenerator::generate($result);

foreach ($statements as $stmt) {
    $connection->execute($stmt['sql'], $stmt['params']);
}
```

---

## CellMergeConfig

`Merql\CellMerge\CellMergeConfig` konfigurē šūnu līmeņa sapludināšanas stratēģijas noteiktām kolonnām vai kolonnu tipiem.

### `auto(): self` (statisks)

Ērta izveides funkcija, kas piešķir `TextCellMerger` TEXT/LONGTEXT kolonnām un `JsonCellMerger` kolonnām JSON.

```php
use Merql\CellMerge\CellMergeConfig;

$config = CellMergeConfig::auto();
```

### `forColumn(string $column, CellMerger $merger): self`

Piešķiriet sapludinātāju konkrētai kolonnai. Izmantojiet `table.column`, lai norādītu pilnībā kvalificētus nosaukumus, vai tikai `column` visām tabulām.

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

$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('description', new TextCellMerger());
```

### `forType(string $typePattern, CellMerger $merger): self`

Piešķiriet sapludinātāju visām kolonnām, kas atbilst tipa paraugam (reģistrnejutīga apakšvirknes atbilstība).

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

$config = (new CellMergeConfig())
    ->forType('json', new JsonCellMerger());
```

### `getMerger(string $table, string $column, string $columnType = ''): CellMerger`

Atrodiet sapludinātāju norādītajai tabulai, kolonnai un tipam. Prioritāte: kvalificēts kolonnas nosaukums, nekvalificēts kolonnas nosaukums, tipa paraugs, noklusējums (opaque).

---

## CellMerger (interfeiss)

`Merql\CellMerge\CellMerger` ir interfeiss šūnu līmeņa sapludināšanas stratēģijām.

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

interface CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult;
}
```

Ir pieejamas trīs iebūvētas implementācijas.

### TextCellMerger

`Merql\CellMerge\TextCellMerger` sapludina vairākas teksta rindas, izmantojot trīspusēju rindu pa rindai atšķirību salīdzināšanu (to pašu algoritmu, ko git izmanto failu saturam). Ja abas puses rediģē dažādas rindas, sapludināšana notiek bez konfliktiem. Ja abas puses atšķirīgi rediģē vienu un to pašu rindu, tiek ziņots par konfliktu.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
$result = $merger->merge(
    "line 1\nline 2\nline 3",
    "line 1\nline 2 modified\nline 3",
    "line 1\nline 2\nline 3 modified",
);
// $result->clean === true
// $result->value === "line 1\nline 2 modified\nline 3 modified"
```

### JsonCellMerger

`Merql\CellMerge\JsonCellMerger` apvieno JSON vērtības, neatkarīgi salīdzinot augšējā līmeņa atslēgas. Katra atslēga tiek apvienota, izmantojot trīsvirzienu loģiku. Iekļautie objekti tiek salīdzināti kā necaurskatāmas JSON virknes (tie netiek apvienoti rekursīvi).

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
$result = $merger->merge(
    '{"a": 1, "b": 2}',
    '{"a": 10, "b": 2}',        // changed "a"
    '{"a": 1, "b": 20}',         // changed "b"
);
// $result->clean === true
// $result->value === '{"a":10,"b":20}'
```

### OpaqueCellMerger

`Merql\CellMerge\OpaqueCellMerger` ir noklusējuma izvēle. Tā uztver vērtības kā necaurskatāmas virknes un vienmēr ziņo par konfliktu, ja tās atšķiras.

---

## CellMergeResult

`Merql\CellMerge\CellMergeResult` ir visu šūnu apvienotāju atgriežamais tips.

```php
$result->clean;                    // bool: true if merge succeeded
$result->value;                    // mixed: merged value (or ours on conflict)
$result->conflicts;                // int: number of conflicts

// Factory methods.
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // conflict with count
```

---

## TableSchema

`Merql\Schema\TableSchema` ir tikai lasāms tabulas struktūras attēlojums.

```php
use Merql\Schema\TableSchema;

$schema = new TableSchema(
    name: 'posts',
    columns: ['id' => 'int', 'title' => 'varchar(255)', 'content' => 'text'],
    primaryKey: ['id'],
    uniqueKeys: [['slug']],
);

$schema->name;                     // string
$schema->columns;                  // array<string, string>: name to type
$schema->primaryKey;               // list<string>
$schema->uniqueKeys;               // list<list<string>>
$schema->hasPrimaryKey();          // bool
$schema->hasUniqueKeys();          // bool
$schema->columnNames();            // list<string>
```

---

## Draiveru sistēma

merql izmanto paplašināmu draiveru sistēmu, lai abstrahētu datubāzēm specifiskās darbības. Draiveri tiek automātiski noteikti pēc savienojuma draivera identifikatora.

### Draiveris (saskarne)

`Merql\Driver\Driver` definē līgumu, ko īsteno visi datubāzu draiveri.

```php
interface Driver
{
    public function quoteIdentifier(string $name): string;
    public function listTables(DatabaseConnection $connection): array;
    public function readSchema(DatabaseConnection $connection, string $table): TableSchema;
    public function readForeignKeys(DatabaseConnection $connection): array;
    public function selectAll(string $table): string;
}
```

Iebūvētās realizācijas: `MysqlDriver` un `SqliteDriver`.

### DriverFactory

`Merql\Driver\DriverFactory` automātiski nosaka draiveri pēc savienojuma.

```php
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);
```

Reģistrējiet pielāgotu draiveri citām datubāzēm:

```php
DriverFactory::register('pgsql', MyPostgresDriver::class);
```

---

## Filtri

### TableFilter

`Merql\Filter\TableFilter` iekļauj vai izslēdz tabulas, izmantojot glob modeļus.

```php
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['cache_*', 'sessions', 'temp_*']);
$filter = TableFilter::include(['posts', 'users', 'comments']);
```

### ColumnFilter

`Merql\Filter\ColumnFilter` noņem noteiktas kolonnas no momentuzņēmumiem.

```php
use Merql\Filter\ColumnFilter;

$filter = ColumnFilter::ignore(['updated_at', 'created_at', 'modified_date']);
```

### RowFilter

`Merql\Filter\RowFilter` filtrē rindas, izmantojot predikāta funkciju.

```php
use Merql\Filter\RowFilter;

$filter = RowFilter::create(function (string $table, array $row): bool {
    // Exclude trashed posts.
    if ($table === 'posts' && ($row['status'] ?? '') === 'trash') {
        return false;
    }
    return true;
});
```

---

## Izņēmumi

### SnapshotException

`Merql\Exceptions\SnapshotException` tiek izmests, ja momentuzņēmumu nevar atrast vai tā nosaukums nav derīgs.

```php
SnapshotException::notFound('baseline', '/path/to/baseline.json');
```

### MergeException

`Merql\Exceptions\MergeException` tiek izmests, ja apvienošanai trūkst nepieciešamā momentuzņēmuma.

```php
MergeException::snapshotRequired('base');
```

### ConflictException

`Merql\Exceptions\ConflictException` tiek izmests, mēģinot lietot apvienošanas rezultātu, kurā ir neatrisināti konflikti.

```php
ConflictException::unresolved(3);
// Message: "Cannot apply merge with 3 unresolved conflict(s)"
```

### SchemaException

`Merql\Exceptions\SchemaException` tiek izmests (vai apkopots), ja momentuzņēmumu shēmas atšķiras.

```php
SchemaException::mismatch('posts', 'column "tags" exists in current but not in base');
```

Shēmu neatbilstības neaptur apvienošanu. Tās tiek apkopotas un apskatei ir pieejamas, izmantojot `$result->schemaMismatches()`.
