diff --git a/database/cs/@home.texy b/database/cs/@home.texy
index 20cc87170b..57efa0ed50 100644
--- a/database/cs/@home.texy
+++ b/database/cs/@home.texy
@@ -1,20 +1,176 @@
+.[perex]
+Nette Database je výkonná a elegantní databázová vrstva pro PHP, která vyniká svou jednoduchostí použití a chytrými funkcemi. Nevyžaduje žádnou složitou konfiguraci nebo generování entit, s Nette Database můžete začít pracovat okamžitě.
+S Nette Database můžete pracovat dvěma způsoby - buď psaním SQL dotazů (Direct přístup), nebo nechat SQL generovat automaticky (Explorer přístup).
-Podporované databáze
-====================
+
+
+
+
+Direct SQL
+==========
+- Bezpečné parametrizované dotazy
+- Přesná kontrola nad podobou SQL dotazů
+- Když píšete komplexní dotazy s pokročilými funkcemi
+- Optimalizujete výkon pomocí specifických SQL funkcí
+
+
+
+
+
+
+
+Explorer
+========
+- Vyvíjíte rychle bez psaní SQL
+- Intuitivní práce s relacemi mezi tabulkami
+- Oceníte automatickou optimalizaci dotazů
+- Vhodné pro rychlou a pohodlnout práci s databází
+
+
+
+
+
+
+---
+
+
+
+
+
+Automatická optimalizace výkonu
+-------------------------------
+- Inteligentní načítání souvisejících dat
+- Adaptivní načítání pouze potřebných sloupců
+- Minimalizace počtu databázových dotazů
+
+
+
+
+
+
+Bezpečnost na prvním místě
+--------------------------
+- Vestavěná ochrana proti SQL injection
+- Parametrizované dotazy
+- Bezpečné zpracování vstupních dat
+
+
+
+
+
+
+Intuitivní práce s relacemi
+---------------------------
+- Přirozený přístup k propojeným datům
+- Podpora všech typů vazeb (1:1, 1:N, M:N)
+- Bez nutnosti psát JOIN dotazy
+
+
+
+
+
+
+Pohodlné debuggování
+--------------------
+- Panel do [Tracy|tracy:]
+- Všechny provedené dotazy s časy
+- Vysvětlení dotazů (EXPLAIN)
+
+
+
+
-Nette podporuje následující databáze:
-|* Databázový server |* DSN jméno |* Podpora v Core |* Podpora v Explorer
-| MySQL (>= 5.1) | mysql | ANO | ANO
-| PostgreSQL (>= 9.0) | pgsql | ANO | ANO
-| Sqlite 3 (>= 3.8) | sqlite | ANO | ANO
-| Oracle | oci | ANO | -
-| MS SQL (PDO_SQLSRV) | sqlsrv | ANO | ANO
-| MS SQL (PDO_DBLIB) | mssql | ANO | -
-| ODBC | odbc | ANO | -
+Nejjednodušší parametrické dotazy
+---------------------------------
+
+Stačí jen čárka a hodnota:
+
+
+```php .[dark]
+$database->query('
+ SELECT *
+ FROM users
+ WHERE name =', $name
+);
+```
+
+
+Žádné `?`, `:param`, `@param` nebo jiné speciální syntaxe - prostě jen otazník.
+
+
+
+
+
+
+Chytrá detekce vazeb
+--------------------
+Nepotřebujete konfigurovat entity ani mapování:
+
+
+```php .[dark]
+$book = $explorer->table('book')->get(1);
+// automaticky nalezne vazbu přes book.author_id
+echo $book->author->name;
+```
+
+
+
+
+
+
+
+Adaptivní načítání dat
+----------------------
+
+Automaticky načítá jen sloupce, které skutečně používáte v kódu
+
+
+```php .[dark]
+foreach ($books as $book) {
+ // načte z databáze jen sloupec 'title'
+ echo $book->title;
+}
+```
+
+
+
+
+
+
+
+Přes 18 let vývoje
+==================
+Nette vyvíjíme přes 18 let - a číslo stále roste! Knihovny, které poskytujeme, jsou proto **velmi zralé, stabilní a široce používané**. Věří jim řada globálních korporací a pohání mnoho významných webových stránek.
Kdo používá a důvěřuje Nette?
+
+
+
+
+
+Instalace
+=========
+
+Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|best-practices:composer]:
+
+```shell .[dark]
+composer require nette/database
+```
+
+
+Podporované databáze
+--------------------
+Nette Database podporuje následující databáze:
+|* Databázový server |* DSN jméno |* Podpora v Explorer
+| MySQL (>= 5.1) | mysql | ANO
+| PostgreSQL (>= 9.0) | pgsql | ANO
+| Sqlite 3 (>= 3.8) | sqlite | ANO
+| Oracle | oci | -
+| MS SQL (PDO_SQLSRV) | sqlsrv | ANO
+| MS SQL (PDO_DBLIB) | mssql | -
+| ODBC | odbc | -
{{title: Nette Database}}
diff --git a/database/cs/@left-menu.texy b/database/cs/@left-menu.texy
index 8353639528..ddeadbab96 100644
--- a/database/cs/@left-menu.texy
+++ b/database/cs/@left-menu.texy
@@ -1,8 +1,12 @@
Databáze
********
-- [Core]
+- [Úvod |guide]
+- [Direct SQL]
- [Explorer]
+- [Transakce |transactions]
+- [Výjimky |exceptions]
- [Reflexe |reflection]
+- [Mapování |mapper]
- [Konfigurace |configuration]
- [Bezpečnostní rizika |security]
- [Upgrade |upgrading]
diff --git a/database/cs/core.texy b/database/cs/core.texy
index 02c3104e43..096e5311b0 100644
--- a/database/cs/core.texy
+++ b/database/cs/core.texy
@@ -1,350 +1 @@
-Database Core
-*************
-
-.[perex]
-Nette Database Core je základní vrstva pro přístup k databázi, tzv. database abstraction layer.
-
-
-Instalace
-=========
-
-Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|best-practices:composer]:
-
-```shell
-composer require nette/database
-```
-
-
-Připojení a konfigurace
-=======================
-
-Pro připojení k databázi stačí vytvořit instanci třídy [api:Nette\Database\Connection]:
-
-```php
-$database = new Nette\Database\Connection($dsn, $user, $password);
-```
-
-Parametr `$dsn` (data source name) je stejný, [jaký používá PDO |https://www.php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters], např. `host=127.0.0.1;dbname=test`. V případě selhání vyhodí výjimku `Nette\Database\ConnectionException`.
-
-Nicméně šikovnější způsob nabízí [aplikační konfigurace |configuration], kam stačí přidat sekci `database` a vytvoří se potřebné objekty a také databázový panel v [Tracy |tracy:] baru.
-
-```neon
-database:
- dsn: 'mysql:host=127.0.0.1;dbname=test'
- user: root
- password: password
-```
-
-Poté objekt spojení [získáme jako službu z DI kontejneru |dependency-injection:passing-dependencies], např.:
-
-```php
-class Model
-{
- // pro práci s vrstvou Database Explorer si předáme Nette\Database\Explorer
- public function __construct(
- private Nette\Database\Connection $database,
- ) {
- }
-}
-```
-
-Více informací o [konfiguraci databáze|configuration].
-
-
-Dotazy
-======
-
-Databázové dotazy pokládáme metodou `query()`, která vrací [ResultSet |api:Nette\Database\ResultSet].
-
-```php
-$result = $database->query('SELECT * FROM users');
-
-foreach ($result as $row) {
- echo $row->id;
- echo $row->name;
-}
-
-echo $result->getRowCount(); // vrací počet řádků výsledku, pokud je znám
-```
-
-.[note]
-Nad `ResultSet` je možné iterovat pouze jednou, pokud potřebujeme iterovat vícekrát, je nutno výsledek převést na pole metodou `fetchAll()`.
-
-Do dotazu lze velmi snadno přidávat i parametry, všimněte si otazníku:
-
-```php
-$database->query('SELECT * FROM users WHERE name = ?', $name);
-
-$database->query('SELECT * FROM users WHERE name = ? AND active = ?', $name, $active);
-
-$database->query('SELECT * FROM users WHERE id IN (?)', $ids); // $ids je pole
-```
-
-
-POZOR, nikdy dotazy neskládejte jako řetězce, vznikla by zranitelnost [SQL injection |https://cs.wikipedia.org/wiki/SQL_injection]
-/--
-$db->query('SELECT * FROM users WHERE name = ' . $name); // ŠPATNĚ!!!
-\--
-
-
-V případě selhání `query()` vyhodí buď `Nette\Database\DriverException` nebo některého z potomků:
-
-- [ConstraintViolationException |api:Nette\Database\ConstraintViolationException] - porušení nějakého omezení pro tabulku
-- [ForeignKeyConstraintViolationException |api:Nette\Database\ForeignKeyConstraintViolationException] - neplatný cizí klíč
-- [NotNullConstraintViolationException |api:Nette\Database\NotNullConstraintViolationException] - porušení podmínky NOT NULL
-- [UniqueConstraintViolationException |api:Nette\Database\UniqueConstraintViolationException] - koliduje unikátní index
-
-Kromě `query()` jsou tu další užitečné funkce:
-
-```php
-// vrátí asociativní pole id => name
-$pairs = $database->fetchPairs('SELECT id, name FROM users');
-
-// vrátí všechny záznamy jako pole
-$rows = $database->fetchAll('SELECT * FROM users');
-
-// vrátí jeden záznam
-$row = $database->fetch('SELECT * FROM users WHERE id = ?', $id);
-
-// vrátí přímo hodnotu buňky
-$name = $database->fetchField('SELECT name FROM users WHERE id = ?', $id);
-```
-
-V případě selhání všechny tyto metody vyhodí `Nette\Database\DriverException`.
-
-
-Insert, Update & Delete
-=======================
-
-Parameterem, který vkládáme do SQL dotazu, může být i pole (v takovém případě je navíc možné zástupný symbol `?` vynechat), což se hodí třeba pro sestavení příkazu `INSERT`:
-
-```php
-$database->query('INSERT INTO users ?', [ // tady můžeme otazník vynechat
- 'name' => $name,
- 'year' => $year,
-]);
-// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)
-
-$id = $database->getInsertId(); // vrátí auto-increment vloženého záznamu
-
-$id = $database->getInsertId($sequence); // nebo hodnotu sekvence
-```
-
-Vícenásobný INSERT:
-
-```php
-$database->query('INSERT INTO users', [
- [
- 'name' => 'Jim',
- 'year' => 1978,
- ], [
- 'name' => 'Jack',
- 'year' => 1987,
- ],
-]);
-// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
-```
-
-Jako parametry můžeme předávat i soubory, objekty DateTime nebo [výčtové typy |https://www.php.net/enumerations]:
-
-```php
-$database->query('INSERT INTO users', [
- 'name' => $name,
- 'created' => new DateTime, // nebo $database::literal('NOW()')
- 'avatar' => fopen('image.gif', 'r'), // vloží soubor
- 'status' => State::New, // enum State
-]);
-```
-
-Úprava záznamů:
-
-```php
-$result = $database->query('UPDATE users SET', [
- 'name' => $name,
- 'year' => $year,
-], 'WHERE id = ?', $id);
-// UPDATE users SET `name` = 'Jim', `year` = 1978 WHERE id = 123
-
-echo $result->getRowCount(); // vrací počet ovlivněných řádků
-```
-
-Pro UPDATE můžeme využít operátorů `+=` a `-=`:
-
-```php
-$database->query('UPDATE users SET', [
- 'age+=' => 1, // všimněte si +=
-], 'WHERE id = ?', $id);
-// UPDATE users SET `age` = `age` + 1
-```
-
-Mazání:
-
-```php
-$result = $database->query('DELETE FROM users WHERE id = ?', $id);
-echo $result->getRowCount(); // vrací počet ovlivněných řádků
-```
-
-
-Pokročilé dotazy
-================
-
-Vložení, nebo úprava záznamu, pokud již existuje:
-
-```php
-$database->query('INSERT INTO users', [
- 'id' => $id,
- 'name' => $name,
- 'year' => $year,
-], 'ON DUPLICATE KEY UPDATE', [
- 'name' => $name,
- 'year' => $year,
-]);
-// INSERT INTO users (`id`, `name`, `year`) VALUES (123, 'Jim', 1978)
-// ON DUPLICATE KEY UPDATE `name` = 'Jim', `year` = 1978
-```
-
-Všimněte si, že Nette Database pozná, v jakém kontextu SQL příkazu parametr s polem vkládáme a podle toho z něj sestaví SQL kód. Takže z prvního pole sestavil `(id, name, year) VALUES (123, 'Jim', 1978)`, zatímco druhé převedl do podoby `name = 'Jim', year = 1978`.
-
-Také řazení můžeme ovlivnit polem, v klíčích uvedeme sloupce a hodnotou bude boolean určující, zda řadit vzestupně:
-
-```php
-$database->query('SELECT id FROM author ORDER BY', [
- 'id' => true, // vzestupně
- 'name' => false, // sestupně
-]);
-// SELECT id FROM author ORDER BY `id`, `name` DESC
-```
-
-Pokud by u neobvyklé konstrukce detekce nezafungovala, můžete formu sestavení určit zástupným symbolem `?` doplněným o hint. Podporovány jsou tyto hinty:
-
-| ?values | (key1, key2, ...) VALUES (value1, value2, ...)
-| ?set | key1 = value1, key2 = value2, ...
-| ?and | key1 = value1 AND key2 = value2 ...
-| ?or | key1 = value1 OR key2 = value2 ...
-| ?order | key1 ASC, key2 DESC
-
-V klauzuli WHERE se používá operátor `?and`, takže podmínky se spojují operátorem `AND`:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name' => $name,
- 'year' => $year,
-]);
-// SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978
-```
-
-Což můžeme snadno změnit na `OR` tím, že uvedeme zástupný symbol `?or`:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE ?or', [
- 'name' => $name,
- 'year' => $year,
-]);
-// SELECT * FROM users WHERE `name` = 'Jim' OR `year` = 1978
-```
-
-V podmínkách můžeme používat operátory:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name <>' => $name,
- 'year >' => $year,
-]);
-// SELECT * FROM users WHERE `name` <> 'Jim' AND `year` > 1978
-```
-
-A také výčty:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name' => ['Jim', 'Jack'],
- 'role NOT IN' => ['admin', 'owner'], // výčet + operátor NOT IN
-]);
-// SELECT * FROM users WHERE
-// `name` IN ('Jim', 'Jack') AND `role` NOT IN ('admin', 'owner')
-```
-
-Do podmínky také můžeme vložit kus vlastního SQL kódu pomocí tzv. SQL literálu:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name' => $name,
- 'year >' => $database::literal('YEAR()'),
-]);
-// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
-```
-
-Nebo alternativě:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name' => $name,
- $database::literal('year > YEAR()'),
-]);
-// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
-```
-
-SQL literál také může mít své parametry:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name' => $name,
- $database::literal('year > ? AND year < ?', $min, $max),
-]);
-// SELECT * FROM users WHERE `name` = 'Jim' AND (year > 1978 AND year < 2017)
-```
-
-Díky čemuž můžeme vytvářet zajímavé kombinace:
-
-```php
-$result = $database->query('SELECT * FROM users WHERE', [
- 'name' => $name,
- $database::literal('?or', [
- 'active' => true,
- 'role' => $role,
- ]),
-]);
-// SELECT * FROM users WHERE `name` = 'Jim' AND (`active` = 1 OR `role` = 'admin')
-```
-
-
-Proměnný název
-==============
-
-Ještě existuje zástupný symbol `?name`, který využijete v případě, že název tabulky nebo sloupce je proměnnou. (Pozor, nedovolte uživateli manipulovat s obsahem takové proměnné):
-
-```php
-$table = 'blog.users';
-$column = 'name';
-$database->query('SELECT * FROM ?name WHERE ?name = ?', $table, $column, $name);
-// SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'
-```
-
-
-Transakce
-=========
-
-Pro práci s transakcemi slouží trojice metod:
-
-```php
-$database->beginTransaction(); // zahájení transakce
-
-$database->commit(); // potvrzení
-
-$database->rollback(); // vrácení zpět
-```
-
-Elegantní způsob nabízí metoda `transaction()`, které předáme callback, který se vykoná v transakci. Pokud během vykonávání dojde k vyhození výjimky, transakce se zahodí, pokud vše proběhne v pořádku, transakce se potvrdí.
-
-```php
-$id = $database->transaction(function ($database) {
- $database->query('DELETE FROM ...');
- $database->query('INSERT INTO ...');
- // ...
- return $database->getInsertId();
-});
-```
-
-Jak vidíte, metoda `transaction()` vrací návratovou hodnotu callbacku.
-
-Volání `transaction()` může být i zanořeno, což zjednodušuje implementaci nezávislých repozitářů.
+{{redirect: guide}}
diff --git a/database/cs/direct-sql.texy b/database/cs/direct-sql.texy
new file mode 100644
index 0000000000..e0f5d3ec7a
--- /dev/null
+++ b/database/cs/direct-sql.texy
@@ -0,0 +1,516 @@
+Direct SQL
+**********
+
+.[perex]
+S Nette Database můžete pracovat dvěma způsoby - buď přímo psát SQL dotazy (Direct přístup), nebo nechat SQL generovat automaticky ([Explorer přístup|explorer]). Direct přístup vám pomůže s bezpečným sestavováním dotazů, ale zachovává vám plnou kontrolu nad jejich podobou.
+
+.[note]
+Informace o vytvoření připojení a konfiguraci najdete na [samostatné stránce |guide#Připojení a konfigurace].
+
+
+Pokládání SQL dotazů
+====================
+
+Pro dotazování do databáze slouží metoda `query()`. Ta vrací objekt [ResultSet |api:Nette\Database\ResultSet], který reprezentuje výsledek dotazu. V případě selhání metoda [vyhodí výjimku|exceptions].
+Výsledek dotazu můžeme procházet pomocí cyklu `foreach`, nebo použít některou z [pomocných funkcí|#Získání dat].
+
+```php
+$result = $database->query('SELECT * FROM users');
+
+foreach ($result as $row) {
+ echo $row->id;
+ echo $row->name;
+}
+```
+
+Pro bezpečné vkládání hodnot do SQL dotazů používáme parametrizované dotazy. Nette Database je dělá maximálně jednoduché - stačí za SQL dotaz přidat čárku a hodnotu:
+
+```php
+$database->query('SELECT * FROM users WHERE name = ?', $name);
+```
+
+Při více parametrech máte dvě možnosti zápisu. Buď můžete SQL dotaz "prokládat" parametry:
+
+```php
+$database->query('SELECT * FROM users WHERE name = ?', $name, 'AND age > ?', $age);
+```
+
+Nebo napsat nejdříve celý SQL dotaz a pak připojit všechny parametry:
+
+```php
+$database->query('SELECT * FROM users WHERE name = ? AND age > ?', $name, $age);
+```
+
+
+Ochrana před SQL injection
+==========================
+
+Proč je důležité používat parametrizované dotazy? Protože vás chrání před útokem zvaným SQL injection, při kterém by útočník mohl podstrčit vlastní SQL příkazy a tím získat nebo poškodit data v databázi.
+
+.[warning]
+**Nikdy nevkládejte proměnné přímo do SQL dotazu!** Vždy používejte parametrizované dotazy, které vás ochrání před SQL injection.
+
+```php
+// ❌ NEBEZPEČNÝ KÓD - zranitelný vůči SQL injection
+$database->query("SELECT * FROM users WHERE name = '$name'");
+
+// ✅ Bezpečný parametrizovaný dotaz
+$database->query('SELECT * FROM users WHERE name = ?', $name);
+```
+
+Seznamte se s [možnými bezpečnostními riziky |security].
+
+
+Techniky dotazování
+===================
+
+
+Podmínky WHERE
+--------------
+
+Podmínky WHERE můžete zapsat jako asociativní pole, kde klíče jsou názvy sloupců a hodnoty jsou data pro porovnání. Nette Database automaticky vybere nejvhodnější SQL operátor podle typu hodnoty.
+
+```php
+$database->query('SELECT * FROM users WHERE', [
+ 'name' => 'John',
+ 'active' => true,
+]);
+// WHERE `name` = 'John' AND `active` = 1
+```
+
+V klíči můžete také explicitně specifikovat operátor pro porovnání:
+
+```php
+$database->query('SELECT * FROM users WHERE', [
+ 'age >' => 25, // použije operátor >
+ 'name LIKE' => '%John%', // použije operátor LIKE
+ 'email NOT LIKE' => '%example.com%', // použije operátor NOT LIKE
+]);
+// WHERE `age` > 25 AND `name` LIKE '%John%' AND `email` NOT LIKE '%example.com%'
+```
+
+Nette automaticky ošetřuje speciální případy jako `null` hodnoty nebo pole.
+
+```php
+$database->query('SELECT * FROM products WHERE', [
+ 'name' => 'Laptop', // použije operátor =
+ 'category_id' => [1, 2, 3], // použije IN
+ 'description' => null, // použije IS NULL
+]);
+// WHERE `name` = 'Laptop' AND `category_id` IN (1, 2, 3) AND `description` IS NULL
+```
+
+Pro negativní podmínky použijte operátor `NOT`:
+
+```php
+$database->query('SELECT * FROM products WHERE', [
+ 'name NOT' => 'Laptop', // použije operátor <>
+ 'category_id NOT' => [1, 2, 3], // použije NOT IN
+ 'description NOT' => null, // použije IS NOT NULL
+ 'id' => [], // vynechá se
+]);
+// WHERE `name` <> 'Laptop' AND `category_id` NOT IN (1, 2, 3) AND `description` IS NOT NULL
+```
+
+Pro spojování podmínek se používá operátor `AND`. To lze změnit pomocí [zástupného symbolu ?or|#Hinty pro sestavování SQL].
+
+
+Pravidla ORDER BY
+-----------------
+
+Řazení `ORDER BY` se dá zapsat pomocí pole. V klíčích uvedeme sloupce a hodnotou bude boolean určující, zda řadit vzestupně:
+
+```php
+$database->query('SELECT id FROM author ORDER BY', [
+ 'id' => true, // vzestupně
+ 'name' => false, // sestupně
+]);
+// SELECT id FROM author ORDER BY `id`, `name` DESC
+```
+
+
+Vkládání dat (INSERT)
+---------------------
+
+Pro vkládání záznamů se používá SQL příkaz `INSERT`.
+
+```php
+$values = [
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+];
+$database->query('INSERT INTO users ?', $values);
+$userId = $database->getInsertId();
+```
+
+Metoda `getInsertId()` vrátí ID naposledy vloženého řádku. U některých databází (např. PostgreSQL) je nutné jako parametr specifikovat název sekvence, ze které se má ID generovat pomocí `$database->getInsertId($sequenceId)`.
+
+Jako parametry můžeme předávat i [#speciální hodnoty] jako soubory, objekty DateTime nebo výčtové typy.
+
+Vložení více záznamů najednou:
+
+```php
+$database->query('INSERT INTO users ?', [
+ ['name' => 'User 1', 'email' => 'user1@mail.com'],
+ ['name' => 'User 2', 'email' => 'user2@mail.com'],
+]);
+```
+
+Vícenásobný INSERT je mnohem rychlejší, protože se provede jediný databázový dotaz, namísto mnoha jednotlivých.
+
+**Bezpečnostní upozornění:** Nikdy nepoužívejte jako `$values` nevalidovaná data. Seznamte se s [možnými riziky |security#Klíče polí nejsou bezpečné API].
+
+
+Aktualizace dat (UPDATE)
+------------------------
+
+Pro aktualizacizáznamů se používá SQL příkaz `UPDATE`.
+
+```php
+// Aktualizace jednoho záznamu
+$values = [
+ 'name' => 'John Smith',
+];
+$result = $database->query('UPDATE users SET ? WHERE id = ?', $values, 1);
+```
+
+Počet ovlivněných řádků vrátí `$result->getRowCount()`.
+
+Pro UPDATE můžeme využít operátorů `+=` a `-=`:
+
+```php
+$database->query('UPDATE users SET ? WHERE id = ?', [
+ 'login_count+=' => 1, // inkrementace login_count
+], 1);
+```
+
+Příklad vložení, nebo úpravy záznamu, pokud již existuje. Použijeme techniku `ON DUPLICATE KEY UPDATE`:
+
+```php
+$values = [
+ 'name' => $name,
+ 'year' => $year,
+];
+$database->query('INSERT INTO users ? ON DUPLICATE KEY UPDATE ?',
+ $values + ['id' => $id],
+ $values,
+);
+// INSERT INTO users (`id`, `name`, `year`) VALUES (123, 'Jim', 1978)
+// ON DUPLICATE KEY UPDATE `name` = 'Jim', `year` = 1978
+```
+
+Všimněte si, že Nette Database pozná, v jakém kontextu SQL příkazu parametr s polem vkládáme a podle toho z něj sestaví SQL kód. Takže z prvního pole sestavil `(id, name, year) VALUES (123, 'Jim', 1978)`, zatímco druhé převedl do podoby `name = 'Jim', year = 1978`. Podroběji se tomu věnujeme v části [Hinty pro sestavování SQL|#Hinty pro sestavování SQL].
+
+
+Mazání dat (DELETE)
+-------------------
+
+Pro mazání záznamů se používá SQL příkaz `DELETE`. Příklad se získáním počtu smazaných řádků:
+
+```php
+$count = $database->query('DELETE FROM users WHERE id = ?', 1)
+ ->getRowCount();
+```
+
+
+Hinty pro sestavování SQL
+-------------------------
+
+Hint je speciální zástupný symbol v SQL dotazu, který říká, jak se má hodnota parametru přepsat do SQL výrazu:
+
+| Hint | Popis | Automaticky se použije
+|-----------|-------------------------------------------------|-----------------------------
+| `?name` | použije pro vložení názvu tabulky nebo sloupce | -
+| `?values` | vygeneruje `(key, ...) VALUES (value, ...)` | `INSERT ... ?`, `REPLACE ... ?`
+| `?set` | vygeneruje přiřazení `key = value, ...` | `SET ?`, `KEY UPDATE ?`
+| `?and` | spojí podmínky v poli operátorem `AND` | `WHERE ?`, `HAVING ?`
+| `?or` | spojí podmínky v poli operátorem `OR` | -
+| `?order` | vygeneruje klauzuli `ORDER BY` | `ORDER BY ?`, `GROUP BY ?`
+
+Pro dynamické vkládání názvů tabulek a sloupců do dotazu slouží zástupný symbol `?name`. Nette Database se postará o správné ošetření identifikátorů podle konvencí dané databáze (např. uzavření do zpětných uvozovek v MySQL).
+
+```php
+$table = 'users';
+$column = 'name';
+$database->query('SELECT ?name FROM ?name WHERE id = 1', $column, $table);
+// SELECT `name` FROM `users` WHERE id = 1 (v MySQL)
+```
+
+**Upozornění:** symbol `?name` používejte pouze pro názvy tabulek a sloupců z validovaných vstupů, jinak se vystavujete [bezpečnostnímu riziku |security#Dynamické identifikátory].
+
+Ostatní hinty obvykle není potřeba uvádět, neboť Nette používá při skládání SQL dotazu chytrou autodetekci (viz třetí sloupec tabulky). Ale můžete jej využít například v situaci, kdy chcete spojit podmínky pomocí `OR` namísto `AND`:
+
+```php
+$database->query('SELECT * FROM users WHERE ?or', [
+ 'name' => 'John',
+ 'email' => 'john@example.com',
+]);
+// SELECT * FROM users WHERE `name` = 'John' OR `email` = 'john@example.com'
+```
+
+
+Speciální hodnoty
+-----------------
+
+Kromě běžných skalárních typů (string, int, bool) můžete jako parametry předávat i speciální hodnoty:
+
+- soubory: `fopen('image.gif', 'r')` vloží binární obsah souboru
+- datum a čas: objekty `DateTime` se převedou na databázový formát
+- výčtové typy: instance `enum` se převedou na jejich hodnotu
+- SQL literály: vytvořené pomocí `Connection::literal('NOW()')` se vloží přímo do dotazu
+
+```php
+$database->query('INSERT INTO articles ?', [
+ 'title' => 'My Article',
+ 'published_at' => new DateTime,
+ 'content' => fopen('image.png', 'r'),
+ 'state' => Status::Draft,
+]);
+```
+
+U databází, které nemají nativní podporu pro datový typ `datetime` (jako SQLite a Oracle), se `DateTime` převádí na hodnotu určenou v [konfiguraci databáze|configuration] položkou `formatDateTime` (výchozí hodnota je `U` - unix timestamp).
+
+
+SQL literály
+------------
+
+V některých případech potřebujete jako hodnotu uvést přímo SQL kód, který se ale nemá chápat jako řetězec a escapovat. K tomuto slouží objekty třídy `Nette\Database\SqlLiteral`. Vytváří je metoda `Connection::literal()`.
+
+```php
+$result = $database->query('SELECT * FROM users WHERE', [
+ 'name' => $name,
+ 'year >' => $database::literal('YEAR()'),
+]);
+// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
+```
+
+Nebo alternativě:
+
+```php
+$result = $database->query('SELECT * FROM users WHERE', [
+ 'name' => $name,
+ $database::literal('year > YEAR()'),
+]);
+// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
+```
+
+SQL literály mohou obsahovat parametry:
+
+```php
+$result = $database->query('SELECT * FROM users WHERE', [
+ 'name' => $name,
+ $database::literal('year > ? AND year < ?', $min, $max),
+]);
+// SELECT * FROM users WHERE `name` = 'Jim' AND (year > 1978 AND year < 2017)
+```
+
+Díky čemuž můžeme vytvářet zajímavé kombinace:
+
+```php
+$result = $database->query('SELECT * FROM users WHERE', [
+ 'name' => $name,
+ $database::literal('?or', [
+ 'active' => true,
+ 'role' => $role,
+ ]),
+]);
+// SELECT * FROM users WHERE `name` = 'Jim' AND (`active` = 1 OR `role` = 'admin')
+```
+
+
+Získání dat
+===========
+
+
+Zkratky pro SELECT dotazy
+-------------------------
+
+Pro zjednodušení načítání dat nabízí `Connection` několik zkratek, které kombinují volání `query()` s následujícím `fetch*()`. Tyto metody přijímají stejné parametry jako `query()`, tedy SQL dotaz a volitelné parametry.
+Plnohodnotný popis metod `fetch*()` najdete [níže|#fetch()].
+
+| `fetch($sql, ...$params): ?Row` | Provede dotaz a vrátí první řádek jako objekt `Row`
+| `fetchAll($sql, ...$params): array` | Provede dotaz a vrátí všechny řádky jako pole objektů `Row`
+| `fetchPairs($sql, ...$params): array` | Provede dotaz a vrátí asocitivní pole, kde první sloupec představuje klíč a druhý hodnotu
+| `fetchField($sql, ...$params): mixed` | Provede dotaz a vrátí hodnotu prvního políčka z prvního řádku
+| `fetchList($sql, ...$params): ?array` | Provede dotaz a vrací první řádek jako indexované pole
+
+Příklad:
+
+```php
+// fetchField() - vrátí hodnotu první buňky
+$count = $database->query('SELECT COUNT(*) FROM articles')
+ ->fetchField();
+```
+
+
+`foreach` - iterace přes řádky
+------------------------------
+
+Po vykonání dotazu se vrací objekt [ResultSet|api:Nette\Database\ResultSet], který umožňuje procházet výsledky několika způsoby.
+Nejsnazší způsob, jak vykonat dotaz a získat řádky, je iterováním v cyklu `foreach`. Tento způsob je paměťově nejúspornější, neboť vrací data postupně a neukládá si je do paměti najednou.
+
+```php
+$result = $database->query('SELECT * FROM users');
+
+foreach ($result as $row) {
+ echo $row->id;
+ echo $row->name;
+ // ...
+}
+```
+
+.[note]
+`ResultSet` lze iterovat pouze jednou. Pokud potřebujete iterovat opakovaně, musíte nejprve načíst data do pole, například pomocí metody `fetchAll()`.
+
+
+fetch(): ?Row .[method]
+-----------------------
+
+Vrací řádek jako objekt `Row`. Pokud už neexistují další řádky, vrací `null`. Posune interní ukazatel na další řádek.
+
+```php
+$result = $database->query('SELECT * FROM users');
+$row = $result->fetch(); // načte první řádek
+if ($row) {
+ echo $row->name;
+}
+```
+
+
+fetchAll(): array .[method]
+---------------------------
+
+Vrací všechny zbývající řádky z `ResultSetu` jako pole objektů `Row`.
+
+```php
+$result = $database->query('SELECT * FROM users');
+$rows = $result->fetchAll(); // načte všechny řádky
+foreach ($rows as $row) {
+ echo $row->name;
+}
+```
+
+
+fetchPairs(string|int|null $key = null, string|int|null $value = null): array .[method]
+---------------------------------------------------------------------------------------
+
+Vrátí výsledky jako asociativní pole. První argument určuje název sloupce, který se použije jako klíč v poli, druhý argument určuje název sloupce, který se použije jako hodnota:
+
+```php
+$result = $database->query('SELECT id, name FROM users');
+$names = $result->fetchPairs('id', 'name');
+// [1 => 'John Doe', 2 => 'Jane Doe', ...]
+```
+
+Pokud uvedeme pouze první parametr, bude hodnotou celý řádek, tedy objekt `Row`:
+
+```php
+$rows = $result->fetchPairs('id');
+// [1 => Row(id: 1, name: 'John'), 2 => Row(id: 2, name: 'Jane'), ...]
+```
+
+Pokud jako klíč uvedeme `null`, bude pole indexováno numericky od nuly:
+
+```php
+$names = $result->fetchPairs(null, 'name');
+// [0 => 'John Doe', 1 => 'Jane Doe', ...]
+```
+
+
+fetchPairs(Closure $callback): array .[method]
+----------------------------------------------
+
+Alternativně můžete jako parametr uvést callback, který bude pro každý řádek vracet buď samotnou hodnotu, nebo dvojici klíč-hodnota.
+
+```php
+$result = $database->query('SELECT * FROM users');
+$items = $result->fetchPairs(fn($row) => "$row->id - $row->name");
+// ['1 - John', '2 - Jane', ...]
+
+// Callback může také vracet pole s dvojicí klíč & hodnota:
+$names = $result->fetchPairs(fn($row) => [$row->name, $row->age]);
+// ['John' => 46, 'Jane' => 21, ...]
+```
+
+
+fetchField(): mixed .[method]
+-----------------------------
+
+Vrací hodnotu prvního políčka z aktuálního řádku. Pokud už neexistují další řádky, vrací `null`. Posune interní ukazatel na další řádek.
+
+```php
+$result = $database->query('SELECT name FROM users');
+$name = $result->fetchField(); // načte jméno z prvního řádku
+```
+
+
+fetchList(): ?array .[method]
+-----------------------------
+
+Vrací řádek jako indexované pole. Pokud už neexistují další řádky, vrací `null`. Posune interní ukazatel na další řádek.
+
+```php
+$result = $database->query('SELECT name, email FROM users');
+$row = $result->fetchList(); // ['John', 'john@example.com']
+```
+
+
+getRowCount(): ?int .[method]
+-----------------------------
+
+Vrací počet ovlivněných řádků posledním dotazem `UPDATE` nebo `DELETE`. Pro `SELECT` je to počet vrácených řádků, ale ten nemusí být znám - v takovém případě metoda vrátí `null`.
+
+
+getColumnCount(): ?int .[method]
+--------------------------------
+
+Vrací počet sloupců v `ResultSetu`.
+
+
+Informace o dotazech
+====================
+
+Pro ladicí účely můžeme získat informace o posledním provedeném dotazu:
+
+```php
+echo $database->getLastQueryString(); // vypíše SQL dotaz
+
+$result = $database->query('SELECT * FROM articles');
+echo $result->getQueryString(); // vypíše SQL dotaz
+echo $result->getTime(); // vypíše dobu vykonání v sekundách
+```
+
+Pro zobrazení výsledku jako HTML tabulky lze použít:
+
+```php
+$result = $database->query('SELECT * FROM articles');
+$result->dump();
+```
+
+ResultSet nabízí informace o typech sloupců:
+
+```php
+$result = $database->query('SELECT * FROM articles');
+$types = $result->getColumnTypes();
+
+foreach ($types as $column => $type) {
+ echo "$column je typu $type->type"; // např. 'id je typu int'
+}
+```
+
+
+Logování dotazů
+---------------
+
+Můžeme implementovat vlastní logování dotazů. Událost `onQuery` je pole callbacků, které se zavolají po každém provedeném dotazu:
+
+```php
+$database->onQuery[] = function ($database, $result) use ($logger) {
+ $logger->info('Query: ' . $result->getQueryString());
+ $logger->info('Time: ' . $result->getTime());
+
+ if ($result->getRowCount() > 1000) {
+ $logger->warning('Large result set: ' . $result->getRowCount() . ' rows');
+ }
+};
+```
diff --git a/database/cs/exceptions.texy b/database/cs/exceptions.texy
new file mode 100644
index 0000000000..023523c2ec
--- /dev/null
+++ b/database/cs/exceptions.texy
@@ -0,0 +1,36 @@
+Výjimky
+*******
+
+Nette Database používá hierarchii výjimek. Základní třídou je `Nette\Database\DriverException`, která dědí z `PDOException` a poskytuje rozšířené možnosti pro práci s chybami databáze:
+
+- Metoda `getDriverCode()` vrací kód chyby od databázového driveru
+- Metoda `getSqlState()` vrací SQLSTATE kód
+- Metody `getQueryString()` a `getParameters()` umožňují získat původní dotaz a jeho parametry
+
+Z `DriverException` dědí následující specializované výjimky:
+
+- `ConnectionException` - signalizuje selhání připojení k databázovému serveru
+- `ConstraintViolationException` - základní třída pro porušení databázových omezení, ze které dědí:
+ - `ForeignKeyConstraintViolationException` - porušení cizího klíče
+ - `NotNullConstraintViolationException` - porušení NOT NULL omezení
+ - `UniqueConstraintViolationException` - porušení unikátnosti hodnoty
+
+
+Příklad zachytávání výjimky `UniqueConstraintViolationException`, která nastane, když se snažíme vložit uživatele s emailem, který už v databázi existuje (za předpokladu, že sloupec email má unikátní index).
+
+```php
+try {
+ $database->query('INSERT INTO users', [
+ 'email' => 'john@example.com',
+ 'name' => 'John Doe',
+ 'password' => $hashedPassword,
+ ]);
+} catch (Nette\Database\UniqueConstraintViolationException $e) {
+ echo 'Uživatel s tímto emailem již existuje.';
+
+} catch (Nette\Database\DriverException $e) {
+ echo 'Došlo k chybě při registraci: ' . $e->getMessage();
+}
+```
+
+
diff --git a/database/cs/explorer.texy b/database/cs/explorer.texy
index 6dd5669964..6e9f1f2218 100644
--- a/database/cs/explorer.texy
+++ b/database/cs/explorer.texy
@@ -3,7 +3,7 @@ Database Explorer
-Nette Database Explorer je výkonná vrstva, která zásadním způsobem zjednodušuje získávání dat z databáze bez nutnosti psát SQL dotazy.
+S Nette Database můžete pracovat dvěma způsoby - buď nechat SQL dotazy generovat automaticky (Explorer přístup), nebo je psát sami ([Direct přístup|direct-sql]). Explorer zásadním způsobem zjednodušuje přístup k datům. Stará se o práci se vztahy mezi tabulkami, takže se můžete soustředit na logiku vaší aplikace.
- Práce s daty je přirozená a snadno pochopitelná
- Generuje optimalizované SQL dotazy, které načítají pouze potřebná data
@@ -12,9 +12,8 @@ Nette Database Explorer je výkonná vrstva, která zásadním způsobem zjednod
-Nette Database Explorer je nadstavbou nad nízkoúrovňovou vrstou [Nette Database Core |core], která přidává komfortní objektově-orientovaný přístup k databázi.
-
-Práce s Explorerem začíná voláním metody `table()` nad objektem [api:Nette\Database\Explorer] (jak ho získat je [popsáno tady |core#Připojení a konfigurace]):
+.[note]
+Práce s Explorerem začíná voláním metody `table()` nad objektem [api:Nette\Database\Explorer] (informace o vytvoření připojení a konfiguraci najdete na [samostatné stránce |guide#Připojení a konfigurace]):
```php
$books = $explorer->table('book'); // 'book' je jméno tabulky
@@ -76,7 +75,7 @@ Metody automaticky escapují parametry a uvozují identifikátory (názvy tabule
- Řetězce vždy dosazujte přes **parametry**.
```php
-where('name = ' . $name); // KATASTROFA: zranitelné vůči SQL injection
+where('name = ' . $name); // KRITICKÁ ZRANITELNOST: SQL injection
where('name LIKE "%search%"'); // ŠPATNĚ: komplikuje automatické uvozování
where('name LIKE ?', '%search%'); // SPRÁVNĚ: hodnota dosazená přes parametr
@@ -340,7 +339,7 @@ if ($book) {
fetch(): ?ActiveRow .[method]
-----------------------------
-Vrací jeden řádek a posune interní ukazatel na další. Pokud už neexistují další řádky, vrací `null`.
+Vrací řádek a posune interní ukazatel na další. Pokud už neexistují další řádky, vrací `null`.
```php
$books = $explorer->table('book');
@@ -350,8 +349,8 @@ while ($book = $books->fetch()) {
```
-fetchPairs(): array .[method]
------------------------------
+fetchPairs(string|int|null $key = null, string|int|null $value = null): array .[method]
+---------------------------------------------------------------------------------------
Vrátí výsledky jako asociativní pole. První argument určuje název sloupce, který se použije jako klíč v poli, druhý argument určuje název sloupce, který se použije jako hodnota:
@@ -360,7 +359,7 @@ $authors = $explorer->table('author')->fetchPairs('id', 'name');
// [1 => 'John Doe', 2 => 'Jane Doe', ...]
```
-Pokud je zadán pouze název sloupce pro klíč, bude hodnotou celý řadek, tedy objekt `ActiveRow`:
+Pokud uvedeme pouze první parametr, bude hodnotou celý řadek, tedy objekt `ActiveRow`:
```php
$authors = $explorer->table('author')->fetchPairs('id');
@@ -374,12 +373,16 @@ $authors = $explorer->table('author')->fetchPairs(null, 'name');
// [0 => 'John Doe', 1 => 'Jane Doe', ...]
```
-Jako parametr můžeme také uvést callback, který bude pro každý řádek vracet buď samotnou hodnotu, nebo dvojici klíč-hodnota. Pokud callback vrací pouze hodnotu, klíčem bude primární klíč řádku:
+
+fetchPairs(Closure $callback): array .[method]
+----------------------------------------------
+
+Alternativně můžete jako parametr uvést callback, který bude pro každý řádek vracet buď samotnou hodnotu, nebo dvojici klíč-hodnota.
```php
$titles = $explorer->table('book')
->fetchPairs(fn($row) => "$row->title ({$row->author->name})");
-// [1 => 'První kniha (Jan Novák)', ...]
+// ['První kniha (Jan Novák)', ...]
// Callback může také vracet pole s dvojicí klíč & hodnota:
$titles = $explorer->table('book')
diff --git a/database/cs/guide.texy b/database/cs/guide.texy
new file mode 100644
index 0000000000..18277a4a00
--- /dev/null
+++ b/database/cs/guide.texy
@@ -0,0 +1,177 @@
+Nette Database
+**************
+
+.[perex]
+Nette Database je výkonná a elegantní databázová vrstva pro PHP, která vyniká svou jednoduchostí použití a chytrými funkcemi. Nevyžaduje žádnou složitou konfiguraci nebo generování entit, s Nette Database můžete začít pracovat okamžitě.
+
+S Nette Database můžete pracovat dvěma způsoby:
+
+
+
+
+
+[Direct SQL]
+============
+- Bezpečné parametrizované dotazy
+- Přesná kontrola nad podobou SQL dotazů
+- Když píšete komplexní dotazy s pokročilými funkcemi
+- Optimalizujete výkon pomocí specifických SQL funkcí
+
+
+
+
+
+
+[Explorer]
+==========
+- Vyvíjíte rychle bez psaní SQL
+- Intuitivní práce s relacemi mezi tabulkami
+- Oceníte automatickou optimalizaci dotazů
+- Vhodné pro rychlou a pohodlnout práci s databází
+
+
+
+
+
+
+Instalace
+=========
+
+Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|best-practices:composer]:
+
+```shell
+composer require nette/database
+```
+
+
+Připojení a konfigurace
+=======================
+
+Pro připojení k databázi stačí vytvořit instanci třídy [api:Nette\Database\Connection]:
+
+```php
+$database = new Nette\Database\Connection($dsn, $user, $password);
+```
+
+Parametr `$dsn` (data source name) je stejný, [jaký používá PDO |https://www.php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters], např. `host=127.0.0.1;dbname=test`. V případě selhání vyhodí výjimku `Nette\Database\ConnectionException`.
+
+Nicméně šikovnější způsob nabízí [aplikační konfigurace |configuration], kam stačí přidat sekci `database` a vytvoří se potřebné objekty a také databázový panel v [Tracy |tracy:] baru.
+
+```neon
+database:
+ dsn: 'mysql:host=127.0.0.1;dbname=test'
+ user: root
+ password: password
+```
+
+Poté objekt spojení [získáme jako službu z DI kontejneru |dependency-injection:passing-dependencies], např.:
+
+```php
+class Model
+{
+ // pro práci s vrstvou Database Explorer si předáme Nette\Database\Explorer
+ public function __construct(
+ private Nette\Database\Connection $database,
+ ) {
+ }
+}
+```
+
+Více informací o [konfiguraci databáze|configuration].
+
+
+Dva přístupy k databázi
+=======================
+
+S Nette Database můžete pracovat dvěma způsoby - buď psaním SQL dotazů (Direct přístup), nebo nechat SQL generovat automaticky (Explorer přístup).
+
+[Direct přístup|direct-sql] - SQL dotazy
+
+```php
+// SELECT s parametrizovaným dotazem
+$result = $database->query('SELECT * FROM users WHERE role = ?', 'admin');
+
+// INSERT pomocí pole hodnot
+$database->query('INSERT INTO users', [
+ 'name' => 'John',
+ 'email' => 'john@example.com',
+ 'created_at' => new DateTime,
+]);
+
+// UPDATE s podmínkou
+$database->query('UPDATE users SET', [
+ 'active' => true,
+ 'last_login' => new DateTime,
+], 'WHERE id = ?', $userId);
+```
+
+Direct přístup nabízí pomocné nástroje pro bezpečné sestavování dotazů, ale zachovává vám plnou kontrolu nad jejich podobou.
+
+[Explorer přístup|explorer] - automatické generování SQL
+
+```php
+// Práce s tabulkou
+$users = $database->table('users');
+
+// SELECT s podmínkou
+$users->where('role', 'admin');
+
+// Práce se vztahy
+foreach ($users as $user) {
+ echo $user->name;
+ echo $user->role->description; // automatický JOIN
+
+ // Výpis článků uživatele
+ foreach ($user->related('articles') as $article) {
+ echo $article->title;
+ }
+}
+```
+
+Explorer přístup generuje a optimalizuje SQL dotazy automaticky. Stará se o efektivní načítání dat a práci se vztahy mezi tabulkami.
+
+Oba přístupy lze v aplikaci libovolně kombinovat podle potřeby.
+
+
+Správa připojení
+================
+
+Při vytvoření objektu `Connection` dojde automaticky k připojení. Pokud chcete připojení odložit, použijte lazy režim - ten zapnete v [konfiguraci|configuration] nastavením `lazy`, nebo takto:
+
+```php
+$database = new Nette\Database\Connection($dsn, $user, $password, ['lazy' => true]);
+```
+
+Pro správu připojení k databázi slouží metody `connect()`, `disconnect()` a `reconnect()`. Metoda `connect()` naváže spojení s databází, pokud ještě není navázáno. Může vyhodit výjimku `Nette\Database\ConnectionException`. Metoda `disconnect()` odpojí se od databáze. Metoda `reconnect()` odpojí se a znovu připojí k databázi. Může vyhodit výjimku `Nette\Database\ConnectionException`.
+
+Kromě toho můžete sledovat události spojené s připojením pomocí události `onConnect`, což je pole callbacků, které se zavolají po navázání spojení s databází.
+
+```php
+// proběhne po připojení k databázi
+$database->onConnect[] = function($database) {
+ echo "Připojeno k databázi";
+};
+```
+
+
+Tracy Debug Bar
+===============
+
+Pokud používáte [Tracy |tracy:], aktivuje se automaticky panel Database v Debug baru, který zobrazuje všechny provedené dotazy, jejich parametry, dobu vykonání a místo v kódu, kde byly zavolány.
+
+[* db-panel.webp *]
+
+
+Podporované databáze
+====================
+
+Nette Database podporuje následující databáze:
+
+|* Databázový server |* DSN jméno |* Podpora v Explorer
+| MySQL (>= 5.1) | mysql | ANO
+| PostgreSQL (>= 9.0) | pgsql | ANO
+| Sqlite 3 (>= 3.8) | sqlite | ANO
+| Oracle | oci | -
+| MS SQL (PDO_SQLSRV) | sqlsrv | ANO
+| MS SQL (PDO_DBLIB) | mssql | -
+| ODBC | odbc | -
diff --git a/database/cs/mapper.texy b/database/cs/mapper.texy
new file mode 100644
index 0000000000..93c7047ddf
--- /dev/null
+++ b/database/cs/mapper.texy
@@ -0,0 +1,55 @@
+Konverze typů
+*************
+
+.[perex]
+Nette Database automaticky konvertuje hodnoty vrácené z databáze na odpovídající PHP typy.
+
+
+Datum a čas
+-----------
+
+Časové údaje jsou převáděny na objekty `Nette\Utils\DateTime`. Pokud chcete, aby byly časové údaje převáděny na immutable objekty `Nette\Database\DateTime`, nastavte v [konfiguraci|configuration] volbu `newDateTime` na true.
+
+```php
+$row = $database->fetch('SELECT created_at FROM articles');
+echo $row->created_at instanceof DateTime; // true
+echo $row->created_at->format('j. n. Y');
+```
+
+V případě MySQL převádí datový typ `TIME` na objekty `DateInterval`.
+
+
+Booleovské hodnoty
+------------------
+
+Booleovské hodnoty jsou automaticky převedeny na `true` nebo `false`. U MySQL se převádí `TINYINT(1)` pokud nastavíme v [konfiguraci|configuration] `convertBoolean`.
+
+```php
+$row = $database->fetch('SELECT is_published FROM articles');
+echo gettype($row->is_published); // 'boolean'
+```
+
+
+Číselné hodnoty
+---------------
+
+Číselné hodnoty jsou převedeny na `int` nebo `float` podle typu sloupce v databázi:
+
+```php
+$row = $database->fetch('SELECT id, price FROM products');
+echo gettype($row->id); // integer
+echo gettype($row->price); // float
+```
+
+
+Vlastní normalizace
+-------------------
+
+Pomocí metody `setRowNormalizer(?callable $normalizer)` můžete nastavit vlastní funkci pro transformaci řádků z databáze. To se hodí například pro automatický převod datových typů.
+
+```php
+$database->setRowNormalizer(function(array $row, ResultSet $resultSet): array {
+ // konverze typů
+ return $row;
+});
+```
diff --git a/database/cs/reflection.texy b/database/cs/reflection.texy
index eb2a063055..bade83ee3c 100644
--- a/database/cs/reflection.texy
+++ b/database/cs/reflection.texy
@@ -1,5 +1,5 @@
-Reflexe
-*******
+Reflexe struktury
+*****************
.{data-version:3.2.4}
Nette Database poskytuje nástroje pro introspekci databázové struktury pomocí třídy [api:Nette\Database\Reflection\Reflection]. Ta umožňuje získávat informace o tabulkách, sloupcích, indexech a cizích klíčích. Reflexi můžete využít ke generování schémat, vytváření flexibilních aplikací pracujících s databází nebo obecných databázových nástrojů.
@@ -7,7 +7,7 @@ Nette Database poskytuje nástroje pro introspekci databázové struktury pomoc
Objekt reflexe získáme z instance připojení k databázi:
```php
-$reflection = $connection->getReflection();
+$reflection = $database->getReflection();
```
diff --git a/database/cs/security.texy b/database/cs/security.texy
index 6319849831..01a6ef8242 100644
--- a/database/cs/security.texy
+++ b/database/cs/security.texy
@@ -157,4 +157,4 @@ $database->query('SELECT ?name FROM ?name', $column, $table);
$database->query('SELECT ?name FROM users', $_GET['column']);
```
-Důležité: symbol `?name` používejte pouze pro důvěryhodné hodnoty definované v kódu aplikace. Pro hodnoty od uživatele použijte opět whitelist.
+Důležité: symbol `?name` používejte pouze pro důvěryhodné hodnoty definované v kódu aplikace. Pro hodnoty od uživatele použijte opět whitelist. Jinak se vystavujete bezpečnostním rizikům, jako například dříve uvedený SQL enumeration nebo Mass Assignment Vulnerability.
diff --git a/database/cs/transactions.texy b/database/cs/transactions.texy
new file mode 100644
index 0000000000..e91070c2fb
--- /dev/null
+++ b/database/cs/transactions.texy
@@ -0,0 +1,43 @@
+Transakce
+*********
+
+.[perex]
+Transakce zaručují, že se buď provedou všechny operace v rámci transakce, nebo se neprovede žádná. Jsou užitečné pro zajištění konzistence dat při složitějších operacích.
+
+Nejjednodušší způsob použití transakcí vypadá takto:
+
+```php
+$database->beginTransaction();
+try {
+ $database->query('DELETE FROM articles WHERE id = ?', $id);
+ $database->query('INSERT INTO audit_log', [
+ 'article_id' => $id,
+ 'action' => 'delete'
+ ]);
+ $database->commit();
+} catch (\Exception $e) {
+ $database->rollBack();
+ throw $e;
+}
+```
+
+Mnohem elegantněji můžete to samé zapsat pomocí metody `transaction()`. Jako parametr přijímá callback, který vykoná v transakci. Pokud callback proběhne bez výjimky, transakce se automaticky potvrdí. Pokud dojde k výjimce, transakce se zruší (rollback) a výjimka se šíří dál.
+
+```php
+$database->transaction(function ($database) use ($id) {
+ $database->query('DELETE FROM articles WHERE id = ?', $id);
+ $database->query('INSERT INTO audit_log', [
+ 'article_id' => $id,
+ 'action' => 'delete'
+ ]);
+});
+```
+
+Metoda `transaction()` může také vracet hodnoty:
+
+```php
+$count = $database->transaction(function ($database) {
+ $result = $database->query('UPDATE users SET active = ?', true);
+ return $result->getRowCount(); // vrátí počet aktualizovaných řádků
+});
+```