
# Kezdő lépések

Ez az útmutató bemutatja a telepítést, az adatbázishoz való csatlakozást, a pillanatképek rögzítését, az összehasonlítások kiszámítását és a háromutas egyesítés végrehajtását.

## Telepítés

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

A merql használatához PHP 8.2+ szükséges a `ext-mbstring` és a `ext-zlib` kiegészítővel (mindkettőt a `pitmaster/pitmaster` függőség igényli). A használt adapterhez tartozó bővítményre is szükség van: a PDO-adapterhez `ext-pdo` a `ext-pdo_sqlite` vagy a `ext-pdo_mysql` bővítménnyel, a mysqli-adapterhez pedig `ext-mysqli` kell. A Composer csak javasolja ezeket, ezért ellenőrizze, hogy a szükséges bővítmény telepítve van-e.

A csomag a CLI programot `vendor/bin/merql` néven telepíti. Használja a merql 0.4.1 verzióját vagy újabbat a CLI eléréséhez; korábbi kiadásokban a bináris nem találta az automatikus betöltőt, ha a merql függőségként volt telepítve.

## Kapcsolódás adatbázishoz

A merql egy egyszerű kapcsolatadapter-interfészen keresztül működik. Beépített adaptereket kínál PDO-hoz és mysqli-hez, valamint beépített dialektus-illesztőprogramokat MySQL és SQLite használatához.

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

A `fromDsn()` bármely, a PDO által támogatott DSN-t megnyitja, de a merql csak MySQL és SQLite használatához mellékel dialektus-illesztőprogramokat. Más adatbázismotor, például `pgsql:` esetén a kapcsolat létrejön, de az `Merql::init()` addig `Unsupported database driver: pgsql` hibát dob, amíg nem regisztrál hozzá illesztőprogramot. Lásd: [Adatbázis-illesztőprogramok](https://merql.inline0.dev/hu/docs/apply/drivers).

## Pillanatkép rögzítése

A pillanatkép minden tábla minden sorát (vagy a táblák egy részhalmazát) ujjlenyomatként és adatként rögzíti. Az ujjlenyomatok gyors változásészlelést tesznek lehetővé; teljes soradatokra az összehasonlításhoz van szükség.

```php
use Merql\Merql;

Merql::init($connection);

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

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

A pillanatképek alapértelmezés szerint `.merql/snapshots/` alatt, JSON fájlokként maradnak meg. Ezt a helyet módosíthatja:

```php
use Merql\Snapshot\SnapshotStore;

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

## Összehasonlítás kiszámítása

Az összehasonlítás két pillanatképet vet össze, és beszúrások, frissítések és törlések változáskészletét adja vissza. Mindkét pillanatképnek már léteznie kell; itt a `current` az adatbázis módosulása után rögzített második pillanatkép.

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

## Háromutas egyesítés

A központi művelet. Három elnevezett pillanatképet (alap, saját és másik fél állapota) vesz alapul, majd egyesíti a két módosításkészletet.

Az alap az az állapot, amelyből mindkét példány kiindult. A saját és a másik fél állapota a két példányt mutatja a külön-külön végzett módosítások után, ezért mindegyiket az azt tartalmazó adatbázisból kell rögzíteni. Például, ha egy tesztkörnyezetet egy éles adatbázisból másoltak:

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

Ne egymás után, ugyanabból az adatbázisból vegye mindhárom pillanatképet. Ekkor a másik fél állapota már tartalmazná a saját módosításait, és minden, általunk beszúrt sor `insert_insert` ütközésként jelenne meg.

A `Merql::merge()` oszlopszinten végez egyesítést. Ha TEXT- és JSON-értékeken belül is egyesítést szeretne, használja a `ThreeWayMerge`-t egy `CellMergeConfig`-val; lásd: [Cellaszintű egyesítés](https://merql.inline0.dev/hu/docs/merge/cell-level).

### Ütközések kezelése

Ha mindkét fél ugyanazt az oszlopot eltérő értékre módosítja, a merql ütközést jelez. Az ütközéseket automatikusan feloldhatja, vagy kézzel megvizsgálhatja.

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

## Az egyesítés alkalmazása

Ha az egyesítés eredménye tiszta (nincs feloldatlan ütközés), alkalmazza azt a `Merql::init()` által megadott adatbázisra.

Az egyesítés eredményében szereplő műveletek az alapállapotot alakítják át az egyesített állapottá, és mindkét fél módosításait tartalmazzák. A `Merql::apply()` mindegyiket végrehajtja, ezért tisztán alkalmazható egy olyan adatbázisra, amely még az alapállapotot tartalmazza. Ha olyan adatbázison alkalmazza, amely már az egyik fél módosításait tartalmazza, az adott fél bármely beszúrása egy már létező sorba ütközik, meghiúsul, és az egész egyesítés visszagördül. Ha az egyik fél módosításait egy olyan adatbázisba szeretné átvinni, amely már a másik fél módosításait tartalmazza (például a tesztkörnyezet módosításait az éles adatbázisba), csak azokat a műveleteket alkalmazza, amelyek még hiányoznak belőle a [Feltételes alkalmazás](https://merql.inline0.dev/hu/docs/apply/guarded) segítségével.

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

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

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

Az alkalmazó minden műveletet tranzakcióba foglal. Ha bármely utasítás meghiúsul, a teljes egyesítés visszagördül, a hiba pedig a `errors()` mezőben jelenik meg. Ilyenkor a `rowsAffected()` továbbra is beleszámítja a hiba előtt lefutott utasításokat, noha azok vissza lettek görgetve ([#4](https://github.com/inline0/merql/issues/4)); először a `hasErrors()` értékét ellenőrizze.

## Próbaüzem

Tekintse meg az egyesítés által előállított SQL előnézetét végrehajtás nélkül.

```php
use Merql\Apply\DryRun;

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

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

## Kétutas javítás

Ha csak egy alapállapot és egy módosításkészlet áll rendelkezésére (nincsenek párhuzamos „saját” módosítások), használja a `patch()`-t. Ez egy olyan egyesítésnek felel meg, amelyben a saját állapot nem tér el az alaptól.

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

## A CLI használata

Mindezek a műveletek parancssorból is elérhetők. A részletekért lásd a [CLI hivatkozást](https://merql.inline0.dev/hu/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
```

Háromutas egyesítéshez minden pillanatképet abból az adatbázisból vegyen, amely az adott állapotot tartalmazza, majd végezze el az egyesítést:

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

## Táblák és oszlopok szűrése

A pillanatképekből kizárhat táblákat (például gyorsítótár- vagy munkamenettáblákat), és figyelmen kívül hagyhat változó oszlopokat (például a `updated_at` időbélyegeket).

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

## Következő lépések

- A teljes parancssori felületért lásd a [CLI hivatkozást](https://merql.inline0.dev/hu/docs/cli).
- Az összes osztály és metódus részletes dokumentációjáért lásd a [PHP API hivatkozást](https://merql.inline0.dev/hu/docs/api).
