Databaser
Dotazy spúšťajte z kontroléra modulu. Vstupné body: DB::module('RAW') (polia) alebo DB::module('ORM') (Entity/Collection). Riadky načítajte cez all() a pri potrebe jedného riadka použite $rows[0] ?? null. Do execute($ok, $err) vždy odovzdajte oba callbacky — bez chybového callbacku zlyhanie vyvolá výnimku. Zmeny schémy patria do Installation.php modulu. Tabuľky modulu používajú konvenciu pomenovania {modulename}_*.
1. Úvod
1.1. Čo je Databaser?
Databaser je robustná a flexibilná knižnica na prácu s databázou, zabudovaná do DotApp PHP Framework 2.0. Poskytuje jednoduchý, bezpečný a efektívny spôsob, ako spúšťať základné operácie aj pokročilé dotazy. Databaser odstraňuje potrebu písať surové SQL (pritom ho však stále umožňuje) a ponúka moderný prístup cez intuitívny QueryBuilder a voliteľnú vrstvu ORM (Object-Relational Mapping). Cieľom je uľahčiť vývojárom prácu s databázou bez straty flexibility ani výkonu.
Databaser je súčasťou jadra DotApp PHP Framework 2.0, takže ho neinštalujete ani nekonfigurujete samostatne. Po definovaní databázových pripojení vo frameworku je pripravený na použitie.
1.2. Kľúčové vlastnosti
Databaser ponúka širokú škálu funkcií na prácu s databázami:
- Jednoduché vytváranie a vykonávanie SQL: Prepared statements zabezpečujú bezpečnú a prehľadnú prácu s dátami.
- Viaceré databázové pripojenia: Definujte databázy a prepínajte medzi nimi s uloženými prihlasovacími údajmi.
- Podpora vlastných driverov: Okrem predvolených driverov (MySQLi a PDO) môžete implementovať vlastné databázové drivery.
- Voliteľný ORM: Dostupný pre MySQLi aj PDO, s triedami
Entity(jeden riadok) aCollection(súbor riadkov), ktoré spracúvajú dáta ako objekty. - Lazy loading a vzťahy: Súvisiace riadky načítate metódami vzťahov na Entity (
$user->hasMany('shop_posts', 'user_id')). - Pokročilé vzťahy:
QueryBuildervo vzťahu môžete upraviť (napríklad pridaťlimit,orderBy,where) cez voliteľný parameter callbacku. - Validácia: ORM validuje atribúty počas
save(). Collection prejdite v cykle a každú Entity uložte samostatne. - Integrovaný QueryBuilder: Intuitívny nástroj na tvorbu dotazov od jednoduchých SELECTov až po zložité JOINy a vnorené dotazy.
- Callbacky SUCCESS a ERROR: Každá operácia vracia výsledky a ladiace dáta cez callbacky, čo zjednodušuje spracovanie úspechu aj chyby.
- Podpora transakcií: Prehľadná správa transakcií s automatickým commitom alebo rollbackom.
1.3. RAW vs. ORM: Kedy použiť ktorý prístup?
Databaser ponúka dva hlavné spôsoby práce s dátami: RAW a ORM. Výber závisí od potrieb vášho projektu:
- REŽIM RAW:
- Výsledky dotazu sa vracajú priamo (napríklad polia alebo databázové zdroje).
- Vhodné pre jednoduché aplikácie, rýchle prototypy alebo situácie, v ktorých potrebujete plnú kontrolu nad SQL.
- Príklad: Jednoduchý SELECT na zoznam používateľov bez mapovania na objekty.
- Výhody: Rýchle vykonanie, minimálna réžia, plná flexibilita pri písaní dotazov.
- REŽIM ORM:
- Dáta sa mapujú na objekty (
Entitypre jeden riadok,Collectionpre viac riadkov), takže s nimi pracujete ako s objektmi. - Vhodné pre komplexné aplikácie, ktoré potrebujú vzťahy medzi tabuľkami, validáciu dát alebo objektové spracovanie riadkov.
- Príklad: Správa používateľov a ich príspevkov (vzťah
HasMany) a automatické ukladanie zmien. - Výhody: Objektovo orientovaný prístup, podpora vzťahov, prehľadná práca s dátami.
- Dáta sa mapujú na objekty (
Kedy použiť ktorý prístup?
- Zvoľte RAW, keď potrebujete vysoký výkon a jednoduché dotazy.
- Zvoľte ORM, keď pracujete so zložitými dátovými štruktúrami a chcete čistejšie objektovo orientované riešenie.
1.4. Podpora databázových driverov (MySQLi, PDO)
Databaser podporuje dva hlavné databázové drivery, ktoré pokrývajú väčšinu bežných potrieb:
MySQLi
- QueryBuilder a ORM.
- Vhodný pre projekty, ktoré už používajú MySQLi, alebo pre jednoduchšie aplikácie s databázami MySQL.
- Podporuje všetky funkcie
QueryBuildera aj ORM.
PDO
- Podpora viacerých databáz (MySQL, PostgreSQL, SQLite a ďalšie).
- Flexibilnejší vďaka dynamickému DSN (Data Source Name), ktorý umožňuje pripojenie k rôznym typom databáz.
- Rovnako podporuje
QueryBuilderaj ORM.
Oba drivery sú navrhnuté tak, aby boli vzájomne zameniteľné — kód napísaný pre jeden driver funguje aj s druhým bez väčších úprav, pokiaľ rešpektujete špecifiká cieľového databázového systému.
1.5. Integrovaný QueryBuilder
QueryBuilder je srdcom Databaseru. Umožňuje skladať SQL reťaziteľnými metódami, čo uľahčuje písanie bezpečných a čitateľných dotazov. Podporuje:
- Základné operácie:
select,insert,update,delete. - Podmienky:
where,orWhere, vnorené podmienky cez Closure. - Spojenia tabuliek:
join,leftJoin. - Agregácie:
groupBy,having. - Zoradenie a obmedzenia:
orderBy,limit,offset. - Surové dotazy:
raws placeholdermi s otáznikom (?) aj pomenovanými premennými (:name). - Zdroj tabuľky:
from, ak tabuľku nepredáte doselect()anidelete().
QueryBuilder automaticky spravuje prepared statements a bindings, čím chráni pred SQL injection. Každá hodnota použitá v dotaze (napríklad v podmienkach where alebo v dátach predaných do insert) sa escapuje a nahradí placeholdermi (? alebo pomenovanými premennými :name). Tým sa znižuje riziko bezpečnostných problémov a kód ostáva čitateľnejší.
1.6. Callbacky SUCCESS a ERROR
Databaser používa callbacky na spracovanie výsledkov a chýb. Každá operácia (napríklad execute(), save()) môže prijať dva voliteľné callbacky:
Callback SUCCESS
Spustí sa pri úspešnej operácii. Dostane tri parametre:
$result: Výsledok operácie (napríklad pole dát v režime RAW alebo objekt v režime ORM).$db: InštanciaDatabaseru, ktorú môžete použiť na ďalšie dotazy.$debug: Ladiace dáta (napríklad vygenerovaný SQL dotaz a bindings).
Callback ERROR
Spustí sa pri chybe. Rovnako dostane tri parametre:
$error: Pole s podrobnosťami o chybe (error — text chyby, errno — kód chyby).$db: InštanciaDatabaseru na prípadné nadväzujúce operácie.$debug: Ladiace dáta na analýzu problému.
Tento prístup zjednodušuje spracovanie a umožňuje reťaziť operácie priamo v callbackoch. Ak má jeden dotaz okamžite spustiť ďalší, volajte $db->q() z callbacku SUCCESS. Logika úspechu a chyby ostáva oddelená a prehľadná. Do execute($ok, $err) vždy odovzdajte oba callbacky. Ak callback ERROR vynecháte, execute() pri zlyhaní vyvolá výnimku, ktorú môžete zachytiť blokom try/catch. Ak je callback ERROR nastavený, try/catch sa pre toto zlyhanie nespustí — spracovanie chyby je plne delegované na callback.
2. Začíname
2.1. Inštalácia a konfigurácia Databaseru
Databaser je neoddeliteľnou súčasťou DotApp PHP Framework 2.0, takže ho neinštalujete samostatne. Po nastavení frameworku v projekte je Databaser dostupný cez fasádu DB::. V kóde modulu používajte DB::module('RAW') alebo DB::module('ORM'). Táto kapitola predpokladá, že framework je nakonfigurovaný a pripravený na použitie.
2.2. Pridanie databázového pripojenia
Databaser vie pridať a spravovať viacero databázových pripojení. Registrujte ich v app/config.php pomocou Config::addDatabase(). Príklad:
Config::addDatabase(
'main', // Connection name
'localhost', // Host
'root', // Username
'password123', // Password
'my_database', // Database name
'utf8mb4', // Charset
'MYSQL', // Database type
'pdo' // Driver
);
2.3. Výber drivera (MySQLi alebo PDO)
Databaser podporuje MySQLi aj PDO. Driver a hlavná databáza (maindb) sa zvyčajne volia v konfigurácii, nie pri každom dotaze. V ukážkach používajte DB::module('RAW') alebo DB::module('ORM'). Manuálny výber drivera nechajte na pokročilé vlastné drivery.
2.4. Prvé pripojenie k databáze
Po definovaní pripojenia v konfigurácii framework automaticky použije driver a hlavné pripojenie. Pripojenie môžete overiť takto:
if (DB::isConnected()) {
echo 'Database is connected.';
}
Príklad prvého jednoduchého dotazu:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items');
})
->execute(
function ($result, $db, $debug) {
echo "Generated query: " . $debug['query'] . "\n";
var_dump($result);
},
function ($error, $db, $debug) {
echo "Error: {$error['error']} (code: {$error['errno']})\n";
}
);
Vysvetlenie
DB::module('RAW'): Kanonický vstup. Driver a predvolená databáza pochádzajú zapp/config.php.execute($ok, $err): Vždy odovzdajte oba callbacky. Bez$errdatabázová chyba vyvolá výnimku.q()(aliasqb()): SpustíQueryBuildera definuje dotaz (v tomto prípadeSELECT * FROM shop_items).execute(): Vykoná dotaz s callbackmi pre úspech a chybu.$result: Pole výsledkov (v režime RAW).$debug: Obsahuje vygenerovaný SQL dotaz a ďalšie informácie.
Výstup (príklad):
Generated query: SELECT * FROM shop_items
array(2) {
[0] => array(3) {
["id"] => string(1) "1"
["name"] => string(4) "Jane"
["age"] => string(2) "25"
}
[1] => array(3) {
["id"] => string(1) "2"
["name"] => string(5) "Maria"
["age"] => string(2) "30"
}
}
3. QueryBuilder: Podrobný prehľad
QueryBuilder je kľúčový nástroj v Databaseri. Umožňuje skladať SQL reťaziteľnými metódami. Hlavné výhody sú jednoduchosť, čitateľnosť a bezpečnosť — automaticky spravuje prepared statements a bindings, čím chráni pred SQL injection. Táto kapitola opisuje, ako funguje, dostupné metódy a príklady od jednoduchých po zložité dotazy.
3.1. Základné princípy QueryBuildera
QueryBuilder je objekt triedy Dotsystems\App\Parts\QueryBuilder. Používate ho vo vnútri q() alebo qb() na fasáde DB:: (typicky DB::module('RAW')->q(...)). Dotaz skladáte postupným volaním metód; každá metóda pridá časť SQL príkazu (napríklad select, where, join). Dotaz dokončite cez execute(). Riadky načítajte cez all() a pri potrebe jedného riadka použite $rows[0] ?? null.
Základné vlastnosti:
- Reťaziteľnosť: Metódy vracajú inštanciu
QueryBuildera, takže ich môžete reťaziť. - Prepared statements: Všetky hodnoty sa automaticky escapujú a nahradia placeholdermi (?).
- Flexibilita: Surové SQL je dostupné cez
raw()pre špeciálne prípady. - Ladenie: Po vykonaní obsahuje
$debugvygenerované SQL a bindings.
Základný príklad použitia:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('age', '>', 18);
})->execute(
function ($result, $db, $debug) {
echo $debug['query']; // "SELECT * FROM shop_items WHERE age > ?"
var_dump($debug['bindings']); // [18]
var_dump($result);
},
function ($error, $db, $debug) {
echo "Error: {$error['error']} (code: {$error['errno']})\n";
}
);
3.2. Zoznam metód QueryBuildera
Tu je podrobný prehľad hlavných metód QueryBuildera s vysvetlením a príkladmi.
3.2.1. select
Metóda select() určuje, ktoré stĺpce sa majú načítať a z ktorej tabuľky.
Syntax: select($columns = '*', $table = null)
Parametre:
$columns: Reťazec alebo pole stĺpcov (napríklad 'id, name' alebo ['id', 'name']).$table: Názov tabuľky (voliteľný, ak použijetefrom()).
SQL ekvivalent: SELECT columns FROM table
Príklad:
$qb->select('id, name', 'shop_items');
// SQL: SELECT id, name FROM shop_items
3.2.2. insert
Metóda insert() vloží nový riadok do tabuľky.
Syntax: insert($table, array $data)
Parametre:
$table: Názov tabuľky.$data: Asociatívne pole dát (stĺpec => hodnota).
SQL ekvivalent: INSERT INTO table (columns) VALUES (values)
Príklad:
$qb->insert('shop_items', ['name' => 'Jane', 'age' => 25]);
// SQL: INSERT INTO shop_items (name, age) VALUES (?, ?)
// Bindings: ['Jane', 25]
3.2.3. update
Metódy update() a set() aktualizujú existujúce riadky.
Syntax: update($table) + set(array $data)
Parametre:
$table: Názov tabuľky.$data: Asociatívne pole aktualizovaných hodnôt.
SQL ekvivalent: UPDATE table SET column = value
Príklad:
$qb->update('shop_items')->set(['age' => 26])->where('id', '=', 1);
// SQL: UPDATE shop_items SET age = ? WHERE id = ?
// Bindings: [26, 1]
3.2.4. delete
Metóda delete() odstráni riadky z tabuľky.
Syntax: delete($table = null)
Parametre:
$table: Názov tabuľky (voliteľný, ak je definovaná inde).
SQL ekvivalent: DELETE FROM table
Príklad:
$qb->delete('shop_items')->where('id', '=', 1);
// SQL: DELETE FROM shop_items WHERE id = ?
// Bindings: [1]
3.2.5. where a orWhere
Metódy where() a orWhere() pridávajú podmienky.
Syntax: where($column, $operator = null, $value = null, $boolean = 'AND')
Parametre:
$column: Stĺpec alebo Closure pre vnorené podmienky.$operator: Operátor (napríklad =, >, <).$value: Hodnota alebo Closure pre vnorený dotaz.$boolean: Logické spojenie (predvolene AND).
SQL ekvivalent: WHERE column operator value
Príklad:
$qb->select('*', 'shop_items')
->where('age', '>', 18)
->orWhere('name', '=', 'Jane');
// SQL: SELECT * FROM shop_items WHERE age > ? OR name = ?
// Bindings: [18, 'Jane']
3.2.6. join (INNER, LEFT)
Metódy join() a leftJoin() spájajú tabuľky.
Syntax: join($table, $first, $operator, $second, $type = 'INNER')
Parametre:
$table: Tabuľka alebo vnorený dotaz (QueryBuilder).$first: Prvý stĺpec podmienky spojenia.$operator: Operátor spojenia.$second: Druhý stĺpec podmienky spojenia.$type: Typ spojenia (INNER, LEFT).
SQL ekvivalent: INNER JOIN table ON condition
Príklad:
$qb->select('shop_items.name, shop_posts.title', 'shop_items')
->join('shop_posts', 'shop_items.id', '=', 'shop_posts.user_id');
// SQL: SELECT shop_items.name, shop_posts.title FROM shop_items INNER JOIN shop_posts ON shop_items.id = shop_posts.user_id
3.2.7. groupBy
Metóda groupBy() zoskupuje výsledky.
Syntax: groupBy($columns)
Parametre:
$columns: Stĺpec alebo pole stĺpcov.
SQL ekvivalent: GROUP BY columns
Príklad:
$qb->select('age', 'shop_items')->groupBy('age');
// SQL: SELECT age FROM shop_items GROUP BY age
3.2.8. having
Metóda having() filtruje zoskupené výsledky.
Syntax: having($column, $operator, $value)
Parametre:
$column: Stĺpec.$operator: Operátor.$value: Hodnota.
SQL ekvivalent: HAVING column operator value
Príklad:
$qb->select('age', 'shop_items')->groupBy('age')->having('age', '>', 20);
// SQL: SELECT age FROM shop_items GROUP BY age HAVING age > ?
// Bindings: [20]
3.2.9. orderBy
Metóda orderBy() zoradí výsledky.
Syntax: orderBy($column, $direction = 'ASC')
Parametre:
$column: Stĺpec.$direction: Smer (ASC alebo DESC).
SQL ekvivalent: ORDER BY column direction
Príklad:
$qb->select('*', 'shop_items')->orderBy('age', 'DESC');
// SQL: SELECT * FROM shop_items ORDER BY age DESC
3.2.10. limit a offset
Metódy limit() a offset() obmedzia počet vrátených riadkov.
Syntax: limit($limit) + offset($offset)
Parametre:
$limit: Počet riadkov.$offset: Počiatočný posun.
SQL ekvivalent: LIMIT count OFFSET offset
Príklad:
$qb->select('*', 'shop_items')->limit(5)->offset(10);
// SQL: SELECT * FROM shop_items LIMIT ? OFFSET ?
// Bindings: [5, 10]
3.2.11. raw
Metóda raw() umožňuje spustiť surový SQL dotaz.
Syntax: raw($sql, array $bindings = [])
Parametre:
$sql: Surový SQL reťazec.$bindings: Pole hodnôt pre placeholdery.
SQL ekvivalent: Dotaz, ktorý predáte.
Príklad:
$qb->raw('SELECT * FROM shop_items WHERE age > ?', [18]);
// SQL: SELECT * FROM shop_items WHERE age > ?
// Bindings: [18]
3.2.12. from
Metóda from() nastaví tabuľku, ak ste ju nepredali do select(), delete() ani podobnej metódy.
Syntax: from($table)
Parametre:
$table: Názov tabuľky.
SQL ekvivalent: FROM table
Príklad:
$qb->select('id, name')->from('shop_items');
// SQL: SELECT id, name FROM shop_items
3.3. Príklady od jednoduchých po zložité dotazy
Jednoduchý select
$qb->select('*', 'shop_items');
// SQL: SELECT * FROM shop_items
select s podmienkou where
$qb->select('name', 'shop_items')->where('age', '>', 18);
// SQL: SELECT name FROM shop_items WHERE age > ?
// Bindings: [18]
Vnorené podmienky (Closure)
$qb->select('*', 'shop_items')->where(function ($qb) {
$qb->where('age', '>', 18)->orWhere('name', '=', 'Jane');
});
// SQL: SELECT * FROM shop_items WHERE (age > ? OR name = ?)
// Bindings: [18, 'Jane']
join s viacerými tabuľkami
$qb->select('shop_items.name, shop_posts.title', 'shop_items')
->join('shop_posts', 'shop_items.id', '=', 'shop_posts.user_id')
->leftJoin('shop_comments', 'shop_posts.id', '=', 'shop_comments.post_id');
// SQL: SELECT shop_items.name, shop_posts.title FROM shop_items
// INNER JOIN shop_posts ON shop_items.id = shop_posts.user_id
// LEFT JOIN shop_comments ON shop_posts.id = shop_comments.post_id
Vnorený dotaz ako hodnota
$qb->select('name', 'shop_items')->where('id', '=', function ($qb) {
$qb->select('user_id', 'shop_posts')->where('title', '=', 'News');
});
// SQL: SELECT name FROM shop_items WHERE id = (SELECT user_id FROM shop_posts WHERE title = ?)
// Bindings: ['News']
Surový dotaz s pomenovanými premennými
$qb->raw('SELECT * FROM shop_items WHERE age > :age AND name = :name', [
'age' => 18,
'name' => 'Jane'
]);
// SQL: SELECT * FROM shop_items WHERE age > ? AND name = ?
// Bindings: [18, 'Jane']
4. Práca s Databaserom v DotApp
Táto kapitola opisuje praktické použitie Databaseru vo frameworku DotApp: nastavenie typu návratu, spúšťanie dotazov, prácu s ORM, správu transakcií a kontrolu výsledkov. Databaser je navrhnutý na flexibilitu a jednoduchosť, či už preferujete prístup RAW, alebo objektovo orientovaný ORM.
4.1. Nastavenie typu návratu (RAW vs. ORM)
Typ návratu nastavte cez DB::module('RAW') alebo DB::module('ORM') — nie metódou return().
- RAW: Vráti surové dáta (napríklad pole riadkov alebo databázový zdroj výsledku). Toto je predvolené správanie.
- ORM: Vráti dáta ako objekty (
Entitypre jeden riadok,Collectionpre viacero riadkov).
Syntax: DB::module($type)
$type: Reťazec 'RAW' alebo 'ORM' (veľkosť písmen nezáleží).
Príklad — RAW:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->execute(
function ($result, $db, $debug) {
var_dump($result); // Array of rows
},
function ($error) {
// execute() without this callback throws on error
}
);
Príklad — ORM:
DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->execute(
function ($result, $db, $debug) {
var_dump($result); // Collection instance
},
function ($error) {
// execute() without this callback throws on error
}
);
Typ návratu môžete zmeniť pred každým dotazom, takže rôzne časti aplikácie môžu podľa potreby používať RAW alebo ORM.
4.2. Metódy na vykonanie dotazov
Databaser poskytuje niekoľko metód na spúšťanie dotazov zložených cez QueryBuilder. Každá metóda má konkrétne použitie.
4.2.1. execute()
Metóda execute() je najuniverzálnejšia — vykoná dotaz a výsledky doručí cez callbacky.
Syntax: execute($success = null, $error = null)
Parametre:
$success: Callback úspechu (function ($result, $db, $debug)).$error: Callback chyby (function ($error, $db, $debug)). Tento callback vždy odovzdajte; bez nehoexecute()pri chybe vyvolá výnimku.
Výstup: Závisí od typu návratu (RAW: pole/zdroj, ORM: Collection/Entity).
Príklad:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('age', '>', 18);
})->execute(
function ($result, $db, $debug) {
echo "Query: " . $debug['query'] . "\n";
var_dump($result);
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
4.2.2. first() — nebezpečné pri prázdnom výsledku
first() nevolajte bez ochrany. Prázdny RAW spustí upozornenie na nedefinovaný index; prázdny ORM je fatálny. Preferujte all() a použite index 0:
$rows = DB::module('RAW')->q(function ($qb) {
$qb->select('*')->from('shop_items')->where('id', '=', 1)->limit(1);
})->all();
$row = $rows[0] ?? null;
4.2.3. all()
Metóda all() vráti všetky riadky výsledku. Je to aj bezpečný spôsob, ako načítať jeden riadok: použite $rows[0] ?? null.
Syntax: all()
Výstup: RAW — pole riadkov; ORM — Collection.
Príklad:
$users = DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
foreach ($users as $user) {
echo $user['name'] . "\n";
}
4.2.4. raw()
Metóda raw() je terminál, ktorý vráti výsledok drivera (napríklad mysqli_result alebo PDO statement). Riadky z tohto výsledku načítajte cez DB::fetchArray().
Syntax: raw()
Výstup: Závisí od drivera (napríklad mysqli_result alebo PDO statement).
Príklad:
$result = DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->raw();
while ($row = DB::fetchArray($result)) {
echo $row['name'] . "\n";
}
4.3. Práca s ORM
Režim ORM umožňuje pracovať s riadkami ako s objektmi, čo zjednodušuje aktualizácie a vzťahy medzi tabuľkami.
4.3.1. Entity a Collection
Entity: Reprezentuje jeden riadok tabuľky. Sprístupňuje atribúty zodpovedajúce stĺpcom a metódy na aktualizácie a vzťahy.
Collection: Skupina objektov Entity s iteráciou a pomocnými metódami ako filter(), map() a pluck().
Príklad:
$users = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
foreach ($users as $user) {
echo $user->name . "\n"; // Collection yields Entity objects
}
4.3.2. Ukladanie dát (save())
Entity::save($ok, $err) zapíše zmeny do databázy. Vracia void — vždy používajte callbacky; nepíšte if ($entity->save()). Pred uložením načítajte riadok cez all() a $rows[0] ?? null.
Príklad:
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('id', '=', 1);
})->all();
$user = $rows[0] ?? null;
if ($user) {
$user->age = 26;
$user->save(
function ($result, $db, $debug) {
echo "User saved!\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
4.3.3. Vzťahy (hasOne, hasMany)
ORM podporuje vzťahy medzi tabuľkami. Súvisiace riadky načítajte volaním metódy na entite — nie magickou vlastnosťou ako $user->posts:
- hasOne: Jedna k jednej.
- hasMany: Jedna k viacerým.
Príklad:
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('id', '=', 1);
})->all();
$user = $rows[0] ?? null;
$posts = $user ? $user->hasMany('shop_posts', 'user_id') : [];
foreach ($posts as $post) {
echo $post->title . "\n";
}
4.3.4. Lazy loading a metódy Collection
Súvisiace riadky sa načítajú pri volaní metódy vzťahu (lazy loading). Collection poskytuje pomocné metódy ako filter(), map() a pluck(). pluck('name') vráti Collection hodnôt daného poľa; ak potrebujete obyčajné PHP pole, zavolajte na ňom all().
Príklad:
$users = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
$names = $users->pluck('name');
var_dump($names);
4.4. Transakcie
Databaser podporuje transakcie, aby skupina zápisov buď celá uspela, alebo sa celá vrátila späť.
4.4.1. transaction(), commit(), rollback()
Manuálne riadenie cez DB::module('RAW')->transaction() a následne commit() alebo rollback():
$db = DB::module('RAW');
$db->transaction();
$db->q(function ($qb) {
$qb->insert('shop_items', ['name' => 'Jane']);
})->execute(
function ($result, $db, $debug) {
$db->commit();
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
$db->rollback();
}
);
Automatická transakcia cez transact(). Callback operácií dostane inštanciu Databaseru plus callbacky úspechu a chyby — oba prepošlite do každého execute(), aby transakcia mohla vykonať commit alebo rollback:
DB::module('RAW')->transact(function ($db, $ok, $err) {
$db->q(function ($qb) {
$qb->insert('shop_items', ['name' => 'Jane']);
})->execute($ok, $err);
}, function ($result, $db, $debug) {
echo "Transaction succeeded!\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
4.5. Ladenie a práca s výstupom
Každá operácia sprístupňuje tri hlavné hodnoty:
- result: Výsledok dotazu (RAW: pole, ORM: objekty).
- db: Inštancia
Databaseru na nadväzujúce dotazy. - debug: Pole informácií (napríklad
queryabindings). Rovnaké dáta obsahujú ajinsert_idaaffected_rows.
Príklad ladenia:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('age', '>', 18);
})->execute(
function ($result, $db, $debug) {
echo "SQL: " . $debug['query'] . "\n";
echo "Bindings: " . implode(', ', $debug['bindings']) . "\n";
var_dump($result);
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
5. Praktické príklady
Táto kapitola ukazuje praktické použitie Databaseru vo frameworku DotApp. Pokrývame bežné CRUD operácie (Create, Read, Update, Delete), pokročilé dotazy s join a vnorenými dotazmi, transakcie a spracovanie chýb. Po insert alebo update načítajte nové ID a počet ovplyvnených riadkov cez $db->inserted_id() a $db->affected_rows() (s podčiarkovníkmi). Tie isté hodnoty sú v callbackoch execute dostupné aj ako $execution_data['insert_id'] a $execution_data['affected_rows'].
5.1. Základné CRUD operácie v režime RAW
Create (vytvorenie):
DB::module('RAW')->q(function ($qb) {
$qb->insert('shop_items', ['name' => 'Jane', 'age' => 25]);
})->execute(
function ($result, $db, $debug) {
$id = $db->inserted_id(); // ID of the new row
echo "New user with ID: $id has been created.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Read (čítanie):
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('age', '>', 20);
})->execute(
function ($result, $db, $debug) {
foreach ($result as $user) {
echo "Name: {$user['name']}, Age: {$user['age']}\n";
}
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Update (aktualizácia):
DB::module('RAW')->q(function ($qb) {
$qb->update('shop_items')->set(['age' => 26])->where('name', '=', 'Jane');
})->execute(
function ($result, $db, $debug) {
$rows = $db->affected_rows(); // Number of affected rows
echo "$rows row(s) updated.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Delete (odstránenie):
DB::module('RAW')->q(function ($qb) {
$qb->delete('shop_items')->where('name', '=', 'Jane');
})->execute(
function ($result, $db, $debug) {
$rows = $db->affected_rows();
echo "$rows row(s) deleted.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
5.2. Základné CRUD operácie v režime ORM
Create (vytvorenie):
DB::module('ORM')->q(function ($qb) {
$qb->insert('shop_items', ['name' => 'Maria', 'age' => 30]);
})->execute(
function ($result, $db, $debug) {
$id = $db->inserted_id();
$items = $db->q(function ($qb) use ($id) {
$qb->select('*', 'shop_items')->where('id', '=', $id);
})->all();
$user = $items[0] ?? null;
if ($user) {
echo "Created user: {$user->name}\n";
}
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Read (čítanie):
$users = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
foreach ($users as $user) {
echo "Name: {$user->name}, Age: {$user->age}\n";
}
Update (aktualizácia):
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('name', '=', 'Maria');
})->all();
$user = $rows[0] ?? null;
if ($user) {
$user->age = 31;
$user->save(
function ($result, $db, $debug) use ($user) {
echo "User {$user->name} updated.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
Delete (odstránenie):
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('name', '=', 'Maria');
})->all();
$user = $rows[0] ?? null;
if ($user) {
DB::module('RAW')->q(function ($qb) use ($user) {
$qb->delete('shop_items')->where('id', '=', $user->id);
})->execute(
function ($result, $db, $debug) {
$rows = $db->affected_rows();
echo "$rows row(s) deleted.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
5.3. Pokročilé príklady s JOIN a vnoreným dotazom
JOIN medzi tabuľkami:
DB::module('RAW')->q(function ($qb) {
$qb->select('shop_items.name, shop_posts.title', 'shop_items')
->join('shop_posts', 'shop_items.id', '=', 'shop_posts.user_id')
->where('shop_items.age', '>', 25);
})->execute(
function ($result, $db, $debug) {
foreach ($result as $row) {
echo "User: {$row['name']}, Post: {$row['title']}\n";
}
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Vnorený dotaz v ORM:
DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')
->where('id', '=', function ($subQb) {
$subQb->select('user_id', 'shop_posts')
->where('title', '=', 'News');
});
})->execute(
function ($users, $db, $debug) {
foreach ($users as $user) {
echo "User with News post: {$user->name}\n";
}
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
5.4. Práca s transakciami
Automatická transakcia:
DB::module('RAW')->transact(function ($db, $ok, $err) {
$db->q(function ($qb) {
$qb->insert('shop_items', ['name' => 'Peter', 'age' => 28]);
})->execute(function ($result, $db, $debug) use ($ok, $err) {
$id = $db->inserted_id();
$db->q(function ($qb) use ($id) {
$qb->insert('shop_posts', ['user_id' => $id, 'title' => 'First post']);
})->execute($ok, $err);
}, $err);
}, function ($result, $db, $debug) {
echo "Transaction succeeded. Last insert ID: " . $db->inserted_id() . "\n";
}, function ($error, $db, $debug) {
echo "Transaction error: {$error['error']}\n";
});
Manuálna transakcia:
$db = DB::module('RAW');
$db->transaction();
$db->q(function ($qb) {
$qb->insert('shop_items', ['name' => 'Anna', 'age' => 22]);
})->execute(
function ($result, $db, $debug) {
$id = $db->inserted_id();
$db->q(function ($qb) use ($id) {
$qb->insert('shop_posts', ['user_id' => $id, 'title' => 'Test']);
})->execute(
function ($result, $db, $debug) {
$db->commit();
echo "Transaction completed.\n";
},
function ($error, $db, $debug) {
$db->rollback();
echo "Rollback: {$error['error']}\n";
}
);
},
function ($error, $db, $debug) {
$db->rollback();
echo "Error: {$error['error']}\n";
}
);
5.5. Ladenie a spracovanie chýb
Ladenie dotazu:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('age', '>', 18);
})->execute(
function ($result, $db, $debug) {
echo "SQL: " . $debug['query'] . "\n";
echo "Bindings: " . implode(', ', $debug['bindings']) . "\n";
echo "Affected rows: " . $db->affected_rows() . "\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']} (code: {$error['errno']})\n";
echo "SQL: " . $debug['query'] . "\n";
}
);
Spracovanie chyby:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'missing_table'); // Invalid query
})->execute(
function ($result, $db, $debug) {
echo "Success\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
// Follow-up query on a table that exists
$db->q(function ($qb) {
$qb->select('*', 'shop_items');
})->execute(
function ($result, $db, $debug) {
echo "Recovery query succeeded.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
);
6. Práca so SchemaBuilderom
SchemaBuilder je nástroj Databaseru na definovanie a správu štruktúry databázy. Umožňuje vytvárať, upravovať a odstraňovať tabuľky z PHP bez ručného písania surového DDL. Je integrovaný s QueryBuilderom cez createTable(), alterTable() a dropTable(). Táto kapitola opisuje jeho metódy, argumenty a praktické príklady.
6.1. Základy SchemaBuildera
SchemaBuilder je trieda Dotsystems\App\Parts\SchemaBuilder. Dostanete ho v callbacku createTable(), alterTable() a súvisiacich pomocných metód. Tieto metódy sa volajú z QueryBuildera vo vnútri q() alebo cez schema(). Cieľom je programovo definovať tabuľky, stĺpce, indexy a cudzie kľúče. Výsledné príkazy sa prevedú na SQL a vykonajú aktívnym driverom (MySQLi alebo PDO).
Kľúčové vlastnosti:
- Reťaziteľné metódy: Rovnako ako
QueryBuilderje ajSchemaBuildernavrhnutý na reťazenie. - Abstrakcia: Funguje nezávisle od databázového drivera, hoci niektoré funkcie závisia od databázového systému.
- Jednoduchosť: Schému môžete definovať bez písania plnej SQL syntaxe.
6.2. Metódy SchemaBuildera
Prehľad hlavných metód, ich argumentov a príkladov.
Pomocné metódy stĺpcov vracajú definíciu stĺpca. Na tento objekt reťazte modifikátory: nullable(), default(), unsigned() (len MySQL) a comment(). Príznak nullable nepredávajte ako koncový argument do string() ani integer(). Pomocná metóda timestamps() neexistuje — created_at a updated_at deklarujte sami cez datetime() (alebo timestamp(), ak výslovne chcete stĺpec TIMESTAMP).
Ďalšie pomocné metódy stĺpcov zahŕňajú text(), decimal($name, $precision = 10, $scale = 2), timestamp(), date(), boolean(), bigInteger() a tinyInteger().
6.2.1. id()
Pridá primárny kľúč BIGINT AUTO_INCREMENT.
Syntax: id($name = 'id')
Parametre:
$name: Názov stĺpca (predvolene'id').
SQL ekvivalent: id BIGINT NOT NULL AUTO_INCREMENT plus obmedzenie primárneho kľúča. Na MySQL môžete reťaziť ->unsigned().
Príklad:
$schema->id(); // Creates the `id` column
6.2.2. string()
Pridá stĺpec VARCHAR.
Syntax: string($name, $length = 255)
Parametre:
$name: Názov stĺpca.$length: Dĺžka (predvolene 255).
NULL povolíte reťazením nullable(): $schema->string('name', 100)->nullable().
SQL ekvivalent: VARCHAR(length) [NOT NULL | NULL]
Príklad:
$schema->string('name', 100)->nullable(); // `name` VARCHAR(100) NULL
6.2.3. integer()
Pridá stĺpec INT.
Syntax: integer($name)
Parametre:
$name: Názov stĺpca.
NULL povolíte reťazením nullable(): $schema->integer('age')->nullable().
SQL ekvivalent: INT [NOT NULL | NULL]
Príklad:
$schema->integer('age'); // `age` INT NOT NULL
$schema->integer('age')->nullable(); // `age` INT NULL
6.2.4. created_at / updated_at
Stĺpce dátumu a času deklarujte cez datetime(). Nevolajte timestamps() — táto metóda neexistuje. timestamp() použite len vtedy, keď chcete stĺpec TIMESTAMP.
Syntax: datetime('created_at') / datetime('updated_at')
SQL ekvivalent:
created_at DATETIME NOT NULL
updated_at DATETIME NOT NULL
Príklad:
$schema->datetime('created_at');
$schema->datetime('updated_at');
6.2.5. foreign()
Pridá cudzí kľúč.
Syntax: foreign($column, $name = null) a následne reťazte ->references($col)->on($table)->onDelete($action).
Parametre:
$column: Lokálny stĺpec, ktorý drží cudzí kľúč.$name: Voliteľný názov obmedzenia.
Na objekte vrátenom z foreign() reťazte references(), on() a onDelete().
SQL ekvivalent: FOREIGN KEY (column) REFERENCES table (references) ON DELETE CASCADE
Príklad:
$schema->foreign('user_id')->references('id')->on('shop_items')->onDelete('CASCADE');
6.2.6. index()
Pridá index na jeden alebo viac stĺpcov.
Syntax: index($columns, $name = null)
Parametre:
$columns: Názov stĺpca alebo pole názvov stĺpcov.$name: Voliteľný názov indexu.
SQL ekvivalent: INDEX (column)
Príklad:
$schema->index('name');
6.2.7. addColumn() (pre ALTER TABLE)
Pridá nový stĺpec do existujúcej tabuľky.
Syntax: addColumn($name, $type, $length = null, $nullable = false, $default = null, $comment = null)
Parametre:
$name: Názov stĺpca.$type: Typ (napríkladVARCHAR,INT).$length: Dĺžka (voliteľné).$nullable: PovoliťNULL(predvolenefalse).$default: Predvolená hodnota (voliteľné).$comment: Komentár stĺpca (voliteľné).
SQL ekvivalent: ADD column type [length] [NOT NULL | NULL]
Príklad:
$schema->addColumn('email', 'VARCHAR', 150, true);
6.2.8. dropColumn() (pre ALTER TABLE)
Odstráni stĺpec z tabuľky.
Syntax: dropColumn($name)
Parametre:
$name: Názov stĺpca.
SQL ekvivalent: DROP COLUMN column
Príklad:
$schema->dropColumn('email');
6.3. Použitie SchemaBuildera
SchemaBuilder používajte s metódami QueryBuildera createTable(), alterTable() a dropTable(). Spúšťajte ich vo vnútri DB::module('RAW')->q(function ($qb) { ... })->execute($ok, $err). Rovnakú prácu s QueryBuilderom môžete zabaliť aj do DB::module('RAW')->schema($callback, $success, $error). Do execute() vždy odovzdajte oba callbacky a do schema() vždy odovzdajte chybový callback.
6.3.1. Vytvorenie tabuľky
createTable() vytvorí novú tabuľku.
Príklad:
DB::module('RAW')->q(function ($qb) {
$qb->createTable('shop_items', function ($schema) {
$schema->id();
$schema->string('name', 50);
$schema->integer('age')->nullable();
$schema->datetime('created_at');
$schema->datetime('updated_at');
$schema->index('name');
});
})->execute(
function ($result, $db, $debug) {
echo "Table 'shop_items' was created.\n";
echo "SQL: {$debug['query']}\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Rovnaké DDL cez schema():
DB::module('RAW')->schema(
function ($qb) {
$qb->createTable('shop_items', function ($schema) {
$schema->id();
$schema->string('name', 50);
$schema->integer('age')->nullable();
$schema->datetime('created_at');
$schema->datetime('updated_at');
$schema->index('name');
});
},
function ($result, $db, $debug) {
echo "Table 'shop_items' was created.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Vygenerované SQL:
CREATE TABLE shop_items (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`age` INT NULL,
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
INDEX `idx_name` (`name`),
CONSTRAINT `pk_id` PRIMARY KEY (`id`)
)
6.3.2. Úprava tabuľky
alterTable() zmení existujúcu tabuľku.
Príklad:
DB::module('RAW')->q(function ($qb) {
$qb->alterTable('shop_items', function ($schema) {
$schema->addColumn('email', 'VARCHAR', 100, true);
$schema->foreign('user_id')->references('id')->on('shop_items')->onDelete('CASCADE');
$schema->dropColumn('age');
});
})->execute(
function ($result, $db, $debug) {
echo "Table 'shop_items' was altered.\n";
echo "SQL: {$debug['query']}\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Vygenerované SQL:
ALTER TABLE shop_items
ADD `email` VARCHAR(100) NULL,
ADD FOREIGN KEY (`user_id`) REFERENCES `shop_items` (`id`) ON DELETE CASCADE,
DROP COLUMN `age`
6.3.3. Odstránenie tabuľky
dropTable() odstráni tabuľku.
Príklad:
DB::module('RAW')->q(function ($qb) {
$qb->dropTable('shop_items');
})->execute(
function ($result, $db, $debug) {
echo "Table 'shop_items' was dropped.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Vygenerované SQL:
DROP TABLE shop_items
6.4. Pokročilé príklady
Vytvorenie tabuľky s cudzími kľúčmi:
DB::module('RAW')->q(function ($qb) {
$qb->createTable('shop_posts', function ($schema) {
$schema->id();
$schema->string('title', 200);
$schema->integer('user_id');
$schema->foreign('user_id')->references('id')->on('shop_items')->onDelete('CASCADE');
$schema->datetime('created_at');
$schema->datetime('updated_at');
});
})->execute(
function ($result, $db, $debug) {
echo "Table 'shop_posts' created.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
Hromadná zmena schémy v transakcii:
DB::module('RAW')->transact(
function ($db, $commitOnSuccess, $rollbackOnError) {
$db->q(function ($qb) {
$qb->createTable('shop_items', function ($schema) {
$schema->id();
$schema->string('name');
});
})->execute($commitOnSuccess, $rollbackOnError);
$db->q(function ($qb) {
$qb->createTable('shop_posts', function ($schema) {
$schema->id();
$schema->integer('user_id');
$schema->foreign('user_id')->references('id')->on('shop_items')->onDelete('CASCADE');
});
})->execute($commitOnSuccess, $rollbackOnError);
},
function ($result, $db, $debug) {
echo "Schema change succeeded.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
6.5. Poznámky a obmedzenia
Kompatibilita: Niektoré funkcie (napríklad ON DELETE CASCADE) sa na každom databázovom systéme nesprávajú rovnako. SQLite má najmä obmedzenú podporu odstraňovania stĺpcov, indexov a cudzích kľúčov.
Transakcie: Pri väčších zmenách schémy použite DB::module('RAW')->transact(...), aby práca ostala konzistentná.
Ladenie: Vždy skontrolujte $debug['query'] (tretí argument execute), aby ste overili vygenerované SQL.
Výnimky: SchemaBuilder vyvolá \InvalidArgumentException pri neplatných identifikátoroch, nepodporovaných typoch a chýbajúcich cieľoch cudzieho kľúča. DDL zabalte do try/catch.
7. CacheDriverInterface
Databaser vo frameworku DotApp vie kešovať výsledky dotazov, aby sa opakované požiadavky na tie isté dáta vyhli databáze. Kešovanie dotazov je zapnuté, keď Config::db('cache') === true. Vlastné úložisko musí sprístupňovať get a set; pripojte ho cez DB::module()->cache($driverObject) (uprednostnite to pred DB::cache()). Pri zápisoch ORM potrebujete aj deleteKeys(). Táto kapitola opisuje očakávanú zmluvu drivera a jeho použitie s Databaserom.
7.1. Čo je CacheDriverInterface?
CacheDriverInterface je zmluva na ukladanie a načítanie kešovaných výsledkov dotazov. Databaser vie komunikovať s ľubovoľným úložiskom (Memcached, Redis, súborový systém), pokiaľ váš objekt implementuje metódy nižšie. Po priradení drivera cez cache() sa Databaser pred spustením dotazu pokúsi o get() a po úspešnom vykonaní o set().
Dôležité: Entity::save() so zapnutou kešou vyžaduje na driveri deleteKeys(). Žiadny dodávaný driver deleteKeys() neimplementuje. db.cache nechajte vypnuté, kým nedodáte vlastný driver, ktorý implementuje všetky štyri metódy vrátane deleteKeys().
Výhody:
- Nižšie zaťaženie databázy.
- Rýchlejší prístup k často požadovaným dátam.
- Flexibilita — môžete použiť ľubovoľné úložisko keše.
7.2. Metódy CacheDriverInterface
Rozhranie definuje štyri metódy. Kešovanie dotazov používa get a set. Invalidácia pri Entity::save() vyžaduje aj deleteKeys():
interface CacheDriverInterface {
public function get($key);
public function set($key, $value, $ttl = null);
public function delete($key);
public function deleteKeys($pattern);
}
7.2.1. get($key)
Načíta hodnotu z keše podľa kľúča.
Parameter:
$key: Reťazec — jedinečný kľúč uložených dát.
Návratová hodnota: Uložená hodnota, alebo null, ak kľúč neexistuje.
Účel: Databaser volá túto metódu, aby overil, či je výsledok dotazu už v keši.
7.2.2. set($key, $value, $ttl = null)
Uloží hodnotu do keše pod daným kľúčom.
Parametre:
$key: Reťazec — kľúč úložiska.$value: Dáta na uloženie (pole, objekt a podobne).$ttl: Životnosť v sekundách (voliteľné;nullznamená bez expirácie na úrovni drivera).Databaservždy predá3600.
Návratová hodnota: Žiadna (alebo true/false podľa implementácie).
Účel: Po úspešnom dotaze Databaser uloží výsledok do keše.
7.2.3. delete($key)
Odstráni jeden kľúč z keše.
Parameter:
$key: Reťazec — kľúč na odstránenie.
Návratová hodnota: Žiadna (alebo true/false).
Účel: Explicitné odstránenie jednej položky keše.
7.2.4. deleteKeys($pattern)
Odstráni viacero kľúčov, ktoré vyhovujú vzoru.
Parameter:
$pattern: Reťazec — vzor kľúča (napríklad"shop_items:*").
Návratová hodnota: Žiadna (alebo počet odstránených kľúčov).
Účel: Databaser volá túto metódu pri zmene dát (napríklad Entity::save() v ORM), aby sa súvisiace položky keše invalidovali. Ak je cache driver nastavený a táto metóda chýba, save() vyvolá výnimku.
7.3. Implementácia vlastného cache drivera
Príklad jednoduchého súborového cache drivera. Berte ho ako ukážku vlastného drivera, nie ako dodávanú triedu frameworku:
class FileCacheDriver implements CacheDriverInterface {
private $cacheDir;
public function __construct($cacheDir = '/tmp/cache') {
$this->cacheDir = $cacheDir;
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0777, true);
}
}
public function get($key) {
$file = $this->cacheDir . '/' . md5($key);
if (file_exists($file)) {
$data = unserialize(file_get_contents($file));
if ($data['expires'] === null || $data['expires'] > time()) {
return $data['value'];
}
unlink($file); // Expired — remove it
}
return null;
}
public function set($key, $value, $ttl = null) {
$file = $this->cacheDir . '/' . md5($key);
$expires = $ttl ? time() + $ttl : null;
$data = ['value' => $value, 'expires' => $expires];
file_put_contents($file, serialize($data));
return true;
}
public function delete($key) {
$file = $this->cacheDir . '/' . md5($key);
if (file_exists($file)) {
unlink($file);
return true;
}
return false;
}
public function deleteKeys($pattern) {
$count = 0;
foreach (glob($this->cacheDir . '/*') as $file) {
$key = basename($file);
if (fnmatch($pattern, $key)) {
unlink($file);
$count++;
}
}
return $count;
}
}
Vysvetlenie:
get(): Načíta dáta zo súboru, ak ešte nevypršali.set(): Zapíše dáta do súboru s voliteľným TTL.delete(): Odstráni konkrétny súbor.deleteKeys(): Odstráni súbory vyhovujúce vzoru (používafnmatch).
7.4. Použitie cache drivera s Databaserom
Kešovanie dotazov zapnite cez Config::db('cache') === true. Potom priraďte úložisko cez DB::module()->cache($driverObject). Objekt musí sprístupňovať get($key) a set($key, $value, $lifetime). Uprednostnite to pred DB::cache().
$cacheDriver = new FileCacheDriver('/tmp/myapp_cache');
DB::module()->cache($cacheDriver);
// Example query with caching
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('age', '>', 18);
})->execute(
function ($result, $db, $execution_data) {
echo "Results (from cache or DB):\n";
var_dump($result);
// On a cache hit, $execution_data is an empty array.
},
function ($error, $db, $execution_data) {
echo "Error: {$error['error']}\n";
}
);
Ako to funguje:
Databaserzloží kľúč v tvare"{table}:{returnType}:" . md5($query . serialize($bindings))(napríkladshop_items:RAW:nasledovaný hashom).- Keš overí cez
get(). Pri zásahu vráti uloženú hodnotu bez dopytu do databázy a do callbacku úspechu doručí prázdne$execution_data. - Pri minule spustí dotaz a výsledok uloží cez
set(). TTL je natvrdo 3600 sekúnd. - Pri aktualizácii ORM, napríklad
Entity::save(), invaliduje súvisiace kľúče cezdeleteKeys().
7.5. Pokročilý príklad kešovania
Kešovanie s ORM a invalidáciou:
$cacheDriver = new FileCacheDriver();
DB::module()->cache($cacheDriver);
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
$user = $rows[0] ?? null;
if ($user !== null) {
$user->age = 40;
$user->save(
function ($result, $db, $execution_data) {
echo "Item saved, cache invalidated.\n";
},
function ($error, $db, $execution_data) {
echo "Error: {$error['error']}\n";
}
);
}
Čo sa stane:
- Prvý dotaz uloží
Collectiondo keše. - Pri
save()sa spustídeleteKeys("shop_items:ORM:*")a invalidujú sa všetky ORM položky keše preshop_items. - Ak priradený driver nemá
deleteKeys(),save()vyvolá výnimku.db.cachenechajte vypnuté, kým váš vlastný driver neimplementuje všetky štyri metódy.
7.6. Poznámky a tipy
TTL: Databaser ukladá výsledky dotazov na 3600 sekúnd.
Formát kľúča: Kľúče používajú "{table}:{returnType}:" . md5(...), takže vzory ako "shop_items:*" zodpovedajú položkám tabuľky.
Zásahy keše: Callback úspechu sa stále spustí, ale $execution_data je prázdne.
deleteKeys(): Žiadny dodávaný driver ju neimplementuje. Config::db('cache') nechajte vypnuté, kým nedodáte vlastný driver, ktorý implementuje get, set, delete a deleteKeys().
Výkon: V produkcii uprednostnite rýchle úložisko ako Redis pred súbormi — stále až potom, čo toto úložisko implementuje štyri metódy vyššie.
Testovanie: Overte, že deleteKeys() skutočne invaliduje keš, aby ste po save() neposkytovali zastarané riadky.
8. Práca s Entity
Entity reprezentuje jeden riadok tabuľky v module ORM. Entity získate cez DB::module('ORM'), prvý riadok však načítajte bezpečne cez all() a $rows[0] ?? null.
Poznámka k vzťahom ORM: with(), whereHas() a withCount() v DotApp PHP Framework 2.0 len ukladajú stav a SQL neovplyvňujú. Neprezentujte ich ako eager loading. Vzťahy načítajte volaním metód Entity, napríklad $user->hasMany('shop_posts', 'user_id').
8.2. Základné použitie Entity
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('id', '=', 1)->limit(1);
})->all();
$user = $rows[0] ?? null;
if ($user) {
echo $user->name;
$user->age = 26;
$user->save(
function ($result, $db, $debug) {
echo "User saved.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
8.3. Vzťahy
Vzťahy sú metódy volané na entite. Voliteľný callback môže upraviť súvisiaci dotaz, napríklad cez where(), orderBy() alebo limit().
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('id', '=', 1)->limit(1);
})->all();
$user = $rows[0] ?? null;
$posts = $user ? $user->hasMany('shop_posts', 'user_id', null, function ($qb) {
$qb->orderBy('created_at', 'DESC')->limit(2);
}) : [];
foreach ($posts as $post) {
echo $post->title . "\n";
}
Metódy vzťahov dostupné na Entity:
hasOne($relatedTable, $foreignKey, $localKey = null, $callback = null)→Entity|nullbelongsTo($relatedTable, $foreignKey, $ownerKey = null, $callback = null)→Entity|nullhasMany($relatedTable, $foreignKey, $localKey = null, $callback = null)→CollectionmorphOne($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null)→Entity|nullmorphMany($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null)→CollectionmorphTo($name = null, $type = null, $id = null, $ownerKey = null)je tiež dostupná a vraciaEntity|null
Polymorfný vzťah s filtrom
Polymorfný vzťah ukladá typ a id rodiča na súvisiacom riadku. Callbackom môžete súvisiaci dotaz filtrovať, zoradiť alebo obmedziť. Rodiča načítajte cez all() a $rows[0] ?? null:
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('id', '=', 1)->limit(1);
})->all();
$item = $rows[0] ?? null;
if ($item) {
$recentImages = $item->morphMany('shop_images', 'imageable_type', 'imageable_id', 'shop_items', null, function ($qb) {
$qb->orderBy('created_at', 'DESC')->limit(3);
});
foreach ($recentImages as $image) {
echo "Latest image: {$image->url}\n";
}
}
8.4. Vloženie novej Entity
$item = DB::newEntity();
$item->table('shop_items');
$item->name = 'Jane Novak';
$item->age = 30;
$item->save(
function ($result, $db, $debug) {
echo "New record created with ID: {$db->inserted_id()}.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
9. Práca s Collection
Collection je množina entít vrátená z all(). Používajte iteráciu, filter(), map(), pluck(), toArray() a count(). Nepoužívajte saveAll(); Entity::save() vracia void, takže každú entitu uložte samostatne a vždy odovzdajte chybový callback.
9.2. Základné použitie Collection
$items = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
foreach ($items as $item) {
echo "Name: {$item->name}\n";
}
$allItems = $items->all();
$first = $allItems[0] ?? null;
9.3. Filter, map a samostatné ukladanie
$items = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
$active = $items->filter(function ($item) {
return (int) $item->active === 1;
});
foreach ($active as $item) {
$item->checked_at = date('Y-m-d H:i:s');
$item->save(
null,
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
10. Databázová schéma
Štruktúru tabuliek definujete v kóde cez SchemaBuilder a v module cez verzovaný inštalátor Installation.php. Tabuľky a stĺpce môžete vytvárať, upravovať a odstraňovať. Dávkové operácie zabalte do transakcie cez transact(). Nikdy nevolajte DB::migrate(). Pomocná metóda timestamps() neexistuje — stĺpce datetime() pridajte explicitne, keď ich potrebujete.
10.2. Čo je správa schémy?
Tabuľky a vzťahy definujete programovo. V Databaseri to robíte cez SchemaBuilder; dávkové zmeny zabalte do transakcie cez transact(). Hlavné výhody:
- Automatizácia: Zmeny databázy žijú v kóde a dajú sa verzovať.
- Transakcie: Dávkové operácie sú bezpečné a pri chybe vratné.
- Multiplatformovosť: Podpora rôznych driverov (MySQLi, PDO) so syntaxou prispôsobenou databáze.
10.3. Základné princípy
Správa schémy v Databaseri stojí na týchto princípoch:
- SchemaBuilder: Definícia tabuliek a stĺpcov (napríklad
id(),string(),foreign(),datetime()). - Installation.php: Verzovaná inštalácia a odinštalácia tabuliek modulu, chránená cez
self::alreadyDoneaself::markDone. - Transakcie: Dávkové zmeny cez
transact(), kde sa viacero operácií spustí ako jedna jednotka. - Podpora driverov: MySQLi a PDO prispôsobia syntax typu databázy (napríklad MySQL, PostgreSQL, SQLite).
10.4. Použitie schémy
Definujte štruktúru tabuľky a aplikujte ju. Do schema() vždy odovzdajte chybový callback. Príklad vytvorenia tabuľky:
DB::module('RAW')->schema(function ($schema) {
$schema->createTable('shop_items', function ($table) {
$table->id();
$table->string('name');
});
}, function ($result, $db, $debug) {
echo "Table 'shop_items' was created successfully.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
Výstup:
Table 'shop_items' was created successfully.
Dávková zmena v transakcii (viacero tabuliek):
DB::module('RAW')->transact(function ($db) {
$db->q(function ($qb) {
$qb->createTable('shop_items', function ($schema) {
$schema->id();
$schema->string('name');
});
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
$db->q(function ($qb) {
$qb->createTable('shop_posts', function ($schema) {
$schema->id();
$schema->integer('user_id');
$schema->foreign('user_id')->references('id')->on('shop_items')->onDelete('CASCADE');
});
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
}, function ($result, $db, $debug) {
echo "Schema change succeeded.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
Výstup:
Schema change succeeded.
10.5. Dostupné metódy schémy
Databaser poskytuje tieto metódy na prácu so schémou:
10.5.1. schema($callback, $success, $error)
Definuje a spustí jednu operáciu schémy (napríklad vytvorenie tabuľky). Chybový callback vždy odovzdajte.
Syntax: schema(callable $callback, callable $success = null, callable $error = null)
Parametre:
$callback: Closure, ktoré definuje operáciu cezSchemaBuilder.$success: Callback pri úspechu.$error: Callback pri chybe (vždy ho odovzdajte).
Príklad:
DB::module('RAW')->schema(function ($schema) {
$schema->createTable('shop_items', function ($table) {
$table->id();
$table->string('email', 100);
});
}, function ($result, $db, $debug) {
echo "Table created.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
Výstup:
Table created.
10.5.2. Schéma modulu cez Installation.php
Tabuľky modulu vytvárajte a verzujte z Installation.php. Každú verziu chráňte cez self::alreadyDone a zaznamenajte cez self::markDone. Nikdy nevolajte DB::migrate().
DB::module('RAW')->q(function ($qb) {
$qb->raw(
"CREATE TABLE IF NOT EXISTS `shop_items` (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(200) NOT NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
[]
);
})->execute(
function () { /* self::markDone('1.0.0'); */ },
function ($error) { \Dotsystems\App\Parts\Logger::use()->error('schema failed', $error); }
);
10.5.3. transact($operations, $success, $error)
Spustí dávku zmien schémy v transakcii. Volajte ju na inštancii modulu: DB::module('RAW')->transact(...).
Syntax: transact(callable $operations, callable $success = null, callable $error = null)
Parametre:
$operations: Closure s viacerými operáciami schémy. Prvý argument je inštancia modulu ($db).$success: Callback pri úspechu (commit).$error: Callback pri chybe (rollback). Vždy ho odovzdajte.
Príklad:
DB::module('RAW')->transact(function ($db) {
$db->q(function ($qb) {
$qb->createTable('shop_comments', function ($schema) {
$schema->id();
$schema->integer('post_id');
});
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
}, function ($result, $db, $debug) {
echo "Batch schema change succeeded.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
Výstup:
Batch schema change succeeded.
10.5.4. SchemaBuilder::createTable($table, $callback)
Vytvorí novú tabuľku s definovanou štruktúrou.
Syntax: createTable(string $table, callable $callback)
Príklad:
DB::module('RAW')->schema(function ($schema) {
$schema->createTable('shop_products', function ($table) {
$table->id();
$table->string('name');
$table->decimal('price', 8, 2);
});
}, function ($result, $db, $debug) {
echo "Table 'shop_products' created.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
10.5.5. SchemaBuilder::alterTable($table, $callback)
Upraví existujúcu tabuľku (napríklad pridá stĺpec).
Syntax: alterTable(string $table, callable $callback)
Príklad:
DB::module('RAW')->schema(function ($schema) {
$schema->alterTable('shop_items', function ($table) {
$table->addColumn('age', 'INT', null, true);
});
}, function ($result, $db, $debug) {
echo "Column added.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
10.5.6. SchemaBuilder::dropTable($table)
Odstráni tabuľku.
Syntax: dropTable(string $table)
Príklad:
DB::module('RAW')->schema(function ($schema) {
$schema->dropTable('shop_items');
}, function ($result, $db, $debug) {
echo "Table dropped.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
10.6. Praktické príklady
Vytvorenie tabuliek s cudzím kľúčom:
DB::module('RAW')->transact(function ($db) {
$db->q(function ($qb) {
$qb->createTable('shop_items', function ($schema) {
$schema->id();
$schema->string('username');
});
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
$db->q(function ($qb) {
$qb->createTable('shop_posts', function ($schema) {
$schema->id();
$schema->string('title');
$schema->integer('user_id');
$schema->foreign('user_id')->references('id')->on('shop_items')->onDelete('CASCADE');
});
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
}, function ($result, $db, $debug) {
echo "Tables created.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
Výstup:
Tables created.
Úprava tabuľky (pridanie stĺpca):
DB::module('RAW')->schema(function ($schema) {
$schema->alterTable('shop_items', function ($table) {
$table->string('email', 100);
});
}, function ($result, $db, $debug) {
echo "Email column added.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
Výstup:
Email column added.
Tabuľky odstraňujte z Installation::uninstaller() cez DB::module('RAW')->q(...)->execute($ok, $err).
10.7. Poznámky
- Transakcie: Na dávkové zmeny schémy použite na inštancii modulu
transact(), aby databáza ostala konzistentná. Na tej istej inštancii môžete volať ajtransaction(),commit()arollback(). - Podpora driverov: Syntax sa prispôsobí driveru (napríklad MySQL vs. SQLite), niektoré funkcie (napríklad
ON UPDATEna Oracle) však nemusia byť plne podporované. - Inštalácia schémy: Tabuľky modulu vytvárajte v
Installation.phpcezself::alreadyDone/self::markDone. Nikdy nevolajteDB::migrate(). - Chybové callbacky: Chybový callback vždy odovzdajte do
schema(),execute()ajtransact().
11. Prípadová štúdia: e-shop s ORM
Táto kapitola je praktická prípadová štúdia, ktorá ukazuje, ako použiť Databaser a jeho ORM na jednoduchý e-shop. Navrhneme štruktúru databázy, vytvoríme tabuľky, naplníme ich dátami a s dátami pracujeme cez Entity a Collection. Príklady obsahujú callbacky error, aby ste mohli použiť spoľahlivé spracovanie chýb.
11.1. Návrh databázovej štruktúry
Pre e-shop použijeme tieto tabuľky:
- shop_customers: Zákazníci a administrátori.
- shop_products: Produkty v katalógu.
- shop_product_descriptions: Popisy produktov (jeden produkt ich môže mať viac, napríklad v rôznych jazykoch).
- shop_orders: Objednávky.
- shop_order_items: Položky objednávky (produkty naviazané na objednávky).
SQL na vytvorenie tabuliek
Tieto príkazy môžete skopírovať a spustiť v databáze MySQL:
-- Customers
CREATE TABLE shop_customers (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
role ENUM('customer', 'admin') DEFAULT 'customer',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Products
CREATE TABLE shop_products (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Product descriptions
CREATE TABLE shop_product_descriptions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT UNSIGNED NOT NULL,
language VARCHAR(10) NOT NULL,
description TEXT NOT NULL,
FOREIGN KEY (product_id) REFERENCES shop_products(id) ON DELETE CASCADE
);
-- Orders
CREATE TABLE shop_orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_id BIGINT UNSIGNED NOT NULL,
total_price DECIMAL(10, 2) NOT NULL,
status ENUM('pending', 'shipped', 'delivered') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES shop_customers(id) ON DELETE CASCADE
);
-- Order items
CREATE TABLE shop_order_items (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT UNSIGNED NOT NULL,
product_id BIGINT UNSIGNED NOT NULL,
quantity INT NOT NULL DEFAULT 1,
price DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES shop_orders(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES shop_products(id) ON DELETE CASCADE
);
SQL na naplnenie dát
Tieto príkazy naplnia tabuľky ukážkovými dátami:
-- Customers
INSERT INTO shop_customers (name, email, role) VALUES
('Jane Novak', 'jane@example.com', 'customer'),
('Admin Peter', 'admin@example.com', 'admin');
-- Products
INSERT INTO shop_products (name, price, stock) VALUES
('White t-shirt', 15.99, 50),
('Black shoes', 49.99, 20),
('Winter jacket', 89.99, 10);
-- Product descriptions
INSERT INTO shop_product_descriptions (product_id, language, description) VALUES
(1, 'en', 'Comfortable white cotton t-shirt.'),
(1, 'sk', 'Comfortable white cotton t-shirt.'),
(2, 'en', 'Elegant black shoes for any occasion.'),
(3, 'en', 'Warm winter jacket with a hood.');
-- Orders
INSERT INTO shop_orders (customer_id, total_price, status) VALUES
(1, 65.98, 'pending'),
(1, 89.99, 'shipped');
-- Order items
INSERT INTO shop_order_items (order_id, product_id, quantity, price) VALUES
(1, 1, 2, 15.99),
(1, 2, 1, 49.99),
(2, 3, 1, 89.99);
11.2. Implementácia v Databaser s ORM
Príklady ORM používajú DB::module('ORM'), bezpečné čítanie cez all() a explicitné vzťahy cez hasMany(). Nepoužívajte with() tak, ako keby v SQL načítal súvisiace riadky — SQL negeneruje.
11.2.1. Načítanie zákazníka a jeho objednávok
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_customers')->where('id', '=', 1)->limit(1);
})->all();
$customer = $rows[0] ?? null;
$orders = $customer ? $customer->hasMany('shop_orders', 'customer_id') : [];
foreach ($orders as $order) {
echo "Order #{$order->id}: {$order->status}\n";
}
11.2.2. Pridanie nového produktu s popisom
DB::module('RAW')->transact(function ($db) {
$product = $db->newEntity();
$product->table('shop_products');
$product->name = 'Green scarf';
$product->price = 19.99;
$product->stock = 30;
$product->save(
function ($result, $db, $debug) {
$description = $db->newEntity();
$description->table('shop_product_descriptions');
$description->product_id = $db->inserted_id();
$description->language = 'en';
$description->description = 'Warm green scarf for winter.';
$description->save(null, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}, function ($result, $db, $debug) {
echo "Product added.\n";
}, function ($error, $db, $debug) {
echo "Transaction error: {$error['error']}\n";
});
11.2.3. Zobrazenie objednávky s položkami
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_orders')->where('id', '=', 1)->limit(1);
})->all();
$order = $rows[0] ?? null;
$items = $order ? $order->hasMany('shop_order_items', 'order_id') : [];
foreach ($items as $item) {
$productRows = DB::module('ORM')->q(function ($qb) use ($item) {
$qb->select('name', 'shop_products')->where('id', '=', $item->product_id)->limit(1);
})->all();
$product = $productRows[0] ?? null;
if ($product) {
echo "Item: {$product->name}, quantity: {$item->quantity}\n";
}
}
11.3. Použitie validácie
Pred uložením pridajte validáciu produktu.
$product = DB::newEntity();
$product->table('shop_products');
$product->setRules([
'name' => ['required', 'string', 'max:100'],
'price' => ['required', 'numeric', 'min:0'],
'stock' => ['integer', 'min:0']
]);
$product->name = 'This English product name is deliberately written to be longer than one hundred characters so that Databaser validation rejects it';
$product->price = -5;
$product->stock = 10;
$product->save(
function ($result, $db, $debug) {
echo "Product saved successfully.\n";
},
function ($error, $db, $debug) {
echo "Validation failed: {$error['error']}\n";
}
);
11.4. Poznámky k štúdii
Transakcie: Použitie transact() na inštancii modulu udržuje súvisiace zápisy konzistentné; callbacky error hlásia zlyhania.
Vzťahy: Súvisiace riadky načítajte explicitnými metódami Entity, napríklad hasMany(). with(), whereHas() a withCount() sú zástupné metódy a SQL negenerujú — nie sú spôsobom načítania vzťahov.
Validácia: Pravidlá chránia pred neplatnými dátami a produkujú jasnú chybovú správu.
Spracovanie chýb: Callbacky error umožňujú reagovať na problémy (napríklad logovanie alebo oznámenia pre používateľa). Do Entity::save() vždy odovzdajte chybový callback.
12. Tipy a triky
Táto kapitola ponúka praktické rady na efektívne použitie Databaseru vo frameworku DotApp. Pokrýva optimalizáciu dotazov, bezpečnosť a body rozšírenia.
12.1. Optimalizácia dotazov
Efektívne dotazy sú kľúčom k rýchlej aplikácii. Niekoľko tipov:
- Vyberajte len stĺpce, ktoré potrebujete: Namiesto
select('*', 'shop_items')použite konkrétne stĺpce, napríkladselect('id, name', 'shop_items'). Tým sa zníži objem prenesených dát.
DB::module('RAW')->q(function ($qb) {
$qb->select('id, name', 'shop_items')->where('age', '>', 18);
})->execute(
function ($result, $db, $debug) {
var_dump($result);
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
- Používajte indexy: Pri častých filtroch v
where()(napríklad id, age) pridajte indexy cezschema():
DB::module('RAW')->schema(function ($schema) {
$schema->alterTable('shop_items', function ($table) {
$table->index('age');
});
}, function ($result, $db, $debug) {
echo "Index created.\n";
}, function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
});
- Stránkujte zoznamy, ktoré môžu rásť: Pri narastajúcich zoznamoch (položky, objednávky, logy) uprednostnite
paginate($perPage, $page)pred surovýmlimit()/offset().limit()aoffset()použite len vtedy, keď potrebujete jednorazový výrez.
$page = DB::module('RAW')->q(function ($qb) {
$qb->select('id, name', 'shop_items')->orderBy('id', 'DESC');
})->paginate(20, 1);
foreach ($page['data'] as $row) {
echo "{$row['name']}\n";
}
- Kešujte opakované dotazy: Ak máte implementovaný cache driver, použite ho na uloženie výsledkov:
DB::module('RAW')->cache($myCacheDriver)->q(function ($qb) {
$qb->select('*', 'shop_items');
})->execute(
function ($result, $db, $debug) {
echo "Results from cache or DB: ";
var_dump($result);
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
12.2. Bezpečnosť (prevencia SQL injection)
Databaser je navrhnutý s ohľadom na bezpečnosť, aj tak však stojí za to poznať overené postupy:
- Vždy používajte prepared statements:
QueryBuilderhodnoty escapuje automaticky, takže premenné nikdy nevkladajte do reťazca dotazu interpoláciou.
Správne:
DB::module('RAW')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('name', '=', 'Jane');
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
Nesprávne:
$name = "Jane'; DROP TABLE shop_items; --";
DB::module('RAW')->q(function ($qb) use ($name) {
$qb->raw("SELECT * FROM shop_items WHERE name = '$name'");
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
}); // Dangerous!
- Surové dotazy s RAW: Ak použijete
raw(), hodnoty vždy predávajte cez bindings:
DB::module('RAW')->q(function ($qb) {
$qb->raw('SELECT * FROM shop_items WHERE age > ?', [18]);
})->execute(null, function ($error) {
echo "Error: {$error['error']}\n";
});
- Validačné pravidlá v ORM: Pri ukladaní dát cez
Entitynastavte pravidlá:
$rows = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items')->where('id', '=', 1)->limit(1);
})->all();
$user = $rows[0] ?? null;
if ($user) {
$user->setRules(['name' => 'required|string|max:50']);
$user->name = 'Jane';
$user->save(
null,
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
}
12.3. Rozšírenie Databaseru o vlastné drivery
Vlastný databázový driver zaregistrujte cez Databaser::customDriver($name, $class). Trieda musí sprístupňovať public static function create(Databaser $db) a vo vnútri create() zaregistrovať closure drivera na danej inštancii.
DB::addDriver() nie je API modulu na registráciu drivera. Triedy driverov ho môžu používať interne z create(); kód aplikácie a modulu má volať Databaser::customDriver($name, $class).
Closure na registráciu: select_db, q, return, execute, first, all, raw, fetchArray, fetchFirst, newEntity, newCollection, inserted_id, affected_rows, schema, transaction, transact, commit, rollback.
Databaser::customDriver('custom', CustomDriver::class);
// CustomDriver::create(Databaser $db) registers the closures listed above.
12.4. Vzťahy ORM
Súvisiace riadky načítajte explicitnými metódami Entity, napríklad hasMany(). with() v DotApp 2.0 SQL negeneruje a nie je spôsobom načítania vzťahov:
$items = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
foreach ($items as $item) {
foreach ($item->hasMany('shop_posts', 'user_id') as $post) {
echo "Item: {$item->name}, Post: {$post->title}\n";
}
}
12.5. Dávkové operácie s Collection
Nepoužívajte saveAll(). Po map() uložte každú entitu samostatne cez save() a vždy odovzdajte chybový callback:
$items = DB::module('ORM')->q(function ($qb) {
$qb->select('*', 'shop_items');
})->all();
$items->map(function ($item) {
$item->age += 1;
$item->save(
function ($result, $db, $debug) {
echo "Item saved.\n";
},
function ($error, $db, $debug) {
echo "Error: {$error['error']}\n";
}
);
return $item;
});
Metódy: filter(), map(), pluck().