Skip to content

PostgreSQL verwenden ​

NENE2s PdoConnectionFactory unterstützt den pgsql-Adapter von Haus aus. Diese Anleitung behandelt die Einrichtungsschritte und PostgreSQL-spezifische Muster, die sich von MySQL und SQLite unterscheiden.

Docker Compose-Einrichtung ​

Einen postgres-Service hinzufügen und die Umgebungsvariablen für den app-Service setzen:

yaml
# compose.yaml
services:
  app:
    environment:
      DB_ADAPTER: pgsql
      DB_HOST: db
      DB_PORT: "5432"
      DB_NAME: myapp
      DB_USER: myapp
      DB_PASSWORD: secret
      DB_CHARSET: utf8    # für pgsql ignoriert — siehe Abschnitt zur Zeichenkodierung
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myapp -d myapp"]
      interval: 5s
      timeout: 10s
      retries: 10

Dockerfile — pdo_pgsql-Erweiterung ​

Die pdo_pgsql-Erweiterung ist im php:8.4-cli-Basis-Image nicht standardmäßig aktiviert. Sie zum Dockerfile hinzufügen:

dockerfile
FROM php:8.4-cli

RUN apt-get update && apt-get install -y libpq-dev unzip postgresql-client \
    && docker-php-ext-install pdo pdo_pgsql \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html

Phinx-Migrationskonfiguration ​

charset aus der Phinx-Umgebungskonfiguration bei Verwendung von pgsql weglassen — PostgreSQL verwendet diesen Schlüssel nicht:

php
// phinx.php
$database->environment => [
    'adapter' => $database->adapter,   // 'pgsql'
    'host'    => $database->host,
    'name'    => $database->name,
    'user'    => $database->user,
    'pass'    => $database->password,
    'port'    => $database->port,
    // 'charset' => ...  ← für pgsql weglassen
],

Phinx mappt seine Spaltentypen automatisch auf PostgreSQL-Typen. Häufige Zuordnungen:

Phinx-TypPostgreSQL-Typ
integer (Primärschlüssel)SERIAL
stringVARCHAR(n)
textTEXT
datetimeTIMESTAMP(0) WITHOUT TIME ZONE
booleanBOOLEAN

ID nach INSERT ermitteln — RETURNING id verwenden ​

DatabaseQueryExecutorInterface::lastInsertId() gibt auf PostgreSQL 0 zurück, weil PDO::lastInsertId() ein Sequenzname-Argument erfordert, das NENE2 nicht verfolgt.

fetchOne() mit RETURNING id im INSERT-SQL verwenden, um den generierten Primärschlüssel in einem einzigen Roundtrip abzurufen:

php
// ✓ PostgreSQL-kompatibles Muster
$row = $this->executor->fetchOne(
    'INSERT INTO reviews (book_title, content, rating, created_at) VALUES (?, ?, ?, ?) RETURNING id',
    [$bookTitle, $content, $rating, $now],
);
$id = (int) ($row['id'] ?? 0);

RETURNING id ist standard PostgreSQL-SQL und wird auch von SQLite >= 3.35 (2021) unterstützt. Es wird von MySQL/MariaDB nicht unterstützt — Repository-Implementierungen DB-spezifisch halten, wenn beide Zieldatenbanken unterstützt werden sollen.

Warum nicht lastInsertId()? ​

PostgreSQL-Sequenzen sind standardmäßig {table}_{column}_seq benannt (z. B. reviews_id_seq). PDO::lastInsertId('reviews_id_seq') würde funktionieren, aber DatabaseQueryExecutorInterface stellt keinen Sequenzname-Parameter bereit. RETURNING id ist sauberer und vermeidet die Kopplung.

Testdaten zwischen Tests zurücksetzen ​

Testisolation erfordert das Zurücksetzen der Tabelle zwischen jedem Testfall. Das korrekte SQL unterscheidet sich je nach Adapter:

php
protected function setUp(): void
{
    // PostgreSQL — setzt die SERIAL-Sequenz auf 1 zurück
    $this->executor->execute('TRUNCATE reviews RESTART IDENTITY CASCADE');

    // MySQL — TRUNCATE setzt AUTO_INCREMENT implizit zurück
    // $this->executor->execute('TRUNCATE reviews');

    // SQLite — TRUNCATE wird nicht unterstützt; DELETE + Sequenz-Reset verwenden
    // $this->executor->execute('DELETE FROM reviews');
    // $this->executor->execute("DELETE FROM sqlite_sequence WHERE name = 'reviews'");
}

TRUNCATE … RESTART IDENTITY CASCADE ist PostgreSQL-spezifisch. RESTART IDENTITY setzt die Sequenz zurück, sodass das nächste INSERT id = 1 zurückgibt und Test-Assertions deterministisch bleiben. CASCADE kürzt abhängige Tabellen in derselben Anweisung.

Zeichenkodierung — DB_CHARSET wird für pgsql ignoriert ​

Der pgsql-DSN enthält keinen charset-Parameter. DB_CHARSET / DatabaseConfig::charset wird für den pgsql-Adapter stillschweigend ignoriert — nur der mysql-Adapter verwendet ihn im DSN.

PostgreSQL bestimmt die Client-Kodierung aus server_encoding zum Verbindungszeitpunkt. Mit dem Standard-Locale erstellte Datenbanken verwenden UTF-8, was fast immer das Gewünschte ist.

Um eine bestimmte Kodierung zu erzwingen, DATABASE_URL mit dem options-Parameter verwenden:

DATABASE_URL=pgsql://myapp:secret@db:5432/myapp?options=--client_encoding%3DUTF8

Oder nach dem Verbindungsaufbau einen SET-Befehl ausgeben (nicht direkt über PdoDatabaseQueryExecutor möglich — dafür wäre eine benutzerdefinierte Connection Factory erforderlich).

Veröffentlicht unter der MIT-Lizenz.