
# Ξεκινώντας

Αυτός ο οδηγός καλύπτει την εγκατάσταση, τη σύνδεση σε βάση δεδομένων, την καταγραφή στιγμιότυπων, τον υπολογισμό διαφορών και την εκτέλεση συγχώνευσης τριών κατευθύνσεων.

## Εγκατάσταση

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

Το merql απαιτεί PHP 8.2+ με τα `ext-mbstring` και `ext-zlib` (απαιτούνται και τα δύο από την εξάρτηση `pitmaster/pitmaster`). Χρειάζεστε επίσης την επέκταση για τον προσαρμογέα που χρησιμοποιείτε: `ext-pdo` με `ext-pdo_sqlite` ή `ext-pdo_mysql` για τον προσαρμογέα PDO ή `ext-mysqli` για τον προσαρμογέα mysqli. Το Composer απλώς τα προτείνει, οπότε ελέγξτε ότι έχει εγκατασταθεί αυτό που χρειάζεστε.

Το πακέτο εγκαθιστά το CLI ως `vendor/bin/merql`. Χρησιμοποιήστε merql 0.4.1 ή νεότερη έκδοση για το CLI· στις παλαιότερες εκδόσεις το εκτελέσιμο δεν μπορούσε να εντοπίσει τον autoloader όταν το merql είχε εγκατασταθεί ως εξάρτηση.

## Σύνδεση σε βάση δεδομένων

Το merql λειτουργεί μέσω μιας μικρής διεπαφής προσαρμογέα σύνδεσης. Παρέχονται ενσωματωμένοι προσαρμογείς για PDO και mysqli, καθώς και ενσωματωμένοι οδηγοί διαλέκτου για τα MySQL και 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()` ανοίγει οποιοδήποτε DSN υποστηρίζει το PDO, αλλά το merql περιλαμβάνει οδηγούς διαλέκτου μόνο για τα MySQL και SQLite. Με άλλη μηχανή, όπως το `pgsql:`, η σύνδεση ανοίγει, αλλά το `Merql::init()` εμφανίζει `Unsupported database driver: pgsql` μέχρι να καταχωρίσετε οδηγό για αυτήν. Δείτε την ενότητα [Οδηγοί βάσεων δεδομένων](https://merql.inline0.dev/el/docs/apply/drivers).

## Καταγραφή στιγμιότυπου

Ένα στιγμιότυπο καταγράφει κάθε γραμμή κάθε πίνακα (ή υποσύνολο πινάκων) ως αποτυπώματα και δεδομένα. Τα αποτυπώματα επιτρέπουν τον γρήγορο εντοπισμό αλλαγών· τα πλήρη δεδομένα γραμμών είναι διαθέσιμα όταν χρειάζεται σύγκριση.

```php
use Merql\Merql;

Merql::init($connection);

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

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

Τα στιγμιότυπα αποθηκεύονται από προεπιλογή ως αρχεία JSON στο `.merql/snapshots/`. Μπορείτε να αλλάξετε αυτήν την τοποθεσία:

