Driver PostgreSQL para Phobos Framework Database Layer. Aprovecha las características
propias de PostgreSQL: schemas reales (search_path), INSERT ... RETURNING para leer el
id autogenerado, columnas JSONB mapeadas a arrays de PHP, y claves primarias UUIDv7
generadas en la aplicación.
- 🐘 Conectividad PostgreSQL - DSN,
sslmode, y configuración de sesión - 🗂️ Schemas reales - Configuración de
search_pathy nombres calificadosschema.tabla - 🔄 Schema-per-tenant - Re-apuntado de schemas en runtime vía
SchemaRegistry - ↩️ INSERT ... RETURNING - El id autogenerado (SERIAL/IDENTITY/uuid) se lee en el INSERT
- 🧬 UUIDv7 - Claves primarias ordenadas por tiempo, generadas sin round-trip
- 📦 JSONB - Mapeo automático entre arrays de PHP y columnas JSONB vía
$casts - 🔀 Transaction Isolation - Los cuatro niveles estándar
- 🎯 Savepoints - Transacciones anidadas completas
composer require mongoose-studio/phobos-framework-database-postgresEste driver requiere:
mongoose-studio/phobos-framework^3.2mongoose-studio/phobos-framework-database^3.2- Extensión
ext-pdohabilitada - Extensión
ext-pdo_pgsqlhabilitada
En tu config/database.php:
<?php
return [
'default' => 'pgsql',
'drivers' => [
'pgsql' => PhobosFramework\Database\Drivers\Postgres\PostgresDriver::class,
],
'connections' => [
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', 5432),
'database' => env('DB_DATABASE', 'app'),
'username' => env('DB_USERNAME', 'postgres'),
'password' => env('DB_PASSWORD', ''),
'schema' => 'public', // Opcional: search_path
],
],
];DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=app
DB_USERNAME=postgres
DB_PASSWORD=secret| Clave | Tipo | Descripción |
|---|---|---|
host |
string | Servidor PostgreSQL (requerido) |
database |
string | Nombre de la base de datos (requerido) |
port |
int | Puerto (opcional, por defecto 5432) |
username |
string | Usuario |
password |
string | Contraseña |
sslmode |
string | disable/allow/prefer/require/verify-ca/verify-full |
schema |
string | Un único schema para el search_path (ej: app) |
search_path |
string | Lista de schemas separada por comas (ej: app,public) |
timezone |
string | Zona horaria de la sesión (ej: UTC, America/Santiago) |
application_name |
string | Nombre de la aplicación (visible en pg_stat_activity) |
client_encoding |
string | Encoding del cliente (ej: UTF8) |
options |
array | Atributos PDO adicionales |
PostgreSQL organiza las tablas en schemas dentro de una misma base de datos. Hay dos formas de trabajar con ellos:
A) search_path a nivel de conexión — la entidad no declara schema y se resuelve por
el orden del search_path:
// config: 'search_path' => 'app,public'
class User extends TableEntity {
protected static ?string $schema = null; // resuelto por search_path
protected static string $entity = 'users';
}B) Nombre calificado por entidad — la entidad declara su schema; la capa genera
"schema"."tabla":
class Invoice extends TableEntity {
protected static ?string $schema = 'ventas';
protected static string $entity = 'invoices'; // -> "ventas"."invoices"
}El $schema de la entidad es un alias lógico. Con SchemaRegistry puedes mapearlo a un
schema físico distinto por request, sin tocar el código de las entidades:
use PhobosFramework\Database\Schema\SchemaRegistry;
// En un middleware, según el tenant del request:
SchemaRegistry::getInstance()->register('ventas', "tenant_{$tenantId}");
// Ahora toda entidad con $schema = 'ventas' escribe/lee en "tenant_42"."invoices"El driver soporta tres estrategias vía $keyStrategy en la entidad:
// 1. auto (por defecto): SERIAL/IDENTITY; el id se lee con INSERT ... RETURNING
class Account extends TableEntity {
protected static array $pk = ['id'];
protected static string $keyStrategy = 'auto';
public ?int $id = null;
}
// 2. uuidv7: el framework genera un UUIDv7 en PHP antes del INSERT (sin round-trip)
class Document extends TableEntity {
protected static array $pk = ['id'];
protected static string $keyStrategy = 'uuidv7';
public mixed $id = null; // columna UUID
}
// 3. manual: la aplicación asigna el valor
class Setting extends TableEntity {
protected static array $pk = ['key'];
protected static string $keyStrategy = 'manual';
public string $key = '';
}Un PK ya asignado siempre gana: se persiste tal cual, sin generar ni releer.
Declara $casts para mapear columnas automáticamente entre su forma en la base de datos y
su tipo nativo en PHP:
class Account extends TableEntity {
protected static string $entity = 'accounts';
protected static array $casts = [
'meta' => 'json', // JSONB <-> array PHP
'active' => 'bool', // boolean (entiende 't'/'f')
'created_at' => 'datetime', // timestamp <-> DateTimeImmutable
];
public mixed $meta = null;
public mixed $active = null;
}
$account = new Account();
$account->meta = ['plan' => 'pro', 'seats' => 5];
$account->save(); // se persiste como JSONB
$found = Account::findByPk($account->id);
$found->meta['plan']; // 'pro' (array PHP)Nota sobre operadores JSONB y placeholders: los operadores
?,?|y?&de JSONB chocan con los placeholders?de PDO. Para consultar dentro de JSONB usawhere()crudo con operadores que no colisionen (->>,@>,#>>), por ejemplo:Account::find(["meta->>'plan' = ?" => 'pro']).
PostgreSQL no soporta DELETE ... LIMIT. Si se intenta, la capa lanza una excepción clara
(LogicException) en lugar de emitir SQL inválido. Acota las filas con un WHERE.
$driver->getSetIsolationLevelSQL('SERIALIZABLE');
// SET TRANSACTION ISOLATION LEVEL SERIALIZABLENiveles soportados: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE.
# Unit (sin base de datos)
composer test-unit
# Integración (requiere PostgreSQL real; configurar DB_* en phpunit.xml)
composer test-integration
# Todo
composer testLos tests de integración crean tablas y schemas temporales en la base phobos_test y los
limpian al terminar.
- PostgresDriver (
src/Drivers/Postgres/PostgresDriver.php): extiendeAbstractDriver. Genera el DSN, configurasearch_path/timezone/application_name, y expone la gramática. - PostgresGrammar (
src/Drivers/Postgres/PostgresGrammar.php): extiendeAnsiGrammar(comillas dobles). ActivaRETURNINGy deshabilitaDELETE ... LIMIT.
- PostgreSQL 12+ (probado contra 18.x)
- PHP 8.4+
Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para más detalles.
Marcel Rojas
marcelrojas16@gmail.com
Mongoose Studio
Las contribuciones son bienvenidas. Por favor:
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/amazing-feature) - Commit tus cambios (
git commit -m 'Add amazing feature') - Push a la rama (
git push origin feature/amazing-feature) - Abre un Pull Request
Phobos Framework by Mongoose Studio