Příklady použití databázového rozhraní
Tato sekce ukazuje běžné scénáře použití třídy DB v rámci systému.
Inicializace databáze
Automatická inicializace (lazy loading)
Třída DB se inicializuje automaticky při prvním použití jakékoliv metody (DB::query(), DB::results(), atd.). Není nutné ručně volat require na app/database.php - to se děje transparentně na pozadí.
Příklad:
// Automatická inicializace při prvním dotazu
$users = DB::results("SELECT * FROM users WHERE active = ?", [1]);
// DB se připojí automaticky, není potřeba require ani DB::init()
Interní inicializace v app/database.php
Inicializace a nastavení DB probíhá automaticky v rámci CMS v souboru app/database.php. Není třeba (ani vhodné) volat DB::init() ani DB::setting() ručně v běžných částech kódu.
Ukázka z app/database.php:
$dbc = parse_url(env('DATABASE_URL'));
DB::init(
$dbc['user'],
rawurldecode($dbc['pass']),
trim($dbc['path'], '/'),
$dbc['host'],
'utf8',
$dbc['port'] ?? 3306
);
$setting = [
'cacheUse' => CONFIG['dbUseCache'],
'cacheSeconds' => CONFIG['dbCacheSeconds'],
'errorLogUse' => true,
'errorLogDir' => DIR . '/var/log',
'debugAll' => false,
];
DB::setting($setting);
Dále se nastavuje SQL režim a kódování:
DB::query("SET sql_mode='STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'");
DB::query("SET NAMES utf8mb4");
Použití v aplikaci
Třídu DB lze používat kdekoli globálně bez manuální inicializace - připojení se vytvoří automaticky při prvním dotazu. To platí pro modely, kontrolery i CLI skripty.
Příklady běžných dotazů následují níže.
Druhá databáze (DB2)
Pro vzácné případy, kdy je potřeba připojení k druhé databázi, použij třídu DB2. Na rozdíl od DB je nutné DB2 explicitně inicializovat před použitím.
Inicializace DB2
use Petrovo\Database\DB2;
use function Petrovo\Dotenv\env;
// Inicializace druhého připojení
$dbc2 = parse_url(env('DATABASE2_URL'));
$port2 = (int)!empty($dbc2['port']) ? $dbc2['port'] : 3306;
DB2::init($dbc2['user'], $dbc2['pass'], trim($dbc2['path'], '/'), $dbc2['host'], 'utf8', $port2);
unset($dbc2);
Použití DB2
Po inicializaci používej DB2:: stejně jako DB:::
// SELECT dotaz na druhou databázi
$product = DB2::row("SELECT p.cena, p.nazev, p.kod
FROM gProdukt p
WHERE p.kod = ?", [$productCode]);
// INSERT do druhé databáze
$sql = "INSERT INTO log SET action = ?, created = NOW()";
DB2::query($sql, ['product_view']);
Poznámka: DB2 se používá zřídka (např. integrace se starším systémem, external API databáze). Pro běžnou práci používej vždy DB.
Výběry dat SELECT
Práce s daty probíhá výhradně přes statickou třídu DB a využívá připravené SQL dotazy (prepare + execute), které se volají automaticky metodou DB::query().
Pravidla a upozornění
- Nemíchej náhrady
?a:namev jednom dotazu – způsobuje chyby v PDO. Používej buď jeden, nebo druhý způsob. - Parametry se neescapují ručně – escapování provádí MariaDB až na úrovni vykonání dotazu.
- Nepoužívej vlastní
addslashes()nebo ručníescape()– pouze výjimečně můžeš použítDB::escape()pro případy, kdy musíš dynamicky sestavit části dotazu, ale ne hodnoty. - Dotaz se připravuje a spouští automaticky pomocí
DB::query($sql, $params). - Pokud provádíš opakovaný dotaz ve smyčce, je výhodnější připravit si dotaz ručně pomocí
DB::prepare()a opakovaně volatDB::execute($stmp, $params).
Výběry dat SELECT
Pro práci s daty používej metody DB::results(), DB::row(), DB::col(), DB::var(). Všechny tyto metody volají interně prepare + execute a vracejí výsledek z databáze podle zvoleného typu výstupu.
Návratové hodnoty
Výstupem je vždy datová struktura podle typu požadovaného „row typu“:
'OBJECT'(výchozí) – každý řádek jako objekt'ARRAY_A'– asociativní pole (['column' => value])'ARRAY_N'– číselné pole ([0 => value])'JSON'– JSON string (méně často, např. do API)
Tyto typy platí pro results() a row().
Ukázky použití
// Více řádků – výchozí výstup (OBJECT), bez parametrů
$rows = DB::results("SELECT id, name FROM tags ORDER BY name");
foreach ($rows as $tag) {
echo $tag->name;
}
// Více řádků – výstup jako pole (ARRAY_A), bez parametrů
$rows = DB::results("SELECT id, name FROM tags ORDER BY name", 'ARRAY_A');
foreach ($rows as $tag) {
echo $tag['name'];
}
// Jeden řádek – pojmenované parametry
$user = DB::row("SELECT * FROM users WHERE id = :id", ['id' => 42], 'ARRAY_A');
echo $user['email'];
// Jeden sloupec – bez parametrů
$emails = DB::col("SELECT email FROM users ORDER BY id DESC");
// Jedna hodnota – s parametrem
$login = DB::var("SELECT last_login FROM users WHERE id = :id", ['id' => 42]);
Dotazy, které nevrací data
Pro dotazy typu INSERT, UPDATE, DELETE, SET nebo CALL se používá:
DB::query("UPDATE users SET last_login = NOW() WHERE active = 1");
Tyto dotazy nemají návratovou hodnotu, ale ovlivňují $affectedRows, $lastError, $lastQuery atd.
Sestavení SET části dotazu – DB::set()
Metoda DB::set() slouží k automatickému vytvoření části SQL dotazu typu SET column = :column. Výhodou je, že stačí předat asociativní pole, a metoda vygeneruje správný SQL fragment.
⚠️ Upozornění
- Nepoužívej
?jako placeholdery při použitíDB::set()– vygenerované výrazy používají pojmenované parametry (:column), a ty musí být použity i ve zbytku dotazu. - Nesmí se míchat
?a:name– jinak dojde k chybě při provádění dotazu. - Pokud používáš
DB::set(), musíš následně předat pole s klíči odpovídajícími názvům sloupců.
Základní INSERT s DB::set()
$set = [
'news_id' => $newsNewId,
'lang' => $r->lang,
'active' => $r->active,
'homepage' => $r->homepage,
'data' => $r->data,
];
$sql = "INSERT INTO news_lang SET " . DB::set($set);
DB::query($sql, $set);
UPDATE s podmínkou pomocí :id
$set = [];
$set['name'] = html_entity_decode($_post['name']);
$set['alias'] = $_post['alias'];
$set['group'] = array_get_int($_post, 'group', 0);
$set['active'] = array_get_int($_post, 'active', 0);
$set['perex'] = html_entity_decode($_post['perex']);
$set['description'] = $_post['description'];
$sql = "UPDATE cube_type SET " . DB::set($set) . " WHERE id = :id";
DB::query($sql, [...$set, 'id' => $_post['id']]);
Pokročilé použití – ON DUPLICATE KEY UPDATE
$set = [
'cube_id' => $_POST['cId'],
'lang' => $lang,
'data' => Json\encode($this->data($lang, $onlyFields)),
];
$sql = "INSERT INTO cube_lang SET " . DB::set($set) .
" ON DUPLICATE KEY UPDATE " . DB::set($set, ['cube_id', 'lang'], true);
DB::query($sql, $set);
Třetí parametr metody
DB::set($fields, $exclude, $useValues)umožňuje použítfield = VALUES(field)proON DUPLICATE KEY UPDATE, bez nutnosti přepisovat klíče.
Generování SET výrazů – DB::set()
Metoda DB::set() vygeneruje část SQL dotazu typu SET column = :column, nebo alternativní formáty podle hodnot a nastavení.
Signatura
DB::set(array $fields, array $exclude = [], bool $useValues = false): string
Parametry
$fields– pole['column' => value], ze kterého se generují výrazycolumn = :column$exclude– seznam klíčů, které se mají z$fieldsvyloučit (např. primární klíče)$useValues– pokud jetrue, místo:columnse použijeVALUES(column), např. proON DUPLICATE KEY UPDATE
Automatické úpravy hodnot
Metoda se snaží automaticky přizpůsobit výraz podle typu hodnoty:
| Hodnota ve vstupu | Výstup v SQL |
|---|---|
'NOW()' (string) |
column = NOW() |
true (bool) |
column = 1 |
false (bool) |
column = 0 |
| jiné hodnoty | column = :column |
při useValues = true |
column = VALUES(column) |
Příklad: NOW(), true, false
$set = [
'published' => true,
'archived' => false,
'created' => 'NOW()',
'title' => 'Nový článek',
];
$sql = "INSERT INTO article SET " . DB::set($set);
DB::query($sql, [$set['title']]);
Upozornění: Zde se musí dát pozor. DB::set() první tři hodnoty doplní automaticky do SQL dotazu dle předchozí tabulky!
Lépe použít DB::params(), který přímo vkládané vypustí.
$sql = "INSERT INTO article SET " . DB::set($set);
DB::query($sql, DB::params($set));
Výsledný SQL fragment:
`published` = 1, `archived` = 0, `created` = NOW(), `title` = :title
Příklad: Vyloučení polí (exclude)
$set = [
'id' => 42,
'title' => 'Nový název',
'slug' => 'novy-nazev',
];
$sql = "UPDATE article SET " . DB::set($set, ['id']) . " WHERE id = :id";
DB::query($sql, [...$set]);
Příklad: useValues = true pro ON DUPLICATE KEY UPDATE
$set = [
'user_id' => 7,
'points' => 120,
];
$sql = "INSERT INTO leaderboard SET " . DB::set($set) .
" ON DUPLICATE KEY UPDATE " . DB::set($set, ['user_id'], true);
DB::query($sql, $set);
Výsledný fragment:
`points` = VALUES(`points`)
Práce s JSON daty a podmínkami přes JSON_EXTRACT
V některých případech potřebujeme pracovat s JSON daty přímo v SQL dotazu (např. s polem translations). To platí zejména u polí, která nejsou indexovaná a není třeba na ně aplikovat vyhledávání nebo řazení.
Typické scénáře:
- test na prázdný překlad (
[],[""],null) - práce s
JSON_EXTRACT(...) - nutnost vložit název JSON klíče přímo do SQL — nelze použít
?placeholder
Příklad: kontrola prázdné hodnoty v JSON
$_post = $_post ?? $_POST ?? [];
$_target = DB::escape($_post['target']); // bude použit přímo do SQL
$_section = $_post['section'];
$sql = "SELECT id FROM i18n
WHERE section = ? AND (
JSON_LENGTH(JSON_EXTRACT(translations, '$.$_target')) = ''
OR JSON_LENGTH(JSON_EXTRACT(translations, '$.$_target')) = 0
OR JSON_EXTRACT(translations, '$.$_target') IS NULL
OR JSON_EXTRACT(translations, '$.$_target') = '\[\"\"\]'
OR JSON_EXTRACT(translations, '$.$_target') = 'null')";
$ids = DB::results($sql, [$_section]);
Vysvětlení
$_targetje klíč v JSON objektu (translations) – musí být vložen do SQL dotazu přímo jako součást stringu.- Proto je nutné jej ručně escapovat přes
DB::escape()– kvůli bezpečnosti. - Parametr
sectionje stále předán běžně přes?. - Dotaz testuje různé formy prázdné hodnoty:
NULL, prázdné pole,[""],0.
Vkládání, úpravy a mazání dat
Pro všechny dotazy, které nevracejí výsledek (INSERT, UPDATE, DELETE), se používá DB::query().
Výsledky těchto operací je možné kontrolovat pomocí:
DB::$insertId– ID posledního vloženého záznamuDB::$lastError– text poslední chybyDB::$affectedRows– počet ovlivněných řádků
INSERT s insertId
$sql = "INSERT INTO log_ecomail (email, status, action, created_on)
VALUES (?, 0, ?, NOW())";
DB::query($sql, [$r->email, $action]);
if ($id = DB::$insertId) {
// Záznam byl vložen, máme ID
}
INSERT s DB::set() a kontrola chyby
$_post = $_post ?? $_POST ?? [];
$password = array_get_str($_post, 'password', '');
$set['password'] = hash('sha512', DB::escape($password));
$set['username'] = array_get_str($_post, 'username', '');
$set['name'] = array_get_str($_post, 'name', '');
$set['email'] = array_get_str($_post, 'email', '');
$set['group_id'] = array_get_int($_post, 'group_id', 1);
$set['status'] = array_get_str($_post, 'status', 'Blocked');
$sql = "INSERT INTO admin SET id = UUID(), " . DB::set($set);
DB::query($sql, $set);
$output = empty(DB::$lastError);
UPDATE s ověřením změn
$id = (int)($_get['id'] ?? $_GET['id'] ?? 0);
$forLang = $_get['forLang'] ?? $_GET['forLang'] ?? '';
$sql = "UPDATE news_lang SET homepage = NOT homepage WHERE news_id = ? AND lang = ?";
DB::query($sql, [$id, $forLang]);
$output = DB::$affectedRows;
DELETE s návratovou hodnotou
$_id = base64_decode(array_get_str($_GET, 'id', ''));
$sql = "DELETE IGNORE FROM redirection WHERE old = ?";
DB::query($sql, [$_id]);
$output = (bool)DB::$affectedRows;
Práce s transakcemi
Pokud je třeba provést více SQL operací jako celek (např. INSERT + UPDATE + DELETE), použij DB::beginTransaction() spolu s DB::commit() nebo DB::rollback().
Doporučený postup
DB::beginTransaction();- provedení všech dotazů (
DB::query(),DB::var(), …) - pokud OK →
DB::commit(); - při chybě →
DB::rollback();
Jednoduchý INSERT s transakcí
DB::beginTransaction();
$sql = "INSERT IGNORE INTO i18n (section, identifier, translations)
VALUES (?, ?, ?)";
DB::query($sql, [$_section, $_identifier, $translationsJson]);
if ($output = DB::$insertId) {
DB::commit();
} else {
DB::rollback();
}
Složený mazací dotaz s transakcí
$_id = (int)($_get['id'] ?? $_GET['id'] ?? 0);
$_web = (int)($_get['web'] ?? $_GET['web'] ?? 0);
DB::beginTransaction();
// Získání content_id podle ID stránky
$sql = "SELECT content_id FROM page WHERE id = ?";
$contentId = DB::var($sql, [$_id]);
// Smazání z content
$sql = "DELETE FROM content WHERE id = ?";
DB::query($sql, [$contentId]);
// Smazání z page
$sql = "DELETE IGNORE FROM page WHERE id = ?";
DB::query($sql, [$_id]);
$output = (bool)DB::$affectedRows;
// Smazání z tabulky slug
$sql = "DELETE FROM slug WHERE web_id = ? AND type='page' AND join_id = ?";
DB::query($sql, [$_web, $_id]);
if (DB::$lastError) {
DB::rollback();
} else {
DB::commit();
}
return outputJson($output);