
# PHP API nuorodinis vadovas

Išsamus visų viešų merql klasių ir metodų vadovas.

## Merql (fasadinė klasė)

`Merql\Merql` yra statinis bendrųjų operacijų pradžios taškas. Jis apgaubia žemesnio lygio klases ir automatiškai tvarko momentinių kopijų išsaugojimą.

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

Inicializuokite fasadinę klasę su duomenų bazės ryšiu. Tai būtina iškviesti prieš `snapshot()`, `apply()` arba `applyGuarded()`; iškvieskite dar kartą, kad perjungtumėte duomenų bazes, pavyzdžiui, kad sukurtumėte mūsų ir jų momentines kopijas iš dviejų skirtingų kopijų. Jei nenurodytas `$driver`, iškviečiamas `DriverFactory::create()`, kuris su kitais nei MySQL ir SQLite varikliais išmeta išimtį, nebent užregistruota tvarkyklė. Nurodykite tvarkyklę, kad ji pakeistų automatinį aptikimą, arba tapatybės taisyklių rinkinį, kad kuriant momentinę kopiją būtų pakeistos lentelių įrašų tapatybės taisyklės.

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

Užfiksuokite dabartinę duomenų bazės būseną ir išsaugokite ją diske. Nurodykite lentelių pavadinimų masyvą, jei norite sukurti tik konkrečių lentelių momentinę kopiją; jei norite visų lentelių, parametro nenurodykite.

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

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

Įkelkite dvi nurodytas momentines kopijas ir apskaičiuokite jų pakeitimų rinkinį. `init()` nereikia, nes operacija atliekama su išsaugotomis momentinėmis kopijomis.

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

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

