← Code-Übersicht

PostgresDokumentationHistorie.php

Pfad: src/Infrastructure/Persistence/PostgresDokumentationHistorie.php

Ext: php

Größe: 3051 Bytes

Geändert: 2026-07-16 08:33:20+02

<?php
declare(strict_types=1);

namespace Demo\Infrastructure\Persistence;

use Demo\Domain\Dokumentation\Dokumentation;
use Demo\Domain\Dokumentation\DokumentationHistorie;
use Demo\Domain\Dokumentation\Version;
use PDO;

/**
 * Postgres-Adapter des DokumentationHistorie-Ports. Nur lesend, Prepared Statements.
 * Quelle ist public.history_dokumentation (System-Versioning-Pre-Images).
 */
final class PostgresDokumentationHistorie implements DokumentationHistorie
{
    /** @var PdoConnectionFactory Fabrik fuer die Verbindung. */
    private PdoConnectionFactory $factory;

    /** @var PDO|null Zwischengespeicherte Verbindung. */
    private ?PDO $pdo = null;

    /**
     * @param PdoConnectionFactory $factory Injizierte Verbindungs-Fabrik.
     */
    public function __construct(PdoConnectionFactory $factory)
    {
        $this->factory = $factory;
    }

    /**
     * @inheritDoc
     */
    public function versionen(string $slug): array
    {
        $sql = 'SELECT history_id, operation, geaendert_am FROM history_dokumentation '
            . "WHERE daten->>'slug' = :slug ORDER BY geaendert_am DESC, history_id DESC";
        $stmt = $this->pdo()->prepare($sql);
        $stmt->execute([':slug' => $slug]);
        $versionen = [];
        foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $versionen[] = new Version(
                (int) $row['history_id'],
                (string) $row['geaendert_am'],
                (string) $row['operation']
            );
        }

        return $versionen;
    }

    /**
     * @inheritDoc
     */
    public function beiVersion(string $slug, int $id): ?Dokumentation
    {
        $sql = 'SELECT daten FROM history_dokumentation '
            . "WHERE history_id = :id AND daten->>'slug' = :slug";
        $stmt = $this->pdo()->prepare($sql);
        $stmt->execute([':id' => $id, ':slug' => $slug]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($row === false) {
            return null;
        }
        $daten = json_decode((string) $row['daten'], true);

        return is_array($daten) ? $this->hydrate($daten) : null;
    }

    /**
     * Baut aus einem JSONB-Snapshot eine Dokumentation.
     *
     * @param array<string,mixed> $daten Dekodierter Snapshot.
     * @return Dokumentation Hydrierte Entity.
     */
    private function hydrate(array $daten): Dokumentation
    {
        $tags = $daten['tags'] ?? [];

        return new Dokumentation(
            (int) ($daten['id'] ?? 0),
            (string) ($daten['slug'] ?? ''),
            (string) ($daten['titel'] ?? ''),
            (string) ($daten['body'] ?? ''),
            is_array($tags) ? array_values(array_map('strval', $tags)) : [],
            (string) ($daten['created_at'] ?? ''),
            (string) ($daten['updated_at'] ?? '')
        );
    }

    /**
     * Liefert die zwischengespeicherte Verbindung (lazy).
     *
     * @return PDO Aktive Verbindung.
     */
    private function pdo(): PDO
    {
        return $this->pdo ??= $this->factory->create();
    }
}