
# Referință PHP API

Referință completă pentru toate clasele și metodele publice din merql.

## Merql (fațadă)

`Merql\Merql` este punctul de intrare static pentru operațiile uzuale. Încapsulează clasele de nivel inferior și gestionează automat persistența instantaneelor.

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

Inițializați fațada cu o conexiune la baza de date. Trebuie apelată înainte de
`snapshot()`, `apply()` sau `applyGuarded()`; apelați-o din nou pentru a schimba baza de date,
de exemplu pentru a crea instantanee pentru partea noastră și partea lor din două copii diferite. În lipsa unui
`$driver`, apelează `DriverFactory::create()`, care generează o excepție pentru alte motoare decât
MySQL și SQLite, dacă nu este înregistrat un driver. Transmiteți un driver pentru a înlocui detectarea automată sau un set de reguli de identitate pentru a înlocui identitatea rândurilor per tabel în timpul capturării instantaneului.

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

Capturați starea curentă a bazei de date și salvați-o pe disc. Transmiteți un tablou cu numele tabelelor pentru a crea instantanee doar pentru anumite tabele sau omiteți-l pentru toate tabelele.

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

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

Încărcați două instantanee cu nume și calculați setul de modificări dintre ele. Nu necesită `init()`, deoarece folosește instantanee persistate.

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

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

