RRelayer
Home/Features

Database#

Setting DATABASE_DSN automatically registers the Db layer in the DI container, so pages/components can receive Database by type.

DATABASE_DSN=mysql:host=127.0.0.1;dbname=app;charset=utf8mb4
DATABASE_USER=app
DATABASE_PASSWORD=secret
DATABASE_TIMEOUT=5
DATABASE_READ_TIMEOUT=10

The DSN is passed to PDO as-is (no %placeholder% expansion). SQLite requires an absolute path (a relative path is resolved against the process's cwd).

DATABASE_DSN=sqlite:/srv/app/var/app.db

Usage#

<?php
use Polidog\Relayer\Db\Database;
use Polidog\Relayer\Router\Component\PageComponent;
use Polidog\UsePhp\Runtime\Element;

final class UserPage extends PageComponent
{
    public function __construct(private readonly Database $db) {}

    public function render(): Element
    {
        $user = $this->db->fetchOne(
            'SELECT id, name FROM users WHERE id = :id',
            ['id' => 42],
        );

        return <h1>{$user['name']}</h1>;
    }
}

Methods: fetchAll() / fetchOne() / fetchValue() / perform() / insert() / update() / delete() / lastInsertId() / transactional().

The Database alias always resolves to CachingDatabase (request-scoped read memoization), and in dev it is wrapped with TraceableDatabase for the profiler.

Single-table DML (insert / update / delete)#

Thin sugar for simple single-row INSERTs and equality-condition UPDATEs / DELETEs that do not warrant writing your own SQL. Internally it builds a parameterized statement and routes it through perform(), so decorator behavior such as caching and tracing applies as-is.

MethodSignatureReturn value
insertinsert(string $table, array $data)lastInsertId() (empty string if none)
updateupdate(string $table, array $set, array $where)affected row count
deletedelete(string $table, array $where)affected row count
$id = $this->db->insert('users', ['name' => 'Alice', 'email' => '[email protected]']);

$this->db->update('users', ['name' => 'Bob'], ['id' => $id]);

$this->db->delete('users', ['id' => $id]);

$where is equality conditions only combined with AND. A key with a null value is converted to IS NULL (to prevent the silent miss where binding null to col = ? never matches in SQL). The $where of update() / delete() cannot be empty — write an update/delete targeting all rows explicitly with perform(). The $data of insert() and the $set of update() also cannot be empty.

Identifiers (table and column names) are bare names only (^[A-Za-z_][A-Za-z0-9_]*$; $table may be schema.table). Values are always bound. Anything beyond this — JOIN, OR, IN, expressions, RETURNING, upsert, quoted reserved-word identifiers — should be written as raw SQL with perform(). A violation (driver failure, invalid identifier, empty array) raises a DatabaseException.

Storage for this site#

The documentation content itself uses its own DocStore rather than the Db layer of DATABASE_DSN. The reason is that it connects to Turso (libSQL) via the HTTP API and falls back to pdo_sqlite locally. SQLite's FTS5 (trigram tokenizer) provides partial-match search for both Japanese and English. Articles are kept in Turso as the single source of truth and edited directly with the bin/docs CLI ($EDITOR). See Deployment for details.

Last updated: 2026-05-19
Change history (1)
  • Created