```php
use Merql\Snapshot\SnapshotStore;

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

## Υπολογισμός διαφορών

Η σύγκριση αντιπαραβάλλει δύο στιγμιότυπα και επιστρέφει ένα σύνολο αλλαγών με εισαγωγές, ενημερώσεις και διαγραφές. Και τα δύο στιγμιότυπα πρέπει να υπάρχουν ήδη· εδώ το `current` είναι ένα δεύτερο στιγμιότυπο που λήφθηκε μετά την αλλαγή της βάσης δεδομένων.

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

## Συγχώνευση τριών κατευθύνσεων

Η βασική λειτουργία. Δέχεται τρία επώνυμα στιγμιότυπα (base, ours, theirs) και συγχωνεύει τα δύο σύνολα αλλαγών.

Η βάση είναι η κατάσταση από την οποία ξεκίνησαν και τα δύο αντίγραφα. Τα δικά μας και της άλλης πλευράς είναι τα δύο αντίγραφα μετά τις ανεξάρτητες αλλαγές τους, επομένως το καθένα πρέπει να καταγραφεί από τη βάση δεδομένων που το περιέχει. Για παράδειγμα, όταν ένα περιβάλλον δοκιμών αντιγράφηκε από μια ενεργή βάση δεδομένων:

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

Μην καταγράφετε και τα τρία στιγμιότυπα διαδοχικά από μία βάση δεδομένων. Το στιγμιότυπο της άλλης πλευράς θα περιείχε ήδη τις δικές μας αλλαγές και κάθε γραμμή που εισαγάγαμε θα εμφανιζόταν ως διένεξη `insert_insert`.

Το `Merql::merge()` συγχωνεύει σε επίπεδο στήλης. Για συγχώνευση και μέσα σε τιμές TEXT και JSON, χρησιμοποιήστε το `ThreeWayMerge` με ένα `CellMergeConfig`· δείτε τη [Συγχώνευση σε επίπεδο κελιού](https://merql.inline0.dev/el/docs/merge/cell-level).

### Διαχείριση συγκρούσεων

Όταν και οι δύο πλευρές αλλάξουν την ίδια στήλη σε διαφορετικές τιμές, το merql δημιουργεί σύγκρουση. Μπορείτε να επιλύσετε αυτόματα τις συγκρούσεις ή να τις εξετάσετε χειροκίνητα.

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

## Εφαρμογή της συγχώνευσης

Αφού έχετε ένα καθαρό αποτέλεσμα συγχώνευσης (χωρίς ανεπίλυτες συγκρούσεις), εφαρμόστε το στη βάση δεδομένων που μεταβιβάστηκε στο `Merql::init()`.

Οι λειτουργίες του αποτελέσματος συγχώνευσης μετατρέπουν τη βάση στην κατάσταση συγχώνευσης και περιλαμβάνουν τις αλλαγές και των δύο πλευρών. Το `Merql::apply()` εκτελεί κάθε λειτουργία, επομένως εφαρμόζεται ομαλά σε μια βάση δεδομένων που εξακολουθεί να βρίσκεται στην αρχική κατάσταση. Αν εφαρμοστεί σε βάση δεδομένων που περιέχει ήδη τις αλλαγές της μίας πλευράς, οποιαδήποτε εισαγωγή από εκείνη την πλευρά αφορά γραμμή που υπάρχει ήδη, αποτυγχάνει και αναιρεί ολόκληρη τη συγχώνευση. Για να μεταφέρετε τις αλλαγές της μίας πλευράς σε βάση δεδομένων που περιέχει ήδη τις αλλαγές της άλλης (για παράδειγμα, αλλαγές sandbox στη live βάση), εφαρμόστε μόνο τις λειτουργίες που της λείπουν με την [Ελεγχόμενη εφαρμογή](https://merql.inline0.dev/el/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";
    }
}
```

Ο εφαρμοστής περιβάλλει όλες τις λειτουργίες με μια συναλλαγή. Αν αποτύχει οποιαδήποτε εντολή, αναιρείται ολόκληρη η συγχώνευση και το σφάλμα επιστρέφεται στο `errors()`. Σε αυτή την περίπτωση, το `rowsAffected()` εξακολουθεί να μετρά τις εντολές που εκτελέστηκαν πριν από την αποτυχία, παρότι αναιρέθηκαν ([#4](https://github.com/inline0/merql/issues/4))· ελέγξτε πρώτα το `hasErrors()`.

## Δοκιμαστική εκτέλεση

Προεπισκόπηση του SQL που θα δημιουργούσε μια συγχώνευση, χωρίς να την εκτελέσετε.

```php
use Merql\Apply\DryRun;

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

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

## Αμφίδρομο patch

Όταν έχετε μόνο μια βάση και ένα σύνολο αλλαγών (χωρίς ταυτόχρονες τροποποιήσεις από τη δική μας πλευρά), χρησιμοποιήστε το `patch()`. Αυτό ισοδυναμεί με συγχώνευση όπου η δική μας πλευρά παραμένει ίδια με τη βάση.

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

## Χρήση του CLI

Όλες αυτές οι λειτουργίες είναι διαθέσιμες και από τη γραμμή εντολών. Για όλες τις λεπτομέρειες, δείτε την [Αναφορά CLI](https://merql.inline0.dev/el/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
```

Για συγχώνευση τριών κατευθύνσεων, πάρτε κάθε στιγμιότυπο από τη βάση δεδομένων που περιέχει αυτή την κατάσταση και, στη συνέχεια, εκτελέστε τη συγχώνευση:

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

## Φιλτράρισμα πινάκων και στηλών

Τα στιγμιότυπα μπορούν να εξαιρούν πίνακες (όπως πίνακες cache ή συνεδριών) και να αγνοούν μεταβαλλόμενες στήλες (όπως χρονοσημάνσεις `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']),
]);
```

## Επόμενα βήματα

- [Αναφορά CLI](https://merql.inline0.dev/el/docs/cli) για την πλήρη διεπαφή γραμμής εντολών.
- [Αναφορά PHP API](https://merql.inline0.dev/el/docs/api) για αναλυτική τεκμηρίωση κάθε κλάσης και μεθόδου.
