
# Darbo pradžia

Šiame vadove aprašoma, kaip įdiegti programą, prisijungti prie duomenų bazės, kurti momentines kopijas, apskaičiuoti skirtumus ir atlikti trijų krypčių sujungimą.

## Diegimas

```bash
composer require merql/merql
```

merql reikia PHP 8.2+ su `ext-mbstring` ir `ext-zlib` (abi reikalingos `pitmaster/pitmaster` priklausomybei). Taip pat reikia jūsų naudojamo adapterio plėtinio: `ext-pdo` su `ext-pdo_sqlite` arba `ext-pdo_mysql` PDO adapteriui arba `ext-mysqli` mysqli adapteriui. Composer tik pateikia rekomendacijas, todėl patikrinkite, ar reikiamas plėtinys įdiegtas.

Paketas įdiegia CLI kaip `vendor/bin/merql`. Naudokite merql 0.4.1 ar naujesnę versiją, kad veiktų CLI; ankstesnėse versijose dvejetainis failas negalėjo rasti autokrautuvo, kai merql buvo įdiegtas kaip priklausomybė.

## Prisijungimas prie duomenų bazės

merql veikia naudodama nedidelę ryšio adapterio sąsają. Pateikiami integruoti PDO ir mysqli adapteriai bei MySQL ir SQLite dialektų tvarkyklės.

```php
use Merql\Connection;

// SQLite (in-memory or file-based, through PDO).
$connection = Connection::sqlite(':memory:');
$connection = Connection::sqlite('/path/to/database.sqlite');

// MySQL through PDO.
$connection = Connection::mysql('127.0.0.1', 'my_database', 'root', 'password');

// MySQL through mysqli.
$connection = Connection::mysqli('127.0.0.1', 'my_database', 'root', 'password');

// Any MySQL or SQLite PDO DSN.
$connection = Connection::fromDsn('mysql:host=127.0.0.1;dbname=my_database;charset=utf8mb4', 'root', 'password');
```

