Přeskočit obsah

Popis tříd k přístupu k databázi

Tento dokument popisuje hlavní PHP třídy v core/database. Cílem je přiblížit novému vývojáři, jak třídy fungují, jak spolu souvisejí a co je jejich odpovědnost.


Třída DB

Statická brána k databázovému rozhraní. Poskytuje přístup k metodám přes statické volání, ale veškerou práci deleguje na vnitřní instanci třídy PDOHandler.

Automatická inicializace (lazy loading)

Třída DB využívá lazy initialization - připojení k databázi se vytvoří automaticky při prvním volání libovolné metody (query(), results(), row(), atd.).

Není potřeba:

  • Manuálně volat DB::fromUrl()
  • Includovat app/database.php
  • Dělat cokoliv na inicializaci - DB se postará automaticky

Příklad:

// Automaticky se inicializuje při prvním dotazu - bez jakéhokoli require
$users = DB::results("SELECT * FROM users");
echo count($users);  // Funguje okamžitě!

Interně je připojení inicializováno prostřednictvím ensureInitialized() metody, která se volá automaticky před prvním SQL dotazem.

Klíčové public metody

  • DB::fromUrl(string $url) – explicitní inicializace z mysql:// nebo pgsql:// URL
  • DB::query($sql, $params = []) – provede SQL dotaz
  • DB::results($sql, $params = [], asArray: false) – vrátí více řádků
  • DB::row($sql, $params = [], asArray: false) – vrátí jeden řádek
  • DB::var($sql, $params = []) – vrátí jednu hodnotu
  • DB::col($sql, $params = []) – vrátí jeden sloupec
  • DB::test($sql, $params = [])debug helper: vypíše finální SQL s dosazením parametrů (bez spuštění)
  • DB::beginTransaction(), commit(), rollback() – správa transakcí
  • DB::set($fields) – generuje SET část dotazu
  • DB::setExcluded($fields, $exclude = []) – generuje PostgreSQL EXCLUDED.column část pro ON CONFLICT DO UPDATE
  • DB::getLog() – vrátí pole logovaných dotazů s časy

DB::fromUrl() místo DB::init()

Původní ruční inicializace přes DB::init(...) byla nahrazena URL-based API:

DB::fromUrl(env('DATABASE_URL'));

DB používá lazy load přes app/database.php, takže v běžném CMS kódu tuto metodu volat nemusíš. Je důležitá hlavně pro explicitní inicializaci druhého připojení nebo mimo standardní bootstrap.


Třída PDOHandler

Třída, která drží instanci PDO a vykonává reálné SQL operace. Je použita interně v DB::$DB. Lze ji použít přímo jako instanci pro sekundární DB připojení.

Klíčové public metody

  • __construct(...) – vytvoření připojení
  • static fromUrl(string $url): self – factory z DATABASE_URL stringu
  • query(), results(), row(), var(), col() – stejné jako DB
  • beginTransaction(), commit(), rollback() – transakce
  • getLog(): array – logované dotazy s časy (dědí z Base)

Drivery a DSN

PDOHandler nově rozlišuje driver už při konstrukci.

  • mysql / mariadb větev používá MySQL DSN s charset=utf8mb4
  • pgsql větev používá PostgreSQL DSN
  • fromUrl() rozpozná mysql://, pgsql://, postgresql://, postgres://
  • Default port je 3306 pro MySQL a 5432 pro PostgreSQL

To samé API tedy funguje nad oběma databázemi, rozdíl je jen v použitém driveru.


Proč není veřejné prepare()/execute() API

PDO je nakonfigurováno s ATTR_EMULATE_PREPARES = false. To znamená, že každé volání DB::query(), DB::results() atd. interně provede server-side prepare + execute.

Veřejné metody DB::prepare() a DB::execute() už v této vrstvě nejsou. Konzumující kód má používat přímo vyšší API:

DB::query("UPDATE users SET name = ? WHERE id = ?", [$name, $id]);

Interně PDOHandler stále používá prepare()/execute() pro každý parametrizovaný dotaz, ale není to vystaveno jako public API, protože to nepřináší další užitek pro běžný CMS kód.


Třída Base

Poskytuje základní infrastrukturu – výsledky dotazů, stavové proměnné a možnosti nastavení.