Trijų krypčių trijų nurodytų momentinių kopijų sujungimas. Grąžinamas rezultatas su operacijomis be konfliktų ir galimais konfliktais. Sujungiama stulpelių lygmeniu: fasadinė klasė sukuria `ThreeWayMerge` be `CellMergeConfig`, todėl langelių lygmens sujungimas neatliekamas. Tam tiesiogiai naudokite [`ThreeWayMerge`](#threewaymerge).

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

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

Dviejų krypčių sujungimas: pritaikyti pakeitimus bazei. Atitinka `merge($base, $base, $changes)`. Visada gaunamas švarus rezultatas, nes nėra vienalaikių redagavimų. Kaip ir `merge()`, sujungiama tik stulpelių lygmeniu.

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

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

Vykdykite švarų sujungimo rezultatą su duomenų baze, perduota `init()`. Reikalingas `init()`. Jei liko neišspręstų konfliktų, išmetama `ConflictException`.

Vykdomos visos rezultato operacijos, todėl visas `merge()` rezultatas švariai pritaikomas tik duomenų bazei, kurioje tebėra bazinė būsena. Jei duomenų bazėje jau yra viena pusė, pritaikykite trūkstamų operacijų pasirinkimą; žr. [Guarded Apply](https://merql.inline0.dev/lt/docs/apply/guarded).

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

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

Sukurkite UI parengtą sujungimo planą iš trijų nurodytų momentinių kopijų. Plane pateikiami stabilūs operacijų ID, pakeitimų grupių ID, operacijų kontekstas, konfliktai, schemų neatitikimai, suvestinė, metaduomenys ir plano maiša. Kaip ir `merge()`, sujungiama stulpelių lygmeniu; langelių lygmens sujungimui perduokite langelių sujungimo rezultatą į `MergePlanBuilder::build()`.

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

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

Pritaikykite švarų sujungimo rezultatą naudodami optimistines aktyvių įrašų išankstines sąlygas. Atnaujinant ir ištrinant, dabartiniai aktyvūs įrašai turi sutapti su `$expectedLive`; įterpiant, tikrinama, ar tikslinės tapatybės dar nėra. `$expectedLive` yra tikslinės duomenų bazės momentinė kopija (jų pusės, kai mūsų pusė perkeliama į duomenų bazę, iš kurios sukurta jų pusės kopija). Jei bet kuris sakinys paveikia nulį įrašų, operacija atšaukiama.

```php
use Merql\Snapshot\SnapshotStore;

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

### `reset(): void`

Išvalykite vidinius ryšio ir Snapshotter egzempliorius. Daugiausia naudojama testuose būsenai iš naujo nustatyti tarp paleidimų.

---

## Ryšys

`Merql\Connection` sukuria ryšio adapterius su tinkamais numatytaisiais nustatymais. PDO ryšiuose klaidoms naudojamos išimtys, įjungtas asociatyvus išrinkimo režimas, išjungti emuliuojami paruošti sakiniai, o išrinktos reikšmės pateikiamos eilutėmis. mysqli adapteris normalizuoja išrinktas reikšmes, kad jų forma taip pat būtų eilutė arba 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`

Sukuria SQLite ryšį. Automatiškai įjungia išorinius raktus per `PRAGMA foreign_keys = ON`.

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

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

Prisijunkite naudodami bet kurią PDO DSN eilutę. merql turi tik `mysql` ir `sqlite` tvarkykles; su kitomis DSN užmezgamas ryšys, tačiau `Merql::init()`, `Snapshotter` ir pritaikymo įrankiai išmeta `Unsupported database driver`, nebent tvarkyklė užregistruota naudojant `DriverFactory::register()`.

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

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

Apgaubia esamą PDO egzempliorių.

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

Apgaubia esamą mysqli egzempliorių.

### Adapterio sutartis

Visi adapteriai įgyvendina `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()` ir `scalar()` priima pozicinius `list<scalar|null>` parametrus, skirtus `?` vietaženkliams. `query()` grąžina `list<array<string, scalar|null>>`; PDO ir mysqli adapteriuose reikšmės normalizuojamos į vienodą eilutės arba null formą.

---

## Snapshotter

`Merql\Snapshot\Snapshotter` užfiksuoja duomenų bazės būseną. Kitaip nei fasadinė klasė, jis suteikia visišką filtrų valdymą ir automatiškai neišsaugo duomenų.

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

Jei tvarkyklė nenurodyta, ji automatiškai aptinkama pagal ryšio tapatybę. Nurodykite `IdentityRuleSet`, jei lentelės įrašų tapatybė turi pakeisti schemoje numatytąją.

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

Užfiksuokite dabartinę būseną. Nurodykite lentelių pavadinimus, kad apribotumėte apimtį. Nurodykite filtrų objektus, kad neįtrauktumėte lentelių, stulpelių arba įrašų.

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

Užfiksuokite fizines lenteles naudodami kanoninius lentelių pavadinimus. Naudokite tai, kai ta pati loginė lentelė skirtingose aplinkose turi skirtingus fizinius pavadinimus.

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

Gautoje momentinėje kopijoje išsaugomi kanoniniai lentelių pavadinimai, fizinė lentelių schema, sukonfigūruoti tapatybės stulpeliai, įrašų raktai ir filtrai.

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

Sukurkite momentinę kopiją iš neapdorotų duomenų be duomenų bazės ryšio. Naudinga testuojant arba importuojant duomenis iš kito šaltinio.

```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']),
]);
```

---

## Momentinė kopija

`Merql\Snapshot\Snapshot` yra tik skaitymui skirtas reikšmės objektas, vaizduojantis visą duomenų bazės būseną.

```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` saugo vienos lentelės įrašus, kontrolines sumas ir schemą.

```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` yra įvesties duomenys, skirti `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` apibrėžia stulpelius, naudojamus įrašų raktams sudaryti.

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

Pagal schemą parenka numatytąją tapatybės taisyklę: pirminis raktas, pirmasis unikalus raktas, o tada turinio atsarginis variantas.

### `toArray(): array`

Nuosekliai išsaugo taisyklę kaip `['type' => string, 'columns' => list<string>]`.

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

Sukuria taisyklę iš nuosekliai išsaugotų duomenų.

---

## IdentityRuleSet

`Merql\Identity\IdentityRuleSet` saugo konkrečioms lentelėms skirtus tapatybės pakeitimus.

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

Grąžina naują taisyklių rinkinį su pridėtu vienos lentelės pakeitimu.

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

Grąžina `IdentityConflict` įrašus, kai pagal taisyklę keli įrašai sukuria tą patį raktą.

### `toArray(): array`

Nuosekliai išsaugo lentelių taisykles kaip JSON suderinamą masyvą.

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

Sukuria taisyklių rinkinį iš nuosekliai išsaugotų duomenų.

---

## SnapshotStore

`Merql\Snapshot\SnapshotStore` išsaugo momentines kopijas ir įkelia jas kaip JSON failus.

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

Nustato saugojimo katalogą. Numatytoji reikšmė yra `.merql/snapshots`.

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

Įrašo momentinę kopiją į diską.

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

Įkelia momentinę kopiją pagal pavadinimą. Neradus išmeta `SnapshotException`.

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

Patikrina, ar yra momentinės kopijos failas.

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

Pašalina momentinės kopijos failą.

---

## Skirtumų nustatymo įrankis

`Merql\Diff\Differ` palygina dvi momentines kopijas ir sukuria pakeitimų rinkinį.

### `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` (statinis)

Palygina dvi reikšmes, paversdamas jas eilutėmis. NULL laikoma atskira reikšme: `NULL === NULL` yra true, `NULL === ''` yra false.

---

## Pakeitimų rinkinys

`Merql\Diff\Changeset` yra tik skaitymui skirta įrašų lygmens operacijų 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

Tik skaitymui skirti reikšmės objektai, vaizduojantys atskirus įrašų pakeitimus.

### 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` vaizduoja vieno stulpelio pakeitimą atnaujinant įrašą.

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

---

## ThreeWayMerge

`Merql\Merge\ThreeWayMerge` yra pagrindinis sujungimo algoritmas.

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

Nurodykite `CellMergeConfig`, kad įjungtumėte TEXT ir JSON stulpelių sujungimą langelių lygmeniu.

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

Atlieka trijų krypčių sujungimą. Viduje apskaičiuoja pakeitimų rinkinius (nuo bazės iki mūsų ir nuo bazės iki jų) ir juos sujungia.

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

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

Dviejų krypčių sujungimo spartusis variantas. Atitinka `merge($base, $base, $changes)`.

### `schemaMismatches(): array`

Grąžina `SchemaException[]`, kai paskutinio sujungimo metu aptinkami schemos skirtumai (tarp momentinių kopijų pridėti arba pašalinti stulpeliai).

---

## MergeResult

`Merql\Merge\MergeResult` yra tik skaitymui skirtas reikšmės objektas, kuriame pateikiamas sujungimo rezultatas.

```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` yra nuosekliai išsaugomas sujungimo peržiūros 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>
```

Naudokite `MergePlanSerializer::toJson()` ir `::fromJson()`, kad būtų užtikrintas stabilus JSON grįžtamasis nuoseklus išsaugojimas.

## Pasirinkimai

`ChangeGroupSelection` saugo parengtų pakeitimų grupių ID. Paverskite jį į `OperationSelection`, kai kuriate pasirinktus sujungimo rezultatus arba atšaukimo planus.

```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` saugo atvirkštines operacijas ir pasirinkto pritaikymo nukrypimų lūkesčius.

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

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

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

Prieš taikydami atšaukimą, naudokite `RollbackDrift`, tada vykdykite atvirkštines operacijas su `RollbackPlanApplier`.

---

## MergeOperation

`Merql\Merge\MergeOperation` yra viena išspręsta sujungimo operacija.

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

Tipų konstantos:

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

---

## Konfliktas

`Merql\Merge\Conflict` vaizduoja neišspręstą sujungimo konfliktą.

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

### Konfliktų tipai

| Tipas | Reikšmė |
|---|---|
| `update_update` | Abi pusės pakeitė tą patį stulpelį į skirtingas reikšmes |
| `update_delete` | Mūsų pusė atnaujino įrašą, kurį jų pusė ištrynė |
| `delete_update` | Mūsų pusė ištrynė įrašą, kurį jų pusė atnaujino |
| `insert_insert` | Abi pusės įterpė įrašą su tuo pačiu pirminiu raktu |

---

## ConflictResolver

`Merql\Merge\ConflictResolver` pritaiko sprendimo politiką visiems sujungimo rezultato konfliktams.

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

Grąžina naują `MergeResult`, kuriame konfliktai išspręsti pagal politiką.

```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` yra backed enum su trimis variantais.

```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` atlieka vieno įrašo stulpelių sujungimo logiką. Viduje naudojamas `ThreeWayMerge`.

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

Grąžina `['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)
```

---

## Pritaikymo įrankis

`Merql\Apply\Applier` pritaiko sujungimo rezultatą duomenų bazei.

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

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

Vykdo visas operacijas vienoje transakcijoje. Įvykus klaidai, ją atšaukia ir pateikia `ApplyResult::errors()`, užuot išmetęs išimtį. Jei rezultate yra neišspręstų konfliktų, išmeta `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());
}
```

Išorinių raktų tvarka tvarkoma automatiškai: įterpimai rikiuojami pagal priklausomybes, o ištrynimai atliekami atvirkštine priklausomybių tvarka.

---

## ApplyResult

`Merql\Apply\ApplyResult` yra tik skaitymui skirtas reikšmės objektas, kurį grąžina pritaikymo įrankis.

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

Net ir atšaukus pritaikymą, `rowsAffected()` skaičiuoja iki klaidos įvykdytus sakinius ([#4](https://github.com/inline0/merql/issues/4)). Prieš pasikliaudami skaičiumi, patikrinkite `hasErrors()`.

---

## DryRun

`Merql\Apply\DryRun` sugeneruoja žmogui skaitomus SQL, nieko nevykdydamas.

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

Grąžina SQL eilučių sąrašą su įterptomis reikšmėmis (tik rodymo tikslais).

```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` generuoja parametrizuotus SQL sakinius. Viduje naudojamas ir `Applier`, ir `DryRun`.

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

Grąžina `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` konfigūruoja langelių sujungimo strategijas konkretiems stulpeliams arba stulpelių tipams.

### `auto(): self` (statinis)

Patogi gamyklinė funkcija, priskirianti `TextCellMerger` TEXT/LONGTEXT stulpeliams ir `JsonCellMerger` stulpeliams JSON.

```php
use Merql\CellMerge\CellMergeConfig;

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

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

Priskiria sujungimo įrankį konkrečiam stulpeliui. Naudokite `table.column` kvalifikuotiems pavadinimams arba tik `column` visoms lentelėms.

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

Priskiria sujungimo įrankį visiems stulpeliams, atitinkantiems tipo šabloną (didžiųjų ir mažųjų raidžių neskirianti poeilutės atitiktis).

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

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

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

Ieško sujungimo įrankio pagal lentelę, stulpelį ir tipą. Prioritetas: kvalifikuotas stulpelio pavadinimas, nekvalifikuotas stulpelio pavadinimas, tipo šablonas, numatytasis (nepermatomas).

---

## CellMerger (sąsaja)

`Merql\CellMerge\CellMerger` yra sąsaja, skirta langelių lygmens sujungimo strategijoms.

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

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

Pateikiamos trys integruotos realizacijos.

### TextCellMerger

`Merql\CellMerge\TextCellMerger` sujungia kelių eilučių tekstą naudodamas trijų krypčių eilučių palyginimą (tą patį algoritmą, kurį git naudoja failų turiniui). Kai abi pusės redaguoja skirtingas eilutes, sujungimas atliekamas be konfliktų. Kai abi pusės skirtingai redaguoja tą pačią eilutę, pranešama apie konfliktą.

```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` sujungia JSON reikšmes, nepriklausomai palygindamas aukščiausio lygmens raktus. Kiekvienas raktas sujungiamas taikant trijų krypčių logiką. Įdėtiniai objektai lyginami kaip nepermatomos JSON eilutės (rekursyviai nejungiami).

```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` yra numatytoji strategija. Ji laiko reikšmes nepermatomomis eilutėmis ir visada praneša apie konfliktą, kai jos skiriasi.

---

## CellMergeResult

`Merql\CellMerge\CellMergeResult` yra visų langelių sujungimo funkcijų grąžinamas tipas.

```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` yra tik skaitoma lentelės struktūros reprezentacija.

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

---

## Tvarkyklių sistema

merql naudoja prijungiamų tvarkyklių sistemą, kad abstrahuotų konkrečiai duomenų bazei skirtas operacijas. Tvarkyklės automatiškai aptinkamos pagal prisijungimo tvarkyklės tapatybę.

### Tvarkyklė (sąsaja)

`Merql\Driver\Driver` apibrėžia sąsają, kurią įgyvendina visos duomenų bazių tvarkyklės.

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

Integruotos realizacijos: `MysqlDriver` ir `SqliteDriver`.

### DriverFactory

`Merql\Driver\DriverFactory` automatiškai aptinka prisijungimo tvarkyklę.

```php
use Merql\Driver\DriverFactory;

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

Užregistruokite pasirinktinę tvarkyklę kitoms duomenų bazėms:

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

---

## Filtrai

### TableFilter

`Merql\Filter\TableFilter` įtraukia arba neįtraukia lenteles pagal glob šablonus.

```php
use Merql\Filter\TableFilter;

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

### ColumnFilter

`Merql\Filter\ColumnFilter` pašalina konkrečius stulpelius iš momentinių kopijų.

```php
use Merql\Filter\ColumnFilter;

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

### RowFilter

`Merql\Filter\RowFilter` filtruoja eilutes naudodama predikato funkciją.

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

---

## Išimtys

### SnapshotException

`Merql\Exceptions\SnapshotException` išmetama, kai momentinės kopijos nepavyksta rasti arba jos pavadinimas netinkamas.

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

### MergeException

`Merql\Exceptions\MergeException` išmetama, kai sujungimui trūksta privalomos momentinės kopijos.

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

### ConflictException

`Merql\Exceptions\ConflictException` išmetama bandant pritaikyti sujungimo rezultatą, kuriame yra neišspręstų konfliktų.

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

### SchemaException

`Merql\Exceptions\SchemaException` išmetama (arba surenkama), kai momentinių kopijų schemos skiriasi.

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

Schemos neatitikimai nesustabdo sujungimo. Jie surenkami ir juos galima peržiūrėti per `$result->schemaMismatches()`.