`fromDsn()` atidaro bet kurį PDO palaikomą DSN, tačiau merql pateikiamos tik MySQL ir SQLite dialektų tvarkyklės. Naudojant kitą sistemą, pavyzdžiui, `pgsql:`, ryšys užmezgamas, tačiau `Merql::init()` pateikia `Unsupported database driver: pgsql` klaidą, kol jai neužregistruojate tvarkyklės. Žr. [Duomenų bazių tvarkyklės](https://merql.inline0.dev/lt/docs/apply/drivers).

## Momentinės kopijos kūrimas

Momentinėje kopijoje užfiksuojamos visos kiekvienos lentelės eilutės (arba jų poaibis) kaip kontrolinės sumos ir duomenys. Kontrolinės sumos leidžia greitai aptikti pakeitimus; išsamūs eilučių duomenys pasiekiami, kai reikia apskaičiuoti skirtumą.

```php
use Merql\Merql;

Merql::init($connection);

// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');

// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);
```

Momentinės kopijos pagal numatytuosius nustatymus išsaugomos kaip JSON failai kataloge `.merql/snapshots/`. Šią vietą galite pakeisti:

```php
use Merql\Snapshot\SnapshotStore;

SnapshotStore::setDirectory('/var/data/merql-snapshots');
```

## Skirtumo apskaičiavimas

Skirtumas palygina dvi momentines kopijas ir pateikia įterpimų, atnaujinimų bei ištrynimų pakeitimų rinkinį. Abi kopijos turi būti jau sukurtos; čia `current` yra antroji momentinė kopija, sukurta pasikeitus duomenų bazei.

```php
$changeset = Merql::diff('baseline', 'current');

echo "Inserts: " . count($changeset->inserts()) . "\n";
echo "Updates: " . count($changeset->updates()) . "\n";
echo "Deletes: " . count($changeset->deletes()) . "\n";

// Inspect changes for a single table.
$postChanges = $changeset->forTable('posts');

// Check per-column diffs on updates.
foreach ($changeset->updates() as $update) {
    echo "Row {$update->rowKey} in {$update->table}:\n";
    foreach ($update->columnDiffs as $diff) {
        echo "  {$diff->column}: {$diff->oldValue} -> {$diff->newValue}\n";
    }
}
```

## Trijų krypčių sujungimas

Pagrindinė operacija. Paimamos trys pavadintos momentinės kopijos (bazinė, mūsų ir jų) ir sujungiami du pakeitimų rinkiniai.

Bazinė kopija yra būsena, nuo kurios pradėjo abi kopijos. Mūsų ir jų kopijos yra dvi nepriklausomai pakeistos kopijos, todėl kiekviena turi būti užfiksuota iš ją turinčios duomenų bazės. Pavyzdžiui, kai bandomoji aplinka nukopijuota iš veikiančios duomenų bazės:

```php
use Merql\Connection;
use Merql\Merql;

$live    = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');

Merql::init($live);
Merql::snapshot('base');      // before the sandbox is copied from live

// ... copy live.sqlite to sandbox.sqlite, then both databases change ...

Merql::init($sandbox);
Merql::snapshot('ours');      // the sandbox with our edits
Merql::init($live);
Merql::snapshot('theirs');    // live with its own changes

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

echo "Operations: {$result->operationCount()}\n";
echo "Conflicts:  {$result->conflictCount()}\n";
echo "Clean:      " . ($result->isClean() ? 'yes' : 'no') . "\n";
```

Nedarykite visų trijų momentinių kopijų iš eilės iš tos pačios duomenų bazės. Tada jų kopijoje jau bus mūsų pakeitimai, todėl kiekviena mūsų įterpta eilutė bus nurodyta kaip `insert_insert` konfliktas.

`Merql::merge()` sujungia stulpelių lygmeniu. Norėdami papildomai sujungti TEXT ir JSON reikšmes, naudokite `ThreeWayMerge` su `CellMergeConfig`; žr. [Sujungimas langelių lygmeniu](https://merql.inline0.dev/lt/docs/merge/cell-level).

### Konfliktų sprendimas

Kai abi pusės tą patį stulpelį pakeičia skirtingomis reikšmėmis, merql pateikia konfliktą. Konfliktus galite išspręsti automatiškai arba patikrinti rankiniu būdu.

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

if (!$result->isClean()) {
    // Auto-resolve: one side wins.
    $resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);

    // Or inspect each conflict.
    foreach ($result->conflicts() as $conflict) {
        echo "Conflict on {$conflict->table()} row {$conflict->rowKey()}:\n";
        echo "  Type:   {$conflict->type()}\n";
        echo "  Column: {$conflict->column()}\n";
        echo "  Ours:   " . var_export($conflict->oursValue(), true) . "\n";
        echo "  Theirs: " . var_export($conflict->theirsValue(), true) . "\n";
    }
}
```

## Sujungimo pritaikymas

Kai turite švarų sujungimo rezultatą (neišspręstų konfliktų nėra), pritaikykite jį duomenų bazei, nurodytai `Merql::init()`.

Sujungimo rezultato operacijos paverčia bazinę būseną sujungta būsena ir apima abiejų pusių pakeitimus. `Merql::apply()` vykdo visas šias operacijas, todėl jos sėkmingai pritaikomos duomenų bazei, kurioje tebėra bazinė būsena. Pritaikius jas duomenų bazei, kurioje jau yra vienos pusės pakeitimų, tos pusės įterpimas susidurs su jau esančia eilute, nepavyks ir bus atšauktas visas sujungimas. Norėdami perkelti vienos pusės pakeitimus į duomenų bazę, kurioje jau yra kitos pusės pakeitimų (pavyzdžiui, bandomosios aplinkos pakeitimus į veikiančią aplinką), naudokite [Apsaugotą pritaikymą](https://merql.inline0.dev/lt/docs/apply/guarded) ir pritaikykite tik trūkstamas operacijas.

```php
$applied = Merql::apply($result);

echo "Rows affected: {$applied->rowsAffected()}\n";

if ($applied->hasErrors()) {
    foreach ($applied->errors() as $error) {
        echo "Error: {$error}\n";
    }
}
```

Pritaikymo programa visas operacijas vykdo vienoje transakcijoje. Jei kuris nors sakinys nepavyksta, visas sujungimas atšaukiamas, o klaida pateikiama `errors()`. Tokiu atveju `rowsAffected()` vis tiek skaičiuoja prieš klaidą vykdytus sakinius, nors jie buvo atšaukti ([#4](https://github.com/inline0/merql/issues/4)); pirmiausia patikrinkite `hasErrors()`.

## Bandomasis vykdymas

Peržiūrėkite SQL, kuriuos sujungimas sugeneruotų, jų nevykdydami.

```php
use Merql\Apply\DryRun;

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

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

## Dviejų krypčių pataisa

Jei turite tik bazinę kopiją ir pakeitimų rinkinį (be lygiagrečių „mūsų“ pakeitimų), naudokite `patch()`. Tai atitinka sujungimą, kai mūsų kopija nuo bazinės nesiskiria.

```php
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);
```

## CLI naudojimas

Visas šias operacijas taip pat galima atlikti komandinėje eilutėje. Išsamią informaciją rasite [CLI nuorodoje](https://merql.inline0.dev/lt/docs/cli).

```bash
export MERQL_DB_DSN="sqlite:mydb.sqlite"

vendor/bin/merql snapshot base
# ... change the database ...
vendor/bin/merql snapshot current
vendor/bin/merql diff base current
```

Trijų krypčių sujungimui sukurkite kiekvieną momentinę kopiją iš duomenų bazės, kurioje yra atitinkama būsena, o tada sujunkite:

```bash
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base
MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs
vendor/bin/merql merge base ours theirs --dry-run
```

## Lentelių ir stulpelių filtravimas

Kuriant momentines kopijas galima neįtraukti lentelių (pvz., talpyklos ar seanso lentelių) ir ignoruoti dažnai kintančius stulpelius (pvz., `updated_at` laiko žymas).

```php
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Snapshot\Snapshotter;

$snapshotter = new Snapshotter($connection);

$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['sessions', 'cache_*']),
    ColumnFilter::ignore(['updated_at', 'modified_date']),
]);
```

## Tolesni veiksmai

- Visą komandinės eilutės sąsają rasite [CLI nuorodoje](https://merql.inline0.dev/lt/docs/cli).
- Išsamią kiekvienos klasės ir metodo dokumentaciją rasite [PHP API nuorodoje](https://merql.inline0.dev/lt/docs/api).
