
# Controladores de bases de datos

merql separa el transporte de conexión del dialecto de base de datos. Un adaptador `DatabaseConnection` ejecuta SQL mediante PDO o mysqli, mientras que un `Driver` gestiona los metadatos SQL específicos del motor de base de datos y el entrecomillado de identificadores. Hay controladores integrados para MySQL y SQLite.

```php
use Merql\Driver\DriverFactory;

$driver = DriverFactory::create($connection);
// Auto-detects MySQL or SQLite from the connection driver identity.
```

## La interfaz Driver

Todos los controladores implementan cinco métodos:

```php
use Merql\Driver\Driver;

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

| Método | Propósito |
|---|---|
| `quoteIdentifier` | Entrecomillar el nombre de una tabla o columna para usarlo en SQL. |
| `listTables` | Devolver todas las tablas de usuario de la base de datos actual. |
| `readSchema` | Leer los nombres y tipos de columna, la clave primaria y las claves únicas de una tabla. |
| `readForeignKeys` | Devolver un mapa de las tablas secundarias y sus dependencias de tablas principales. |
| `selectAll` | Crear una consulta `SELECT *` para una tabla. |

## MysqlDriver

Usa consultas `INFORMATION_SCHEMA` para los metadatos y comillas invertidas para entrecomillar identificadores.

```php
use Merql\Driver\MysqlDriver;

$driver = new MysqlDriver();

$driver->quoteIdentifier('posts');
// `posts`

$driver->quoteIdentifier('my`table');
// `my``table`

$tables = $driver->listTables($connection);
// ['comments', 'posts', 'users']

$schema = $driver->readSchema($connection, 'posts');
// TableSchema with columns, primaryKey, uniqueKeys

$fks = $driver->readForeignKeys($connection);
// ['comments' => ['posts', 'users']]
```

Las consultas para enumerar tablas consultan `INFORMATION_SCHEMA.TABLES` filtrando por la base de datos actual y el tipo `BASE TABLE`. Las consultas para leer esquemas consultan `INFORMATION_SCHEMA.COLUMNS` para las columnas y `INFORMATION_SCHEMA.KEY_COLUMN_USAGE` para las claves primarias y únicas.

## SqliteDriver

Usa consultas `PRAGMA` para los metadatos y comillas dobles para entrecomillar identificadores.

```php
use Merql\Driver\SqliteDriver;

$driver = new SqliteDriver();

$driver->quoteIdentifier('posts');
// "posts"

$driver->quoteIdentifier('my"table');
// "my""table"

$tables = $driver->listTables($connection);
// Queries sqlite_master, excludes sqlite_* internal tables.

$schema = $driver->readSchema($connection, 'posts');
// Uses PRAGMA table_info() and PRAGMA index_list().

$fks = $driver->readForeignKeys($connection);
// Uses PRAGMA foreign_key_list() for each table.
```

## DriverFactory

`DriverFactory` detecta automáticamente el controlador correcto a partir de `DatabaseConnection::driverName()`.

```php
use Merql\Connection;
use Merql\Driver\DriverFactory;

$connection = Connection::mysql('127.0.0.1', 'mydb', 'root', '');
$driver = DriverFactory::create($connection);
// Returns MysqlDriver

$connection = Connection::sqlite(':memory:');
$driver = DriverFactory::create($connection);
// Returns SqliteDriver
```

Si no se reconoce la identidad del controlador de conexión, se lanza un `RuntimeException` con el mensaje `Unsupported database driver: <name>`. merql no incluye ningún controlador PostgreSQL.

### Registro de controladores personalizados

Para bases de datos distintas de MySQL y SQLite, escriba una clase que implemente `Driver` y regístrela. `PostgresDriver` representa su propia clase:

```php
use Merql\Driver\DriverFactory;

DriverFactory::register('pgsql', PostgresDriver::class);

$connection = Connection::fromDsn('pgsql:host=localhost;dbname=mydb', 'user', 'pass');
$driver = DriverFactory::create($connection);
// Returns your PostgresDriver instance.
```

La clase del controlador debe implementar la interfaz `Driver`. El primer argumento de `register()` es la identidad del controlador de conexión.

### Restablecer

Para realizar pruebas, restablezca la fábrica a su estado predeterminado:

```php
DriverFactory::reset();
// Reverts to only MySQL and SQLite drivers.
```

## Ayudante de conexión

La clase `Connection` crea adaptadores con valores predeterminados adecuados. Los adaptadores PDO usan excepciones ante errores, modo de obtención asociativo, instrucciones preparadas reales y resultados obtenidos convertidos a cadenas. El adaptador mysqli configura `utf8mb4`, usa el modo de excepciones de mysqli e instrucciones preparadas, y normaliza los valores obtenidos al mismo formato de cadena o null.

```php
use Merql\Connection;

// MySQL.
$connection = Connection::mysql('127.0.0.1', 'my_database', 'root', 'password');
$connection = Connection::mysql('db.example.com', 'mydb', 'user', 'pass', port: 3307);

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

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

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

`fromDsn()` acepta cualquier DSN que PDO pueda abrir, pero merql solo puede crear instantáneas y aplicar cambios mediante un controlador registrado. Una conexión `pgsql:` se abre (si `pdo_pgsql` está instalado), pero los constructores `Merql::init()`, `Snapshotter`, `Applier` y `GuardedApplier` llaman a `DriverFactory::create()`, que lanza `RuntimeException: Unsupported database driver: pgsql` salvo que haya [registrado un controlador](#registering-custom-drivers) para ella o que le pase uno.

`Connection::sqlite()` también activa `PRAGMA foreign_keys = ON` automáticamente, algo que SQLite necesita para aplicar las restricciones de claves externas.

## Dónde se usan los controladores

Los controladores se usan en todo merql:

- **Snapshotter** usa `listTables`, `readSchema` y `selectAll` para capturar el estado de la base de datos.
- **Applier** usa `readForeignKeys` para determinar el orden de las operaciones y pasa el controlador a `SqlGenerator` para entrecomillar identificadores.
- **SqlGenerator** usa `quoteIdentifier` para crear SQL seguros.

Si pasa una conexión a `Snapshotter` o `Applier` sin especificar un controlador, se llama automáticamente a `DriverFactory::create()`.
