
# Kom godt i gang

Denne vejledning gennemgår installation, tilslutning til en database, oprettelse af øjebliksbilleder, beregning af diff og udførelse af en trevejsfletning.

## Installation

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

merql kræver PHP 8.2+ med `ext-mbstring` og `ext-zlib` (begge kræves af afhængigheden `pitmaster/pitmaster`). Du skal også bruge udvidelsen til den adapter, du bruger: `ext-pdo` med `ext-pdo_sqlite` eller `ext-pdo_mysql` til PDO-adapteren eller `ext-mysqli` til mysqli-adapteren. Composer foreslår kun disse, så kontrollér, at den nødvendige udvidelse er installeret.

Pakken installerer CLI som `vendor/bin/merql`. Brug merql 0.4.1 eller nyere til CLI; i tidligere versioner kunne programfilen ikke finde autoloaderen, når merql var installeret som afhængighed.

## Opret forbindelse til en database

merql fungerer via et lille interface til forbindelsesadaptere. Der medfølger indbyggede adaptere til PDO og mysqli samt indbyggede dialektdrivere til MySQL og SQLite.

```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()` åbner alle DSN'er, som PDO understøtter, men merql leveres kun med dialektdrivere til MySQL og SQLite. Med en anden databasemotor, f.eks. `pgsql:`, oprettes forbindelsen, men `Merql::init()` udløser `Unsupported database driver: pgsql`, indtil du registrerer en driver til den. Se [Databasedrivere](https://merql.inline0.dev/da/docs/apply/drivers).

## Oprettelse af et øjebliksbillede

Et øjebliksbillede registrerer hver række i hver tabel (eller et udvalg af tabeller) som fingeraftryk og data. Fingeraftryk muliggør hurtig registrering af ændringer; alle rækkedata er tilgængelige, når der er brug for en diff.

```php
use Merql\Merql;

Merql::init($connection);

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

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

Som standard gemmes øjebliksbilleder som JSON-filer i `.merql/snapshots/`. Du kan ændre denne placering:

```php
use Merql\Snapshot\SnapshotStore;

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

## Beregning af en diff

En diff sammenligner to øjebliksbilleder og returnerer et ændringssæt med indsættelser, opdateringer og sletninger. Begge øjebliksbilleder skal allerede findes; her er `current` et andet øjebliksbillede, der er taget, efter at databasen blev ændret.

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

## Trevejsfletning

Den centrale handling. Tager tre navngivne øjebliksbilleder (base, ours, theirs) og fletter de to sæt ændringer.

Base er den tilstand, som begge kopier startede fra. Ours og theirs er de to kopier efter uafhængige ændringer, så hver skal hentes fra den database, der indeholder den. Eksempel: en sandkasse blev kopieret fra en live-database:

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

Tag ikke alle tre øjebliksbilleder fra én database i rækkefølge. Theirs ville så allerede indeholde ændringerne fra ours, og hver række, som ours indsatte, ville blive vist som en `insert_insert`-konflikt.

`Merql::merge()` fletter på kolonneniveau. Hvis du også vil flette inde i værdier af typen TEXT og JSON, skal du bruge `ThreeWayMerge` med en `CellMergeConfig`; se [Fletning på celleniveau](https://merql.inline0.dev/da/docs/merge/cell-level).

### Håndtering af konflikter

Når begge sider ændrer samme kolonne til forskellige værdier, opstår der en konflikt i merql. Du kan løse konflikter automatisk eller undersøge dem manuelt.

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

## Anvendelse af fletningen

Når du har et rent fletningsresultat (uden uløste konflikter), skal du anvende det på databasen, der er angivet til `Merql::init()`.

Handlingerne i et fletningsresultat omdanner base til den flettede tilstand og omfatter ændringerne fra begge sider. `Merql::apply()` udfører dem alle, så resultatet kan anvendes direkte på en database, der stadig er i grundtilstanden. Hvis det anvendes på en database, der allerede indeholder ændringer fra den ene side, vil en indsættelse fra den side ramme en eksisterende række, fejle og rulle hele fletningen tilbage. Hvis du vil overføre ændringer fra den ene side til en database, der allerede indeholder ændringerne fra den anden (f.eks. sandkasseændringer til live), skal du kun anvende de handlinger, den mangler, med [Beskyttet anvendelse](https://merql.inline0.dev/da/docs/apply/guarded).

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

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

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

Anvenderen indkapsler alle handlinger i en transaktion. Hvis en sætning fejler, rulles hele fletningen tilbage, og fejlen returneres i `errors()`. I så fald tæller `rowsAffected()` stadig de sætninger, der blev kørt før fejlen, selvom de blev rullet tilbage ([#4](https://github.com/inline0/merql/issues/4)); kontrollér `hasErrors()` først.

## Prøvekørsel

Få vist de SQL, som en fletning ville generere, uden at udføre dem.

```php
use Merql\Apply\DryRun;

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

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

## Tovejspatch

Når du kun har en base og et sæt ændringer (uden samtidige ændringer fra "ours"), skal du bruge `patch()`. Det svarer til en fletning, hvor ours er uændret i forhold til base.

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

## Brug af CLI

Alle disse handlinger er også tilgængelige fra kommandolinjen. Se [CLI-reference](https://merql.inline0.dev/da/docs/cli) for alle detaljer.

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

Ved en trevejsfletning skal du hente hvert øjebliksbillede fra databasen, der indeholder den pågældende tilstand, og derefter flette:

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

## Filtrering af tabeller og kolonner

Øjebliksbilleder kan udelade tabeller (f.eks. cache- eller sessionstabeller) og ignorere flygtige kolonner (f.eks. tidsstempler af typen `updated_at`).

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

## Næste trin

- [CLI-reference](https://merql.inline0.dev/da/docs/cli) med hele kommandolinjegrænsefladen.
- [PHP API-reference](https://merql.inline0.dev/da/docs/api) med detaljeret dokumentation af hver klasse og metode.