Îmbinare în trei direcții a trei instantanee cu nume. Returnează un rezultat care conține operații fără conflicte și eventuale conflicte. Îmbină la nivel de coloană: fațada construiește `ThreeWayMerge` fără un `CellMergeConfig`, deci îmbinarea la nivel de celulă nu se aplică. Pentru aceasta, folosiți direct [`ThreeWayMerge`](#threewaymerge).

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

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

Îmbinare în două direcții: aplică modificările peste baza inițială. Echivalentă cu `merge($base, $base, $changes)`. Produce întotdeauna un rezultat fără conflicte, deoarece nu există editări concurente. Doar la nivel de coloană, la fel ca `merge()`.

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

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

Execută un rezultat de îmbinare fără conflicte asupra bazei de date transmise către `init()`. Necesită `init()`. Generează `ConflictException` dacă rămân conflicte nerezolvate.

Fiecare operație din rezultat este executată, așa că rezultatul complet al `merge()` se aplică fără probleme doar unei baze de date care încă păstrează starea inițială. Pentru o bază de date care conține deja una dintre părți, aplicați operațiile care îi lipsesc; consultați [Aplicare cu verificări](https://merql.inline0.dev/ro/docs/apply/guarded).

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

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

Construiți un plan de îmbinare compatibil cu UI din trei instantanee cu nume. Planul include ID-uri stabile ale operațiilor, ID-uri ale grupurilor de modificări, contextul operațiilor, conflicte, nepotriviri de schemă, un rezumat, metadate și un hash al planului. Îmbinarea este la nivel de coloană, ca în `merge()`; transmiteți un rezultat îmbinat la nivel de celulă către `MergePlanBuilder::build()` pentru îmbinarea la nivel de celulă.

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

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

Aplicați un rezultat de îmbinare fără conflicte, cu precondiții optimiste pentru rândurile curente. Actualizările și
ștergerile necesită ca rândurile curente să corespundă cu `$expectedLive`; inserările necesită ca
identitatea țintă să nu existe deja. `$expectedLive` este un instantaneu al
bazei de date țintă (partea lor, când promovați partea noastră în baza de date din care a fost
capturată partea lor). Orice instrucțiune care afectează zero rânduri anulează tranzacția.

```php
use Merql\Snapshot\SnapshotStore;

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

### `reset(): void`

Șterge instanțele interne de conexiune și Snapshotter. Folosit în principal în teste pentru a reseta starea între rulări.

---

## Conexiune

`Merql\Connection` construiește adaptoare de conexiune cu valori implicite adecvate. Conexiunile PDO folosesc excepții la erori, modul de extragere asociativă, fără pregătiri simulate și valori extrase convertite în șiruri. Adaptorul mysqli normalizează valorile extrase la aceeași formă șir sau 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`

Creează o conexiune SQLite. Activează automat cheile externe prin `PRAGMA foreign_keys = ON`.

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

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

Conectați-vă folosind orice șir DSN PDO. merql include drivere doar pentru `mysql` și `sqlite`; celelalte DSN-uri deschid o conexiune, dar `Merql::init()`, `Snapshotter` și aplicatoarele generează `Unsupported database driver`, dacă nu este înregistrat un driver cu `DriverFactory::register()`.

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

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

Încapsulează o instanță PDO existentă.

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

Încapsulează o instanță mysqli existentă.

### Contractul adaptorului

Toate adaptoarele implementează `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()` și `scalar()` acceptă parametri poziționali `list<scalar|null>`
pentru substituenții `?`. `query()` returnează
`list<array<string, scalar|null>>`; valorile sunt normalizate la aceeași
formă șir sau null în adaptoarele PDO și mysqli.

---

## Snapshotter

`Merql\Snapshot\Snapshotter` capturează starea bazei de date. Spre deosebire de fațadă, vă oferă control deplin asupra filtrelor și nu salvează automat rezultatele.

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

Dacă nu este specificat, driverul este detectat automat pe baza identității conexiunii. Transmiteți un
`IdentityRuleSet` când identitatea rândurilor specifică tabelului trebuie să înlocuiască valorile implicite ale schemei.

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

Capturați starea curentă. Transmiteți numele tabelelor pentru a limita aria. Transmiteți obiecte de filtrare pentru a exclude tabele, coloane sau rânduri.

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

Capturați tabelele fizice folosind numele canonice ale tabelelor. Folosiți această opțiune când același
tabel logic are nume fizice diferite în medii diferite.

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

Instantaneul rezultat stochează numele canonice ale tabelelor, păstrând în același timp
schema tabelului fizic, coloanele de identitate configurate, cheile rândurilor și filtrele.

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

Construiește un instantaneu din date brute fără conexiune la baza de date. Util pentru teste sau la importul datelor din altă sursă.

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

---

## Instantaneu

`Merql\Snapshot\Snapshot` este un obiect readonly care reprezintă o stare completă a bazei de date.

```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` conține rândurile, amprentele și schema unui tabel.

```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` reprezintă date de intrare pentru `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` definește coloanele folosite pentru a construi cheile rândurilor.

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

Rezolvă regula de identitate implicită din schemă: cheie primară, prima cheie unică,
apoi la conținut.

### `toArray(): array`

Serializează regula ca `['type' => string, 'columns' => list<string>]`.

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

Încarcă o regulă din date serializate.

---

## IdentityRuleSet

`Merql\Identity\IdentityRuleSet` stochează suprascrieri ale identității specifice tabelelor.

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

Returnează un set nou de reguli, cu o suprascriere adăugată pentru un tabel.

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

Returnează intrările `IdentityConflict` când mai multe rânduri produc aceeași cheie pentru o
regulă.

### `toArray(): array`

Serializează regulile tabelelor ca tablou sigur pentru JSON.

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

Încarcă un set de reguli din date serializate.

---

## SnapshotStore

`Merql\Snapshot\SnapshotStore` salvează și încarcă instantanee ca fișiere JSON.

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

Setează directorul de stocare. Valoarea implicită este `.merql/snapshots`.

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

Scrie un instantaneu pe disc.

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

Încarcă un instantaneu după nume. Generează `SnapshotException` dacă nu este găsit.

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

Verifică dacă există un fișier de instantaneu.

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

Elimină un fișier de instantaneu.

---

## Comparator

`Merql\Diff\Differ` compară două instantanee și produce un set de modificări.

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

Compară două valori convertindu-le în șiruri. NULL este tratat ca valoare distinctă: `NULL === NULL` este adevărat, `NULL === ''` este fals.

---

## Set de modificări

`Merql\Diff\Changeset` este o colecție readonly de operații la nivel de rând.

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

Obiecte readonly care reprezintă modificări individuale ale rândurilor.

### 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` reprezintă o singură modificare de coloană în cadrul unei actualizări de rând.

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

---

## ThreeWayMerge

`Merql\Merge\ThreeWayMerge` este algoritmul central de îmbinare.

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

Transmiteți un `CellMergeConfig` pentru a activa îmbinarea la nivel de celulă pentru coloanele TEXT și JSON.

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

Efectuează o îmbinare în trei direcții. Calculează intern seturile de modificări (de la baza inițială la partea noastră și de la baza inițială la partea lor) și le îmbină.

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

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

Scurtătură pentru îmbinarea în două direcții. Echivalentă cu `merge($base, $base, $changes)`.

### `schemaMismatches(): array`

Returnează `SchemaException[]` pentru toate diferențele de schemă detectate în timpul ultimei îmbinări (coloane adăugate sau eliminate între instantanee).

---

## MergeResult

`Merql\Merge\MergeResult` este un obiect readonly care conține rezultatul îmbinării.

```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` este un model de revizuire serializabil pentru o îmbinare.

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

Folosiți `MergePlanSerializer::toJson()` și `::fromJson()` pentru parcurgeri dus-întors JSON stabile.

## Selecții

`ChangeGroupSelection` stochează ID-urile grupurilor de modificări etapizate. Convertiți-l într-un
`OperationSelection` la construirea rezultatelor de îmbinare selectate sau a planurilor de revenire.

```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` stochează operațiile inverse și așteptările privind deriva
pentru o aplicare selectată.

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

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

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

Folosiți `RollbackDrift` înainte de a aplica revenirea, apoi `RollbackPlanApplier` pentru a rula
operațiile inverse.

---

## MergeOperation

`Merql\Merge\MergeOperation` este o singură operație rezolvată din îmbinare.

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

Constante de tip:

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

---

## Conflict

`Merql\Merge\Conflict` reprezintă un conflict de îmbinare nerezolvat.

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

### Tipuri de conflicte

| Tip | Semnificație |
|---|---|
| `update_update` | Ambele părți au modificat aceeași coloană cu valori diferite |
| `update_delete` | Partea noastră a actualizat un rând pe care partea lor l-a șters |
| `delete_update` | Partea noastră a șters un rând pe care partea lor l-a actualizat |
| `insert_insert` | Ambele părți au inserat un rând cu aceeași cheie primară |

---

## ConflictResolver

`Merql\Merge\ConflictResolver` aplică o politică de rezolvare tuturor conflictelor dintr-un rezultat de îmbinare.

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

Returnează un nou `MergeResult` cu conflictele rezolvate conform politicii.

```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` este un enum backed cu trei cazuri.

```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` aplică logica de îmbinare per coloană pentru un singur rând. Este folosit intern de `ThreeWayMerge`.

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

Returnează `['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)
```

---

## Aplicator

`Merql\Apply\Applier` execută un rezultat de îmbinare asupra unei baze de date.

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

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

Execută toate operațiile într-o singură tranzacție. Revine asupra tranzacției la orice eroare și o raportează în `ApplyResult::errors()` în loc să genereze o excepție. Generează `ConflictException` dacă rezultatul conține conflicte nerezolvate.

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

Ordinea cheilor externe este gestionată automat: inserările sunt sortate în ordinea dependențelor, iar ștergerile în ordinea inversă a dependențelor.

---

## ApplyResult

`Merql\Apply\ApplyResult` este un obiect readonly returnat de aplicator.

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

După o aplicare anulată, `rowsAffected()` numără în continuare instrucțiunile executate înainte de eșec ([#4](https://github.com/inline0/merql/issues/4)). Verificați `hasErrors()` înainte de a vă baza pe număr.

---

## DryRun

`Merql\Apply\DryRun` generează SQL lizibile de oameni fără să execute nimic.

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

Returnează o listă de șiruri SQL cu valorile incluse direct (doar în scopuri de afișare).

```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` generează instrucțiuni SQL parametrizate. Folosit intern atât de `Applier`, cât și de `DryRun`.

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

Returnează `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` configurează strategii de îmbinare la nivel de celulă pentru coloane sau tipuri de coloane specifice.

### `auto(): self` (static)

Funcție utilitară care atribuie `TextCellMerger` coloanelor TEXT/LONGTEXT și `JsonCellMerger` coloanelor JSON.

```php
use Merql\CellMerge\CellMergeConfig;

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

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

Atribuie un îmbinător unei coloane specifice. Folosiți `table.column` pentru nume calificate sau doar `column` pentru toate tabelele.

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

Atribuie un îmbinător tuturor coloanelor care corespund unui tipar de tip (potrivire insensibilă la majuscule și minuscule după subșir).

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

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

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

Caută îmbinătorul pentru un tabel, o coloană și un tip date. Prioritate: numele calificat al coloanei, numele necalificat al coloanei, tiparul de tip, valoarea implicită (opac).

---

## CellMerger (interfață)

`Merql\CellMerge\CellMerger` este interfața strategiilor de îmbinare la nivel de celulă.

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

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

Sunt furnizate trei implementări încorporate.

### TextCellMerger

`Merql\CellMerge\TextCellMerger` îmbină text pe mai multe rânduri folosind o comparație linie cu linie în trei direcții (același algoritm pe care git îl folosește pentru conținutul fișierelor). Când cele două părți modifică rânduri diferite, îmbinarea reușește fără conflicte. Când ambele modifică același rând în mod diferit, raportează un conflict.

```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` îmbină valorile JSON comparând separat cheile de nivel superior. Fiecare cheie este îmbinată folosind logica în trei căi. Obiectele imbricate sunt comparate ca șiruri JSON opace (nu sunt îmbinate recursiv).

```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` este opțiunea implicită. Tratează valorile ca șiruri opace și raportează întotdeauna un conflict atunci când diferă.

---

## CellMergeResult

`Merql\CellMerge\CellMergeResult` este tipul de returnare pentru toate funcțiile de îmbinare a celulelor.

```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` este o reprezentare doar pentru citire a structurii tabelului.

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

---

## Sistemul de drivere

merql folosește un sistem de drivere extensibil pentru a abstractiza operațiile specifice bazelor de date. Driverele sunt detectate automat pe baza identității driverului conexiunii.

### Driver (interfață)

`Merql\Driver\Driver` definește contractul implementat de toate driverele de baze de date.

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

Implementări incluse: `MysqlDriver` și `SqliteDriver`.

### DriverFactory

`Merql\Driver\DriverFactory` detectează automat driverul unei conexiuni.

```php
use Merql\Driver\DriverFactory;

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

Înregistrați un driver personalizat pentru alte baze de date:

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

---

## Filtre

### TableFilter

`Merql\Filter\TableFilter` include sau exclude tabele folosind modele glob.

```php
use Merql\Filter\TableFilter;

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

### ColumnFilter

`Merql\Filter\ColumnFilter` elimină coloane specifice din instantanee.

```php
use Merql\Filter\ColumnFilter;

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

### RowFilter

`Merql\Filter\RowFilter` filtrează rândurile folosind o funcție predicat.

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

---

## Excepții

### SnapshotException

`Merql\Exceptions\SnapshotException` este aruncată când o instantanee nu poate fi găsită sau are un nume nevalid.

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

### MergeException

`Merql\Exceptions\MergeException` este aruncată când lipsește o instantanee necesară pentru o îmbinare.

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

### ConflictException

`Merql\Exceptions\ConflictException` este aruncată când se încearcă aplicarea unui rezultat de îmbinare cu conflicte nerezolvate.

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

### SchemaException

`Merql\Exceptions\SchemaException` este aruncată (sau colectată) când schemele diferă între instantanee.

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

Nepotrivirile de schemă nu opresc îmbinarea. Acestea sunt colectate și pot fi inspectate prin `$result->schemaMismatches()`.