Klíčové vlastnosti

  • $lastQuery, $lastError, $queryError, $lastResult, $insertId, $affectedRows, $numRows
  • $driver – aktivní databázový driver (mysql nebo pgsql)
  • $quoteChar – znak pro quoting identifikátorů (` pro MySQL, " pro PostgreSQL)
  • setting(array $array)
  • set(array $fields, array $exclude = [], bool $useValues = false)
  • setExcluded(array $fields, array $exclude = [])

Quoting a generování SQL

Base::set() už nepoužívá napevno backticky. Místo toho pracuje s $quoteChar, takže stejný helper funguje jak pro MySQL, tak pro PostgreSQL.

Pro PostgreSQL upsert je navíc přidaná metoda setExcluded(), která generuje pravou stranu pro ON CONFLICT DO UPDATE:

$sql = 'INSERT INTO t ("id", "name") VALUES (:id, :name)
        ON CONFLICT (id) DO UPDATE SET ' . DB2::setExcluded($set, ['id']);

Výstup bude ve stylu:

"name" = EXCLUDED."name"

Naopak MySQL-specific REPLACE už se v DB vrstvě nepovažuje za standardní write query.


Třída DB2

DB2 je druhá statická DB vrstva nad stejným API jako DB. Dědí z DB, ale drží vlastní statické properties, takže má oddělený connection state, query log i metadata výsledků.

K čemu slouží

  • PostgreSQL připojení vedle hlavní CMS databáze
  • Integrace s externím systémem
  • Druhá databáze se samostatným lifecycle

Důležité chování

  • DB se inicializuje lazy přes app/database.php
  • DB2 nepodporuje lazy inicializaci
  • Před prvním použitím je nutné zavolat DB2::fromUrl(...)
  • Pokud to neuděláš, DB2::ensureInitialized() vyhodí výjimku

Příklad

use Core\Database\DB2;

DB2::fromUrl('pgsql://user:pass@host/dbname');

$row = DB2::row('SELECT * FROM article WHERE id = ?', [$id]);

Vztahy mezi třídami

classDiagram
    class DB {
        +static fromUrl()
        +static query()
        +static beginTransaction()
        +static setExcluded()
    }

    class PDOHandler {
        +query()
        +results()
        +row()
        +fromUrl()
    }

    class Base {
        +lastQuery
        +lastError
        +driver
        +quoteChar
        +setting()
    }

    DB --> PDOHandler : obsahuje instanci
    DB2 --|> DB : dědí
    PDOHandler --> Base : dědí

Sekundární databáze (PDOHandler přímo)

Pro výjimečné případy připojení k externí nebo partnerské databázi se použije PDOHandler přímo jako instance — bez statické třídy.

Kdy se to používá

  • Integrace se starším systémem (legacy databáze)
  • Partnerský web / subdoména s vlastní DB
  • Datový sklad s jinými přihlašovacími údaji

V 99% projektů na Petrovo CMS stačí jen DB.

Použití

use Core\Database\PDOHandler;

$db2 = PDOHandler::fromUrl(env('PARTNER_DATABASE_URL'));

$rows = $db2->results("SELECT * FROM products WHERE active = ?", [1]);
$row  = $db2->row("SELECT * FROM orders WHERE id = ?", [$id]);
$db2->getLog(); // timing k dispozici

Nebo s přímými parametry:

$db2 = new PDOHandler('user', 'pass', 'dbname', 'host.example.com', 'pgsql', 5432);

Rozdíl od DB

Aspekt DB PDOHandler instance
Inicializace Automatická (lazy) new nebo fromUrl()
Přístup Statický DB:: Instance $db2->
Debug bar ✅ DB::getLog() $db2->getLog() (samostatné)
API metody Stejné Stejné

Shrnutí (důležité pro vývojáře)

  • Používej DB:: pro veškeré databázové operace — auto-init, žádný setup
  • Vždy používej parametrizované dotazy (DB::query($sql, $params))
  • Výsledky vrací objekty (přístup ->columnName); pole jen pokud explicitně asArray: true
  • Base poskytuje metadata: lastQuery, lastError, affectedRows, insertId, getLog()
  • Pro druhé statické připojení použij DB2::fromUrl(env('...'))
  • Pro sekundární databázi jako instanci použij PDOHandler::fromUrl(env('...')) nebo new PDOHandler(...)