Framework DotApp Aktualizované: 2025-04-08


O autorovi

Volám sa Štefan Miščík a som senior fullstack web developer v spoločnosti Dotsystems s.r.o. (WEB)



Prečo som vyvinul DotApp?

1. Chcel som si zjednodušiť prácu.

Väčšina frameworkov ma nútila ísť kamiónom aj pre nákup špagiet. Preto som vytvoril DotApp – je škálovateľný a dokáže byť nákupnou taškou, vozíkom aj kamiónom, plne automaticky podľa veľkosti nákupu. DotApp je framework navrhnutý na zjednodušenie vývoja webových aplikácií – rýchly, efektívny a bez zbytočností. Ponúka schopnosti, ktoré ho robia ideálnym pre projekty všetkých veľkostí.

2. Chcel som otestovať svoje schopnosti.

Písal sa rok 2014, a ja som chcel vedieť, ako na tom vedomostne som. Používanie existujúcich frameworkov je pohodlné, no lákala ma výzva vytvoriť niečo vlastné. Nechcel som skončiť pri priemernom riešení, ale vybudovať framework, ktorý by obstál aj v náročných podmienkach. DotApp tak pre mňa predstavoval príležitosť posunúť svoje zručnosti na vyššiu úroveň a presvedčiť sa, čoho som schopný.



Kľúčové vlastnosti DotApp

Jednoduchosť bez kompromisov

DotApp kombinuje intuitívny dizajn s vysokým výkonom. Nepotrebujete zložité nastavenia ani prehnané konfigurácie – stačí definovať routy a moduly, a všetko ostatné sa spravuje samo. Routy sa spracovávajú iba tam, kde je to potrebné, a nepotrebujete žiadne dodatočné kroky na udržanie výkonu – všetko je automatické a efektívne.


// Ukážka jednoduchosti práce s DotApp frameworkom
namespace Dotsystems\App\Modules\DotAppDoc\Controllers;

class TestController1 {
    public static function testMiddlewareFn($request) {
        return "Ahoj " . $request->body(); // Pridá text na začiatok
    }
    
    public static function mainFn($request) {
        return $request->body() . "Svet"; // Pridá text na koniec
    }
}

// Jednoduché zavolanie controllera
$dotApp->router
    ->get("/home", "dotappdoc:TestController1@mainFn")
    ->before("dotappdoc:TestController1@testMiddlewareFn");

// Výsledok pre /home: "Ahoj Svet"
            
Zameranie na nízku spotrebu výpočtových zdrojov

DotApp udržuje pamäťové nároky na minime – namiesto načítania obrovských štruktúr routov a konfigurácií spracováva iba to, čo je aktuálne potrebné. To znamená rýchlejší štart a skvelý výkon aj na slabších serveroch.

Rýchle spracovanie routovania

DotApp inteligentne filtruje iba relevantné moduly a ich routy, čím eliminuje zbytočné prehľadávanie. Výsledkom je svižné načítanie aj pri tisícoch routách.

Ukážka

Demonštrácia rýchlosti routovania: Pred zobrazením tejto stránky bolo automaticky náhodne pridaných 1000 jedinečných statických a 1000 jedinečných dynamických a zámerne neorganizovaných routov do routera. Cieľom bolo ukázať svižné načítanie aj napriek 2000 zbytočným routam navyše. Žiadna z nich nezodpovedá aktuálnej URL adrese, čím je zabezpečené, že všetky musia prejsť procesom párovania v routeri.


for ($i = 0; $i < 1000; $i++) {
    $this->dotApp->router->any($this->getData("base_url") . "_routa" . $i, function () use ($dotApp, $i) {
        $dotApp->unprotect($_POST);
        echo "Toto je routa: " . $this->getData("base_url") . "_routa" . $i;
    }, true);
}

for ($i = 0; $i < 1000; $i++) {
    $this->dotApp->router->any($this->getData("base_url") . "_routa" . $i . "(?:/{language})?", function () use ($dotApp, $i) {
        $dotApp->unprotect($_POST);
        echo "Toto je routa: " . $this->getData("base_url") . "_routa" . $i . "(?:/{language})?";
    });
}

// Skúste si to: /documentation/_routa7
            
$this->dotApp->router->get($this->getData("base_url") . "intro", function () use ($dotApp) {
    /*
        Pre info:
        $this->moduleName - premenná automaticky nachádzajúca sa v každom inicializovanom module
        $this->getData("base_url") - pri registrácii listenerov modulu je v module dokumentácie nastavené base_url modulu (/documentation/)
            $dotApp->on("dotapp.module_dotappdoc.init.start", function ($moduleObj) {
                $moduleObj->setData("base_url", "/documentation/");
            });
        Vysvetlené v dokumentácii modulov.
    */
    $viewVars['seo']['description'] = "Description";
    $viewVars['seo']['keywords'] = "Keywords";
    $viewVars['seo']['title'] = "Dokumentácia k PHP frameworku DotApp";
    return $dotApp->router->renderer->module($this->moduleName)->setView("index")->setViewVar("variables", $viewVars)->renderView();
});
                            


Zobrazenie stránky, vrátane vytvorenia routov, routovania a generovania kódu pomocou šablónovacieho systému, trvalo:

Modulárna efektivita s obojsmernou prepojiteľnosťou

DotApp spracováva iba routy aktívneho modulu, čím šetrí zdroje. Moduly sú navzájom prepojiteľné v oboch smeroch – môžu aktivovať svoje závislosti (napr. modul 3 načíta modul 1 cez $dotApp->module("Module1")->load()) alebo byť spustené nadradeným modulom (napr. modul 1 aktivuje modul 2 a 3 cez listenery). Táto flexibilita umožňuje dynamicky kombinovať moduly podľa potreby projektu.

Kaskádové načítavanie modulov

Ak modul závisí od iného (napr. BBB potrebuje XXX), DotApp automaticky načíta XXX pred dokončením BBB. To zaisťuje spoľahlivosť – žiadne chyby kvôli chýbajúcim závislostiam – a udržuje systém ľahký, pretože sa načítajú iba potrebné časti.

Dynamická správa závislostí cez triggery a listenery

Každý modul má triggery ako init.start, loading, loaded a ďalšie, na ktoré reagujú listenery. Napríklad listener dotapp.module.Module1.loading môže spustiť načítanie modulu 2, ak je modul 1 aktívny. Funkcia load() zaisťuje, že modul sa načíta iba raz, či už kaskádovo (zhora nadol) alebo obojsmerne (zdola nahor).

Poznámka: Názvy triggerov sú case-insensitive, takže dotapp.module.Module1.loading a Dotapp.Module.Module1.Loading sú ekvivalentné, ale odporúčame používať formát dotapp.module.NázovTriedy.názovUdalosti pre konzistenciu.


// Listener pre kaskádové načítavanie
$dotApp->on("dotapp.module.Module1.loading", function () use ($dotApp) {
    $dotApp->module("Module2")->load(); // Načíta Module2 iba ak je potrebný
});
            
Automatické riešenie závislostí a DI

Moduly a ich závislosti sa načítajú automaticky – stačí definovať logiku v initializeCondition() alebo listeneroch. Dependency Injection (DI) je jednoduché a efektívne – služby sa registrujú (napr. singleton) a DotApp ich podá tam, kde sú potrebné, bez zbytočného zaťaženia.


// Registrácia singletonu v module
$this->singleton(DotApp::class, function () { return $this; });

// Použitie DI v controlleri
namespace Dotsystems\App\Modules\DotAppDoc\Controllers;

use Dotsystems\App\DotApp;

class TestController2 {
    public static function injectionTest(DotApp $dotApp) {
        echo "Automaticky injektovaný DotApp";
    }
}
            
Prvý callback víťazí

Pre každú URL sa uchováva iba prvý zodpovedajúci callback – ďalšie pokusy o registráciu sa ignorujú, čo zvyšuje výkon a predchádza konfliktom.


$dotApp->router->get('/documentation/test1', "dotappdoc:className@fnName");
$dotApp->router->get('/documentation/test1', function () { return "Ignorované"; });
// Použije sa iba prvá definícia
            
Škálovateľnosť pre malé aj veľké projekty

DotApp je ideálny pre malé stránky aj komplexné aplikácie – zachováva nízke nároky a vysokú rýchlosť bez ohľadu na rozsah projektu. Veľké moduly sa dajú rozdeliť na menšie časti, ktoré sa načítajú rekurzívne podľa potreby.

Žiadne zbytočné réžie

DotApp sa sústreďuje na podstatu – rýchle routovanie, minimálna spotreba zdrojov a jednoduché použitie. Nenúti vás niesť bremeno funkcií, ktoré nepotrebujete.

DotApp Bridge

Živé prepojenie – most medzi frontendom a backendom. Stačí jednoduchý kód:

<button {{ dotbridge:on(click)="dotcms.newsletter(newsletter.email)" }}>Prihlásiť</button>

a na strane PHP:


$dotApp->bridge->fn("dotcms.newsletter", function ($data) { /* Logika */ });
            

Tlačidlo je automaticky prepojené s PHP funkciou, s bohatými možnosťami, ktoré budú predstavené v dokumentácii.

Ukážka vygenerovaného kódu:


<button  dotbridge-key="BNChfL1IaYi8T2WMxJrAa@Xb7uU21Vc" dotbridge-id="ussczZOv5np2BOtehnVm9UFxcVKBhReaBPrny2Wqux1ejgu20251206030803080308040408033615dd2cecc9d56e55c98a936fe06c3b" dotbridge-event="click" dotbridge-data="cBIsR+fiZEYt3a1Uto6F8nFJaUhnOFQrd0JBOGdid3VqNTEvUDdSSUhmdjhaOThiY0Fpcnh3Mlk4SkU9" dotbridge-data-id="IKzRYByA/zY699Z2/DX0UFYwS0ZzMnBZUytuN3lCclF6cHBxQ3YyTlBvalVLblh1NytCdDNvb2lQeTVHUmx6WS9jSXZNa2UzZk5mbVFoajk=" dotbridge-function="dotcms.newsletter" dotbridge-inputs="newsletter.email">Prihlásiť</button>
            
HTML

DotApp je ako stvorený pre vývojárov, ktorí chcú efektívny nástroj bez zbytočností. Ponúka rýchlosť, nízke nároky a jednoduchosť, ktorá uľahčuje prácu. Je to framework, ktorý dokazuje, že menej môže byť viac – a to s výsledkami, ktoré hovoria sami za seba.

Vyskúšajte DotApp a presvedčte sa sami!

Inštalácia

Aplikácia DotApp sa neinštaluje štandardným spôsobom cez composer install, pretože nejde o knižnicu, ale o kompletnú aplikáciu s vlastnou architektúrou. Namiesto toho postupujte podľa nasledujúcich krokov:

1. Nainštalujte aplikáciu pomocou Git:

git clone https://github.com/dotsystems-sk/dotapp.git ./

Tento príkaz naklonuje repozitár do aktuálneho priečinka.

Alternatívne si môžete stiahnuť ZIP súbor:

Stiahnuť dotapp-1.0.zip

Rozbaľte ho do priečinka, kam potrebujete aplikáciu umiestniť.

2. Nastavenie a použitie:

Po naklonovaní alebo rozbalení aplikácie ju nastavte podľa vašich potrieb (napr. konfigurácia databázy, prostredia atď.).

3. Používanie Composer:

Hoci samotná aplikácia nie je inštalovateľná cez Composer, po jej nainštalovaní môžete v priečinku aplikácie používať Composer na pridanie ďalších závislostí, ktoré potrebujete pre váš projekt. Stačí spustiť:

composer require <názov-balíčka>

Štruktúra adresárov

dotapp
├───app
│   ├───custom.classes
│   │   └───phpmailer
│   ├───modules
│   ├───parts
│   │   ├───controllers
│   │   ├───models
│   │   └───views
│   ├───runtime
│   │   ├───cache
│   │   ├───generator
│   │   └───routercache
│   └───vendor
│       └───composer
└───assets

Nastavenia po inštalácii

Táto sekcia popisuje minimálne nastavenia po inštalácii DotApp frameworku. Ak nepotrebujete pracovať s databázami alebo preferujete vlastné riešenie namiesto vstavanej knižnice, stačí vykonať nasledujúce kroky:

1. Definujte __ROOTDIR__ konštantu v index.php v koreňovom adresári (bez lomítka na konci)

define('__ROOTDIR__', "/var/www/html");

Uistite sa, že __ROOTDIR__ zodpovedá skutočnému umiestneniu na serveri, inak framework nemusí správne fungovať.


2. Nastavte bezpečný kľúč na šifrovanie $dotApp->encKey v súbore ./App/config.php

$dotApp->encKey("Dlhy_a_bezpecny_kluc_na_sifrovanie_udajov");

Pri nastavovaní encKey použite unikátny a dostatočne dlhý reťazec (odporúča sa aspoň 32 znakov).


3. (Voliteľné) Ak chcete používať vstavaný driver pre prácu s DB, odkomentujte a vyplňte ukážkovú sekciu v súbore ./App/config.php

Poznámka: Podmienku if (!__MAINTENANCE__) môžete pokojne odstrániť.

if (!__MAINTENANCE__) {
    /* Database setup */
    $dotApp->db->driver("mysqli");
    // Príklad použitia: $dotApp->db->add("nazov_ako_chcete_volat_databazu", "server 127.0.0.1", "meno", "heslo", "nazov_databazy_napr_db_w4778", "kodovanie spojenia UTF8");
    // Zadefinujme si parametre pre prvú databázu
    $dotApp->db->add("zdrojovaDB", "127.0.0.1", "meno", "heslo", "ws_477854", "UTF8");
    // Zadefinujme si parametre pre druhú databázu
    $dotApp->db->add("cielovaDB", "127.0.0.1", "meno2", "heslo2", "ar_994475", "UTF8");
    
    // Načítanie modulov je povinné pre správne fungovanie frameworku. Môžete ho umiestniť do bloku if alebo mimo neho podľa vašich potrieb.
    $dotApp->loadModules();
}
            

Spustenie frameworku

Teraz je všetko pripravené. Framework DotApp zabezpečí bezpečný a rýchly beh vašej aplikácie. Uistite sa, že všetky nastavenia aj vlastný kód vložíte pred volaním funkcie $dotApp->run(); (alebo jej aliasu $dotApp->davajhet();).


// Všetko pripravené? Spustite framework! - Ako vidíte v súbore index.php
$dotApp->davajhet();

/*
    Poznámka: $dotApp->davajhet() je alias pre $dotApp->run().
    Pochádzam z východného Slovenska, tak som pridal kúsok nášho "davaj het!"
*/
            

Pridanie prvej routy

Prázdna stránka a ERROR 404? Ak ste postupovali podľa návodu a po spustení vidíte prázdnu stránku so stavovým kódom 404, nebojte sa. Je to logické. Router je prázdny, a preto pre adresu / vo vašom prehliadači nenašiel routu a správne zobrazil 404. Váš framework nemá žiadne moduly, žiadne routy a preto robí to, čo má. Poďme si teda pridať prvú routu. Pred kód $dotApp->run(); / $dotApp->davajhet(); pridáme prvú routu.


// Pridanie prvej routy pre domovskú stránku
$dotApp->router->get("/", function () {
    return "Ahoj svet!";
});

// Spustenie frameworku
$dotApp->davajhet();
        

Po spustení vidíme stránku s obsahom:

Ahoj svet!

DotApp Router

Router je kľúčovou súčasťou DotApp Frameworku, ktorá spravuje routovanie HTTP požiadaviek v aplikácii. Umožňuje definovať, ako sa majú požiadavky (napr. GET, POST) mapovať na konkrétne callback funkcie, controllery alebo middleware. Router je navrhnutý tak, aby zvládal statické aj dynamické routy, podporoval hooks (before a after) a poskytoval flexibilitu pri tvorbe webových aplikácií.

1.1 Čo je Router?

Router v DotApp Frameworku je trieda Dotsystems\App\Parts\Router, ktorá spracováva prichádzajúce HTTP požiadavky a smeruje ich na príslušné handlery. Pracuje v súčinnosti s objektom Request, ktorý obsahuje informácie o požiadavke (cesta, metóda, premenné). Jeho hlavnou úlohou je zjednodušiť definovanie rout a zabezpečiť, aby sa správny kód spustil pre danú URL a HTTP metódu.

Router je integrovaný priamo do jadra frameworku, takže ho nemusíte samostatne inštalovať ani konfigurovať – stačí ho používať cez $dotApp->router.

1.2 Kľúčové vlastnosti

Router ponúka širokú škálu funkcií, ktoré uľahčujú vývoj aplikácií:

  • Podpora HTTP metód: Definovanie rout pre GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE a univerzálnu metódu ANY.
  • Dynamické routy: Použitie premenných (napr. {id}) a regulárnych výrazov na zachytenie častí URL.
  • Middleware: Podpora before a after hooks na vykonávanie logiky pred a po hlavnom handleri.
  • Request objekt: Prenáša informácie o požiadavke do callbackov a controllerov.
  • Flexibilita: Možnosť použiť anonymné funkcie, controllery alebo middleware pomocou reťazca (napr. "modul:controller@funkcia").
  • Chaining: Reťazenie metód pre prehľadnejší kód.

1.3 Základné princípy routingu

Router porovnáva aktuálnu URL (získanú z request->getPath()) a HTTP metódu (z request->getMethod()) s definovanými routami. Ak sa nájde zhoda:

  1. Spustia sa prípadné before hooks.
  2. Vykoná sa hlavná logika (callback, controller alebo middleware).
  3. Spustia sa prípadné after hooks.

Routy môžu byť:

  • Statické: Presná zhoda URL (napr. /home).
  • Dynamické: Obsahujú premenné alebo wildcardy (napr. /user/{id}).
  • Prvý match víťazí: Prvý match obsadí routu. Ostatné sa dropnú.

Router resolvuje požiadavky pomocou metódy resolve(), ktorá je zvyčajne volaná automaticky v rámci životného cyklu DotApp.

Príklad základnej routy:

$dotApp->router->get('/home', function ($request) {
    return "Vitajte na domovskej stránke!";
});
        

Po zadaní URL http://example.com/home sa zobrazí text "Vitajte na domovskej stránke!".

2. Začíname

Táto kapitola vás prevedie základmi práce s Routerom v DotApp Frameworku – od inicializácie až po definovanie prvej routy.

2.1 Inicializácia Routeru

Router je automaticky inicializovaný ako súčasť inštancie $dotApp pri vytvorení aplikácie v DotApp Frameworku. Nie je potrebné ho manuálne vytvárať ani konfigurovať – stačí pristúpiť k nemu cez $dotApp->router. Pri inicializácii sa nastavia predvolené hodnoty, ako napríklad adresár pre controllery (__ROOTDIR__/App/Parts/Controllers/) a prázdne polia pre routy a hooks.

Technické detaily:

  • Router je inštancia triedy Dotsystems\App\Parts\Router.
  • Pri konštrukcii prijíma $dotAppObj (inštanciu hlavnej triedy DotApp), ktorá mu poskytuje prístup k objektu Request a ďalším službám frameworku.

2.2 Prístup k Routeru v DotApp

K Routeru pristupujete cez globálnu inštanciu $dotApp, ktorá je dostupná kdekoľvek v aplikácii po jej bootstrape. Objekt Request je automaticky dostupný cez $dotApp->router->request a prenáša informácie o aktuálnej požiadavke (cesta, metóda, premenné).

Príklad prístupu:

// Overenie aktuálnej cesty
echo $dotApp->router->request->getPath(); // Napr. "/home"

// Overenie HTTP metódy
echo $dotApp->router->request->getMethod(); // Napr. "get"
        

2.3 Prvé definovanie routy

Najjednoduchší spôsob, ako začať s Routerom, je definovať základnú routu pomocou niektorej z HTTP metód (napr. get()). Routa môže byť spojená s anonymnou funkciou (callbackom), controllerom alebo middleware.

Príklad prvej routy s callbackom:

$dotApp->router->get('/home', function ($request) {
    return "Vitajte na domovskej stránke!";
});
        

Vysvetlenie:

  • /home: Statická URL cesta.
  • function($request): Callback, ktorý prijíma objekt Request a vracia odpoveď.
  • Po zavolaní http://example.com/home sa zobrazí text "Vitajte na domovskej stránke!".
Príklad s controllerom:

Predpokladajme, že máte controller HomeController v __ROOTDIR__/App/Parts/Controllers/HomeController.php s funkciou index:


// HomeController.php
namespace Dotsystems\App\Parts\Controllers;

use Dotsystems\App\DotApp;

class HomeController {
    public static function index($request) {
        return "Toto je domovská stránka z controlleru!";
    }
}

// Definícia routy
$dotApp->router->get('/home', 'HomeController@index');
        

Vysvetlenie:

  • 'HomeController@index': Odkaz na statickú metódu index v triede HomeController.
  • Router automaticky načíta a zavolá túto metódu s objektom Request.
Spustenie routingu:

Router spracuje požiadavky po zavolaní $dotApp->router->resolve(), ktoré je zvyčajne v hlavnom skripte aplikácie (napr. index.php). Ak je framework správne nastavený, toto volanie môže byť automatické.


// index.php
require_once 'vendor/autoload.php';
$dotApp = new Dotsystems\App\DotApp();
$dotApp->router->get('/home', function ($request) {
    return "Vitajte!";
});
$dotApp->router->resolve();
        

3. Definovanie rout

Táto kapitola popisuje, ako definovať routy v Routeri DotApp Frameworku. Router podporuje rôzne spôsoby definovania – od základných HTTP metód až po dynamické routy s premennými a prácu s controllermi.

3.1 Základné HTTP metódy (GET, POST, atď.)

Router poskytuje metódy pre všetky štandardné HTTP metódy: get(), post(), put(), delete(), patch(), options(), head() a trace(). Každá metóda definuje routu pre konkrétnu HTTP požiadavku.

Príklad GET routy:

$dotApp->router->get('/about', function ($request) {
    return "Toto je stránka O nás!";
});
        
Príklad POST routy:

$dotApp->router->post('/submit', function ($request) {
    return "Formulár bol odoslaný!";
});
        

Poznámka: Každá metóda prijíma URL cestu ako prvý parameter a callback (alebo odkaz na controller) ako druhý parameter. Callback vždy dostane objekt $request.

3.2 Metóda match() pre viaceré metódy a URL

Metóda match() umožňuje definovať routu pre viacero HTTP metód naraz alebo pre pole URL adries. Je to flexibilnejší spôsob oproti samostatným metódam ako get() či post().

Príklad s viacerými metódami:

$dotApp->router->match(['get', 'post'], '/contact', function ($request) {
    return "Toto je kontaktná stránka!";
});
        

Táto routa funguje pre GET aj POST požiadavky na /contact.

Príklad s viacerými URL:

$dotApp->router->match(['get'], ['/home', '/index'], function ($request) {
    return "Vitajte na domovskej stránke!";
});
        

Routa zachytí požiadavky na /home aj /index.

3.3 Statické vs. dynamické routy

Router rozlišuje medzi statickými a dynamickými routami:

  • Statické routy: Presná zhoda URL (napr. /home).
  • Dynamické routy: Obsahujú premenné alebo wildcardy (napr. /user/{id}).
Príklad statickej routy:

$dotApp->router->get('/profile', function ($request) {
    return "Toto je statický profil!";
});
        
Príklad dynamickej routy:

$dotApp->router->get('/user/{id}', function ($request) {
    return "Profil používateľa s ID: " . $request->matchData()['id'];
});
        

Pri URL /user/123 sa zobrazí "Profil používateľa s ID: 123".

3.4 Použitie premenných v routách

Dynamické routy môžu obsahovať premenné označené zloženými zátvorkami (napr. {id}). Tieto premenné sú automaticky extrahované a dostupné cez $request->matchData().

Základné použitie:

$dotApp->router->get('/article/{slug}', function ($request) {
    return "Článok: " . $request->matchData()['slug'];
});
        

Pre /article/how-to-cook sa zobrazí "Článok: how-to-cook".

Typované premenné:

Router podporuje aj typovanie premenných:

  • {param:s}: Reťazec (bez lomítok).
  • {param:i}: Celé číslo.
  • {param:l}: Písmená.
  • {param?}: Voliteľný parameter.
  • {param*}: Wildcard (zachytí všetko).

$dotApp->router->get('/user/{id:i}', function ($request) {
    return "ID používateľa: " . $request->matchData()['id'];
});
        

Funguje len pre čísla, napr. /user/123, ale nie /user/abc.

3.5 Práca s controllermi a middleware

Okrem anonymných funkcií môžete routy mapovať na controllery alebo middleware pomocou reťazca v tvare "modul:controller@funkcia" alebo "modul:\\middleware\\middleware1@funkcia1".

Príklad s controllerom:

// app/parts/controllers/UserController.php
namespace Dotsystems\App\Parts\Controllers;

use Dotsystems\App\DotApp;

class UserController {
    public static function show($request) {
        return "Zobrazenie používateľa!";
    }
}

// Definícia routy
$dotApp->router->get('/user', 'UserController@show');
        
Príklad s middleware:

// app/parts/middleware/AuthMiddleware.php
namespace Dotsystems\App\Parts\Middleware;

use Dotsystems\App\DotApp;

class AuthMiddleware {
    public static function check($request) {
        return "Overenie autentifikácie!";
    }
}

// Definícia routy
$dotApp->router->get('/secure', 'parts:\\Middleware\\AuthMiddleware@check');
        

Poznámka: Funkcie musia byť definované ako public static a prijímať $request ako parameter.

4. Práca s Request objektom

Objekt Request je neoddeliteľnou súčasťou Routera v DotApp Frameworku. Prenáša informácie o aktuálnej HTTP požiadavke a je automaticky odovzdaný do callbackov, controllerov a middleware. Táto kapitola vysvetlí, ako funguje a ako ho efektívne používať.

4.1 Čo je Request?

Request je inštancia triedy Dotsystems\App\Parts\Request, ktorá slúži ako rozhranie pre prácu s dátami požiadavky. Obsahuje informácie o ceste, HTTP metóde, premenných z dynamických rout a ďalších atribútoch požiadavky. Je automaticky vytvorený pri inicializácii Routera a dostupný cez $dotApp->router->request.

Kľúčové vlastnosti:

  • Získavanie aktuálnej URL cesty a metódy.
  • Prístup k premenným z dynamických rout cez matchData().
  • Prenos dát do callbackov a hooks.

4.2 Prístup k dátam z Requestu

Objekt Request poskytuje metódy na získanie základných informácií o požiadavke:

  • getPath(): Vráti aktuálnu URL cestu (napr. /home).
  • getMethod(): Vráti HTTP metódu (napr. get, post).
  • matchData(): Vráti pole premenných extrahovaných z dynamickej routy.
  • hookData(): Vráti dáta priradené hooks (používa sa pri samostatných before/after).
Príklad prístupu:

$dotApp->router->get('/user/{id}', function ($request) {
    $path = $request->getPath();        // "/user/123"
    $method = $request->getMethod();    // "get"
    $id = $request->matchData()['id'];  // "123"
    return "Cesta: $path, Metóda: $method, ID: $id";
});
        

Pri požiadavke na /user/123 sa zobrazí: "Cesta: /user/123, Metóda: get, ID: 123".

4.3 Využitie Requestu v callbackoch

Objekt Request je automaticky odovzdaný ako parameter do všetkých callbackov, controllerov a middleware definovaných v routách. Umožňuje vám pracovať s dátami požiadavky priamo v logike routy.

Príklad s anonymnou funkciou:

$dotApp->router->get('/profile/{name}', function ($request) {
    $name = $request->matchData()['name'];
    return "Ahoj, $name!";
});
        

Pri /profile/Jano sa zobrazí: "Ahoj, Jano!".

Príklad s controllerom:

// app/parts/controllers/ProfileController.php
namespace Dotsystems\App\Parts\Controllers;

use Dotsystems\App\DotApp;

class ProfileController {
    public static function show($request) {
        $name = $request->matchData()['name'];
        return "Profil pre: $name";
    }
}

// Definícia routy
$dotApp->router->get('/profile/{name}', 'ProfileController@show');
        

Výsledok je rovnaký ako pri anonymnej funkcii.

Príklad s middleware:

// app/parts/middleware/CheckMiddleware.php
namespace Dotsystems\App\Parts\Middleware;

use Dotsystems\App\DotApp;

class CheckMiddleware {
    public static function verify($request) {
        $path = $request->getPath();
        return "Overená cesta: $path";
    }
}

// Definícia routy
$dotApp->router->get('/check', 'parts:\\Middleware\\CheckMiddleware@verify');
        

Pri /check sa zobrazí: "Overená cesta: /check".

Poznámka: matchData() vracia prázdne pole, ak routa neobsahuje dynamické premenné. Pred použitím si overte existenciu kľúča, napr. isset($request->matchData()['id']), aby ste predišli chybám.

5. Middleware (Before a After Hooks)

Middleware v Routeri DotApp Frameworku umožňuje vykonávať dodatočnú logiku pred alebo po hlavnom handleri routy. Tieto "hooks" sú definované pomocou metód before() a after() a sú ideálne na úlohy ako autentifikácia, logovanie alebo úprava odpovedí.

5.1 Čo sú hooks?

Hooks sú funkcie, ktoré sa spúšťajú automaticky v určitých fázach spracovania routy:

  • before: Spustí sa pred hlavnou logikou routy (napr. callbackom alebo controllerom).
  • after: Spustí sa po hlavnej logike, s prístupom k výsledku routy.

Hooks prijímajú objekt $request ako parameter a môžu byť definované globálne, pre konkrétnu routu alebo metódu s routou.

5.2 Definovanie before()

Metóda before() sa používa na pridanie logiky, ktorá sa vykoná pred hlavným handlerom. Môže byť použitá tromi spôsobmi:

  • Globálne: Pre všetky routy.
  • Pre konkrétnu routu: Len pre zadanú cestu.
  • Pre metódu a routu: Špecificky pre HTTP metódu a cestu.
Globálne before:

$dotApp->router->before(function ($request) {
    return "Pred každou routou!";
});
$dotApp->router->get('/test', function ($request) {
    return "Testovacia stránka";
});
        

Hook sa spustí pre všetky routy, napr. pri /test sa najskôr vykoná "Pred každou routou!".

Before pre konkrétnu routu:

$dotApp->router->get('/secure', function ($request) {
    return "Zabezpečená stránka";
})->before(function ($request) {
    return "Overujem prístup...";
});
        

Hook sa spustí len pre /secure.

Before s metódou a routou:

$dotApp->router->before('get', '/login', function ($request) {
    return "Kontrolujem prihlásenie pre GET";
});
$dotApp->router->get('/login', function ($request) {
    return "Prihlasovacia stránka";
});
        

5.3 Definovanie after()

Metóda after() sa spúšťa po hlavnom handleri a má rovnaké možnosti definovania ako before(). Je užitočná na úpravu výsledkov alebo logovanie.

Globálne after:

$dotApp->router->after(function ($request) {
    return "Po každej route!";
});
$dotApp->router->get('/test', function ($request) {
    return "Testovacia stránka";
});
        

Hook sa spustí po každej route, napr. pri /test sa najskôr vykoná "Testovacia stránka" a potom "Po každej route!".

After pre konkrétnu routu:

$dotApp->router->get('/profile', function ($request) {
    return "Profilová stránka";
})->after(function ($request) {
    return "Profil bol zobrazený";
});
        
After s metódou a routou:

$dotApp->router->after('post', '/submit', function ($request) {
    return "Formulár bol spracovaný";
});
$dotApp->router->post('/submit', function ($request) {
    return "Odoslanie úspešné";
});
        

5.4 Použitie s viacerými routami

Hooks môžete priradiť viacerým routám naraz pomocou poľa ciest v metóde match() alebo samostatne volaním before()/after().

Príklad s match:

$dotApp->router->match(['get'], ['/home', '/index'], function ($request) {
    return "Domovská stránka";
})->before(function ($request) {
    return "Pred domovskou stránkou";
})->after(function ($request) {
    return "Po domovskej stránke";
});
    

Hooky sa aplikujú na obe cesty: /home aj /index.

Príklad s poľom ciest:

$dotApp->router->before('get', ['/page1', '/page2'], function ($request) {
    return "Pred stránkami";
});
$dotApp->router->get('/page1', function ($request) {
    return "Stránka 1";
});
$dotApp->router->get('/page2', function ($request) {
    return "Stránka 2";
});
    

Poznámka: Výstup z hooks sa pridáva k odpovedi routy. Ak chcete meniť odpoveď, pracujte s $request->response->body priamo v hooku (viac v pokročilých funkciách).

6. Správa chýb a výnimiek

Router v DotApp Frameworku umožňuje vývojárom spravovať chyby a výnimky, ktoré vznikajú pri spracovaní požiadaviek. Táto kapitola popisuje, ako riešiť štandardné chyby ako 404 a implementovať vlastnú logiku spracovania chýb pomocou callbackov a hooks.

6.1 Riešenie 404 chýb

Ak Router nenájde zhodu pre požiadavku (ani statickú, ani dynamickú routu), automaticky nastaví HTTP kód 404. Predvolené správanie nezobrazí žiadny výstup, takže je na vývojárovi, aby definoval vlastnú logiku na zachytenie a zobrazenie chyby.

Príklad s globálnym after hookom:

$dotApp->router->after("*", function ($request) {
    if (http_response_code() === 404) {
        return "Stránka nenájdená: " . $request->getPath();
    }
});

$dotApp->router->get('/home', function ($request) {
    return "Domovská stránka";
});
    

Pri požiadavke na /about (neexistujúca routa) sa zobrazí: "Stránka nenájdená: /about". Hook s "*" sa spustí pre všetky routy a overí stavový kód.

Príklad s ukončením skriptu:

$dotApp->router->after("*", function ($request) {
    if (http_response_code() === 404) {
        echo "404 - Nenájdená stránka!";
        exit;
    }
});

$dotApp->router->get('/home', function ($request) {
    return "Domovská stránka";
});
    

Pri /about sa zobrazí "404 - Nenájdená stránka!" a skript sa ukončí.

6.2 Vlastné spracovanie chýb

Vývojári môžu implementovať vlastnú logiku spracovania chýb priamo v callbackoch alebo middleware pomocou podmienok a HTTP kódov.

Príklad s podmienkou v callbacku:

$dotApp->router->get('/user/{id:i}', function ($request) {
    $id = $request->matchData()['id'];
    if ($id > 100) {
        http_response_code(403);
        return "Zakázaný prístup pre ID väčšie ako 100!";
    }
    return "Profil používateľa: $id";
});
    

Pri /user/150 sa zobrazí "Zakázaný prístup pre ID väčšie ako 100!" s kódom 403.

Príklad s middleware:

$dotApp->router->get('/user/{id:i}', function ($request) {
    $id = $request->matchData()['id'];
    return "Profil používateľa: $id";
})->before(function ($request) {
    $id = $request->matchData()['id'];
    if (!isset($id)) {
        http_response_code(400);
        return "Chýba ID!";
    }
});
    

Pri /user/ sa zobrazí "Chýba ID!" s kódom 400.

Poznámka: Použitie http_response_code() v callbackoch alebo hooks umožňuje nastaviť vlastné chybové stavy. Je na vývojárovi, či skript ukončí pomocou exit alebo vráti chybovú správu.

7. Pokročilé funkcie

Router v DotApp Frameworku ponúka pokročilé funkcie, ktoré rozširujú jeho možnosti. Táto kapitola popisuje reťazenie metód, dynamické porovnávanie URL, detailné vysvetlenie tvorby dynamických adries a definovanie API endpointov.

7.1 Reťazenie metód (Chaining)

Router podporuje reťazenie metód, čo umožňuje definovať routy, hooks a ďalšie nastavenia v jednom príkaze. To zlepšuje čitateľnosť a organizáciu kódu.

Príklad reťazenia:

$dotApp->router->get('/profile/{id}', function ($request) {
    $id = $request->matchData()['id'];
    return "Profil ID: $id";
})->before(function ($request) {
    return "Kontrolujem pred zobrazením profilu";
})->after(function ($request) {
    return "Profil zobrazený";
});
    

Pri /profile/123 sa postupne spustí before, hlavná logika a after.

7.2 Dynamické porovnávanie rout (matchUrl())

Metóda matchUrl() slúži na manuálne porovnanie URL s routovacím vzorom. Vráti pole extrahovaných premenných, ak sa vzor zhoduje, alebo false, ak nie. Je užitočná pre vlastné validácie alebo testovanie rout.

Príklad použitia:

$dotApp->router->get('/test', function ($request) {
    $pattern = '/user/{id:i}';
    $url = '/user/123';
    $match = $dotApp->router->matchUrl($pattern, $url);
    if ($match !== false) {
        return "Zhoda! ID: " . $match['id'];
    }
    return "Nezhoduje sa";
});
    

Pri /test sa zobrazí "Zhoda! ID: 123".

7.3 Dynamické adresy a vzory

Dynamické adresy v Routeri umožňujú definovať routy s premennými a voliteľnými časťami pomocou špeciálnej syntaxe. Tieto vzory sú rozpoznávané všade rovnako (napr. v get(), post(), match()) a premenné sú dostupné cez $request->matchData(). Nasleduje podrobné vysvetlenie na príklade a zoznam najčastejších vzorov.

Príklad dynamickej adresy:

$dotApp->router->get('/documentation/intro(?:/{language})?', function ($request) {
    $language = $request->matchData()['language'] ?? 'default';
    return "Úvodná dokumentácia, jazyk: $language";
});
    

Vysvetlenie:

  • /documentation/intro(?:/{language})?: Definuje routu, kde {language} je voliteľná časť (označená ?: a ?).
  • /documentation/intro: Platná (jazyk je "default").
  • /documentation/intro/eng: Platná (jazyk je "eng").
  • /documentation/intro/: Neplatná (Router očakáva hodnotu za lomítkom, ak je prítomné).

Premenná language sa extrahuje do $request->matchData(), ak je zadaná, inak je null.

Najčastejšie používané vzory:

Tu je zoznam 10 bežných vzorov dynamických adries, ktoré sa používajú vo webových aplikáciách, s príkladmi a vysvetlením:

  1. /{resource}/{id:i} - Základná CRUD routa
    
    $dotApp->router->get('/users/{id:i}', function ($request) {
        return "Používateľ ID: " . $request->matchData()['id'];
    });
                

    Platné: /users/123, Neplatné: /users/abc

  2. /{category}/{slug:s} - Kategória a slug článku
    
    $dotApp->router->get('/blog/{category}/{slug:s}', function ($request) {
        return "Kategória: " . $request->matchData()['category'] . ", Slug: " . $request->matchData()['slug'];
    });
                

    Platné: /blog/tech/how-to-code

  3. /api/v{version}/{endpoint} - Verzované API
    
    $dotApp->router->get('/api/v{version}/{endpoint}', function ($request) {
        return "API v" . $request->matchData()['version'] . ": " . $request->matchData()['endpoint'];
    });
                

    Platné: /api/v1/users

  4. /{page}(?:/{subpage})? - Voliteľná podstránka
    
    $dotApp->router->get('/docs/{page}(?:/{subpage})?', function ($request) {
        $subpage = $request->matchData()['subpage'] ?? 'hlavná';
        return "Stránka: " . $request->matchData()['page'] . ", Podstránka: $subpage";
    });
                

    Platné: /docs/intro, /docs/intro/setup

  5. /{type}/{id:i}/{action} - Akcia na zdroji
    
    $dotApp->router->get('/posts/{id:i}/{action}', function ($request) {
        return "ID: " . $request->matchData()['id'] . ", Akcia: " . $request->matchData()['action'];
    });
                

    Platné: /posts/5/edit

  6. /{resource}/{filter:s}? - Voliteľný filter
    
    $dotApp->router->get('/products/{filter:s}?', function ($request) {
        $filter = $request->matchData()['filter'] ?? 'všetky';
        return "Produkty, filter: $filter";
    });
                

    Platné: /products, /products/new

  7. /{path*} - Wildcard pre celú cestu
    
    $dotApp->router->get('/files/{path*}', function ($request) {
        return "Cesta k súboru: " . $request->matchData()['path'];
    });
                

    Platné: /files/images/photo.jpg

  8. /{lang:l}/{section} - Jazyk a sekcia
    
    $dotApp->router->get('/{lang:l}/{section}', function ($request) {
        return "Jazyk: " . $request->matchData()['lang'] . ", Sekcia: " . $request->matchData()['section'];
    });
                

    Platné: /en/news, Neplatné: /123/news

  9. /search(?:/{query})? - Voliteľný vyhľadávací reťazec
    
    $dotApp->router->get('/search(?:/{query})?', function ($request) {
        $query = $request->matchData()['query'] ?? 'prázdne';
        return "Vyhľadávanie: $query";
    });
                

    Platné: /search, /search/php

  10. /{resource}/{id:i}(?:/{extra})? - Zdroj s voliteľným parametrom
    
    $dotApp->router->get('/users/{id:i}(?:/{extra})?', function ($request) {
        $extra = $request->matchData()['extra'] ?? 'žiadne';
        return "ID: " . $request->matchData()['id'] . ", Extra: $extra";
    });
                

    Platné: /users/10, /users/10/details

Poznámka: Tieto vzory sú flexibilné a kombinovateľné. Používajte {?:} pre voliteľné časti a typy (:i, :s, :l) pre presné obmedzenia.

7.4 Definovanie API endpointov pomocou apiPoint

Metóda apiPoint v Routeri poskytuje pohodlný spôsob, ako definovať API endpointy s podporou verzovania, modulov a dynamických parametrov. Umožňuje flexibilitu pri definovaní vlastných ciest a metód, pričom v kombinácii s vstavanou abstraktnou triedou Controller a jej metódami apiDispatch (hlavná logika) a api (kratší alias) ponúka automatické rozbočovanie požiadaviek na špecifické metódy v controlleroch s podporou dependency injection (DI).

Definícia metódy apiPoint:

public function apiPoint($verzia, $modul, $controller, $custom = null) {
    $apiRouty = array();
    
    if ($custom !== null) {
        if (is_array($custom)) {
            foreach ($custom as $value) {
                $apiRouty[] = "/api/v".$verzia."/".$modul."/".$value;
            }
        } else {
            $apiRouty[] = "/api/v".$verzia."/".$modul."/".$custom;
        }
    } else {
        $apiRouty[] = "/api/v".$verzia."/".$modul."/{resource}(?:/{id})?";
    }

    $this->any($apiRouty, $controller);
}
    

Parametre:

  • $verzia: Verzia API (napr. "1" pre v1).
  • $modul: Názov modulu (napr. "dotcmsfe").
  • $controller: Callback alebo reťazec vo formáte "Controller@metoda" (napr. "PostsController@apiDispatch", "PostsController@api", alebo vlastná metóda).
  • $custom (voliteľné): Špecifická cesta (reťazec) alebo pole ciest. Podporuje regulárne výrazy (napr. (?:/{id})?).

Ak $custom nie je zadaný, použije sa predvolená dynamická cesta /api/v{verzia}/{modul}/{resource}(?:/{id})?. Ak je zadaný, použijú sa iba cesty z $custom. Prvá routa vyhráva! Statické cesty musia byť uvedené pred dynamickými, aby sa predišlo prekrytiu dynamickou logikou.

Vstavaný Controller a metódy apiDispatch/api:

Framework poskytuje abstraktnú triedu Dotsystems\App\Parts\Controller s metódou apiDispatch, ktorá automaticky rozbočuje požiadavky na špecifické metódy v tvare (napr. postUsers, getPosts) na základe HTTP metódy a hodnoty dynamického parametra resource. Stačí nasmerovať apiPoint na Controller@apiDispatch (alebo Controller@api ako kratší alias), a automatické rozbočovanie funguje, ak cesta obsahuje {resource}. Na rozdiel od iných frameworkov (napr. Laravel, Django) nepotrebujete definovať routy pre každý endpoint – apiDispatch to urobí za vás s plnou podporou DI cez self::$di->callStatic.


// Výňatok z Dotsystems\App\Parts\Controller
public static function apiDispatch($request) {
    $method = strtolower($request->getMethod());
    $resource = $request->matchData()['resource'] ?? null;
    $id = $request->matchData()['id'] ?? null;
    
    // Zostavíme názov metódy: 
    if ($resource) {
        $targetMethod = $method . ucfirst($resource);
        if (method_exists(static::class, $targetMethod)) {
            return self::$di->callStatic($targetMethod, [$request]);
        }
    }

    // Skúsime volať error404, ak existuje
    if (method_exists(static::class, 'error404')) {
        return self::$di->callStatic('error404', [$request]);
    }

    // Predvolená odpoveď, ak error404 neexistuje
    http_response_code(404);
    return "API: Resource '$resource' not found or method '$method' not supported in " . static::class;
}

public static function api($request) {
    // Alias kratší
    self::apiDispatch($request);
}
    
Výhody použitia Controller@apiDispatch s DI:

Metóda apiDispatch využíva DI kontajner na volanie špecifických metód, čím prináša automatickú injekciu závislostí. To znamená, že metódy v controlleroch môžu prijímať ďalšie parametre (napr. služby ako \SomeService), ktoré sú automaticky injektované z DI kontajnera bez potreby manuálneho vytvárania inštancií. Tento prístup zjednodušuje kód, zvyšuje flexibilitu a odlišuje DotApp od iných frameworkov tým, že eliminuje potrebu explicitného routovania pre každý endpoint pri použití automatiky.

Prispôsobenie chýb:

Ak cieľová metóda (napr. postUsers) neexistuje, apiDispatch najprv skontroluje, či controller definuje metódu error404. Ak áno, zavolá ju a umožní používateľovi definovať vlastnú logiku pre 404 chyby (napr. JSON odpoveď, logovanie). Ak error404 neexistuje, vráti predvolenú chybovú hlášku s HTTP kódom 404.

Použitie s automatickým rozbočovaním:

Automatické rozbočovanie cez apiDispatch (alebo api) funguje iba vtedy, ak cesta obsahuje dynamický parameter {resource} na správnom mieste (napr. /api/v1/dotcmsfe/{resource}). Ak $custom nezachová tento formát, automatika nebude fungovať a je potrebné použiť vlastnú metódu.

Príklad bez $custom (automatické rozbočovanie):

$dotApp->router->apiPoint("1", "dotcmsfe", "PostsController@apiDispatch");
    

Výsledné cesty:

  • POST /api/v1/dotcmsfe/users - Spustí postUsers, ak existuje.
  • GET /api/v1/dotcmsfe/posts - Spustí getPosts.
  • GET /api/v1/dotcmsfe/status - Spustí error404, ak existuje, inak 404 s predvolenou hláškou.
  • /api/v1/dotcmsfe/posts/ - Nezachytí sa.
Príklad s $custom a automatickým rozbočovaním:

$dotApp->router->apiPoint("1", "dotcmsfe", "PostsController@apiDispatch", ["{resource}(?:/{id})?/details"]);
    

Výsledné cesty:

  • POST /api/v1/dotcmsfe/users/details - Spustí postUsers.
  • GET /api/v1/dotcmsfe/posts/details - Spustí getPosts.
  • GET /api/v1/dotcmsfe/posts/abc123/details - Spustí getPosts.
  • PUT /api/v1/dotcmsfe/status/details - Spustí error404, ak existuje, inak 404 s predvolenou hláškou.
  • /api/v1/dotcmsfe/users/ - Nezachytí sa.
Príklad s vlastnými routami a vlastnou metódou:

$dotApp->router->apiPoint("1", "dotcmsfe", "PostsController@customMethod", ["users/details", "posts/summary"]);
    

Výsledné cesty: Tu automatické rozbočovanie nefunguje, pretože chýba {resource}. Logika závisí od implementácie customMethod.

  • GET /api/v1/dotcmsfe/users/details - Spustí customMethod.
  • POST /api/v1/dotcmsfe/posts/summary - Spustí customMethod.
Príklad controlleru s DI a vlastnou chybou:

namespace Dotsystems\App\Modules\Dotcmsfe\Controllers;

class PostsController extends \Dotsystems\App\Parts\Controller {
    public static function postUsers($request, \SomeService $service) {
        return "Vytvorenie používateľov: " . $service->process($request->getPath());
    }

    public static function getPosts($request) {
        $id = $request->matchData()['id'] ?? null;
        return "Zoznam príspevkov" . ($id ? " s ID: $id" : "");
    }

    public static function error404($request) {
        http_response_code(404);
        return json_encode([
            'error' => 'Not Found',
            'message' => "Resource '{$request->matchData()['resource']}' not found or method '{$request->getMethod()}' not supported",
            'path' => $request->getPath()
        ]);
    }

    public static function customMethod($request) {
        return "Vlastná metóda pre cestu: " . $request->getPath();
    }
}
    

Poznámka: Vstavaný Controller zjednodušuje prácu s API cez apiDispatch (alebo api), ak cesta obsahuje {resource}. Pre vlastné routy bez {resource} môžete použiť vlastné metódy, ale automatické rozbočovanie nebude fungovať. Poradie ciest v $custom je kritické – statické cesty musia byť pred dynamickými.

8. Praktické príklady

Táto kapitola prináša praktické príklady použitia Routera v DotApp Frameworku. Ukazuje, ako kombinovať základné a pokročilé funkcie na riešenie bežných scenárov vo webových aplikáciách.

8.1 Jednoduchá GET routa

Najzákladnejší príklad definovania statickej routy s jednoduchou odpoveďou.

Príklad:

$dotApp->router->get('/welcome', function ($request) {
    return "Vitajte v aplikácii!";
});
    

Pri požiadavke na /welcome sa zobrazí: "Vitajte v aplikácii!".

Použitie: Ideálne pre statické stránky, ako sú úvodné stránky alebo "O nás".

8.2 Dynamická routa s premennými

Príklad dynamickej routy s extrakciou premenných na zobrazenie údajov o používateľovi.

Príklad:

$dotApp->router->get('/user/{id:i}/{name}', function ($request) {
    $id = $request->matchData()['id'];
    $name = $request->matchData()['name'];
    return "Používateľ ID: $id, Meno: $name";
});
    

Pri /user/123/Jano sa zobrazí: "Používateľ ID: 123, Meno: Jano".

Použitie: Vhodné pre profily, detaily produktov alebo iné zdroje s identifikátormi.

8.3 Použitie middleware

Príklad kombinácie routy s before a after hooks na overenie a logovanie.

Príklad:

$dotApp->router->get('/dashboard', function ($request) {
    return "Vitajte v dashboarde!";
})->before(function ($request) {
    $user = "guest"; // Simulácia overenia
    if ($user === "guest") {
        http_response_code(403);
        return "Prístup zamietnutý!";
    }
})->after(function ($request) {
    return "Dashboard zobrazený o " . date('H:i:s');
});
    

Pri /dashboard sa zobrazí "Prístup zamietnutý!" s kódom 403 (keďže simulácia overenia zlyhá). Ak by bolo overenie úspešné, zobrazilo by sa "Vitajte v dashboarde!" a následne čas zobrazenia.

Použitie: Autentifikácia, logovanie prístupu alebo úprava odpovedí.

8.4 Kombinácia s controllermi

Príklad integrácie routy s controllerom na oddelenie logiky od routingu.

Príklad:

// app/parts/controllers/ArticleController.php
namespace Dotsystems\App\Parts\Controllers;

use Dotsystems\App\DotApp;

class ArticleController {
    public static function detail($request) {
        $slug = $request->matchData()['slug'];
        return "Detail článku: $slug";
    }
}

// Definícia routy
$dotApp->router->get('/article/{slug:s}', 'ArticleController@detail');
    

Pri /article/how-to-code sa zobrazí: "Detail článku: how-to-code".

Použitie: Väčšie aplikácie, kde je potrebná organizácia kódu do controllerov.

9. Tipy a triky

Táto kapitola ponúka praktické tipy a triky na efektívne využitie Routera v DotApp Frameworku. Pomôžu vám optimalizovať kód, ladiť problémy a dodržiavať osvedčené postupy.

9.1 Optimalizácia routingu

Router v DotApp Frameworku funguje na princípe "prvý match víťazí" – prvá zhoda v poradí definovania routy sa použije a ostatné sa ignorujú, bez ohľadu na to, či je routa statická alebo dynamická. Poradie definovania je preto kľúčové pre optimalizáciu.

  • Definujte najdôležitejšie routy najskôr: Keďže prvý match víťazí, umiestnite kritické alebo častejšie používané routy na začiatok.
  • Používajte špecifické vzory: Napr. {id:i} namiesto {id}, aby ste zabránili nechceným zhôdám na nesprávnych routách.
  • Zoskupte podobné routy: Použite match() s poľom ciest na zníženie duplicity kódu, ale dávajte pozor na poradie.
Príklad optimalizácie:

$dotApp->router->get('/user/{id:i}', function ($request) { // Prvá dynamická routa
    return "Dynamický používateľ ID: " . $request->matchData()['id'];
});
$dotApp->router->get('/user/123', function ($request) { // Statická routa druhá
    return "Statický používateľ 123";
});
    

Pri /user/123 sa vždy použije prvá routa ("Dynamický používateľ ID: 123"), pretože bola definovaná ako prvá, aj keď druhá je statická a presnejšia. Ak chcete prioritu pre statickú routu, definujte ju skôr.

Príklad s prepísaním poradia:

$dotApp->router->get('/user/123', function ($request) { // Statická routa prvá
    return "Statický používateľ 123";
});
$dotApp->router->get('/user/{id:i}', function ($request) { // Dynamická routa druhá
    return "Dynamický používateľ ID: " . $request->matchData()['id'];
});
    

Teraz pri /user/123 vyhrá "Statický používateľ 123", lebo je definovaná ako prvá.

9.2 Ladenie rout

Pri ladení problémov s routami použite dostupné nástroje Routera a PHP na identifikáciu, ktorá routa sa skutočne spúšťa, najmä pri "prvom matchi".

  • Skontrolujte cestu: Použite $request->getPath() na overenie, akú URL Router spracováva.
  • Výpis premenných: Vypíšte $request->matchData(), aby ste videli, aké hodnoty boli extrahované.
  • Testujte poradie: Pridajte dočasné výpisy (napr. echo) do callbackov, aby ste zistili, ktorá routa sa spustila.
Príklad ladenia:

$dotApp->router->get('/page/{id}', function ($request) {
    echo "Spustila sa dynamická routa pre ID: " . $request->matchData()['id'];
    return "Dynamická stránka " . $request->matchData()['id'];
});
$dotApp->router->get('/page/1', function ($request) {
    echo "Spustila sa statická routa pre /page/1";
    return "Statická stránka 1";
});
    

Pri /page/1 sa zobrazí "Spustila sa dynamická routa pre ID: 1" a "Dynamická stránka 1", pretože dynamická routa je definovaná ako prvá. Zmena poradia by uprednostnila statickú routu.

9.3 Best practices pre štruktúru rout

Dodržiavanie osvedčených postupov pomáha udržať prehľadnosť a predvídateľnosť routingu.

  • Logické poradie: Definujte routy od najšpecifickejších po najvšeobecnejšie, aby ste využili pravidlo "prvý match víťazí".
  • Komentáre: Pridávajte komentáre nad routy, aby bolo jasné, prečo sú v danom poradí.
  • Oddelenie logiky: Používajte controllery pre komplexné routy namiesto inline callbackov.
Príklad best practices:

// Najšpecifickejšia statická routa
$dotApp->router->get('/api/users/guest', function ($request) {
    return "Hosťovský používateľ";
});

// Špecifická dynamická routa
$dotApp->router->get('/api/users/{id:i}', function ($request) {
    return "Používateľ ID: " . $request->matchData()['id'];
});

// Všeobecná routa na konci
$dotApp->router->get('/api/{resource}', function ($request) {
    return "Zdroj: " . $request->matchData()['resource'];
});
    

Pri /api/users/guest sa spustí prvá routa, pri /api/users/5 druhá a pri /api/products tretia, vďaka logickému poradiu.

10. Záver

Táto kapitola uzatvára dokumentáciu Routera v DotApp Frameworku. Zhŕňa jeho výhody a ponúka pohľad na jeho budúci vývoj a komunitu.

10.1 Prečo používať Router v DotApp?

Router v DotApp Frameworku je jednoduchý, no výkonný nástroj na správu routingu vo webových aplikáciách. Jeho hlavné výhody zahŕňajú:

  • Flexibilita: Podpora statických aj dynamických rout s premennými a voliteľnými časťami.
  • Jednoduchosť: Intuitívne rozhranie na definovanie rout cez HTTP metódy ako get() a post().
  • Middleware: Možnosť pridania before a after hooks pre rozšírenú logiku.
  • Prvý match víťazí: Predvídateľné správanie založené na poradí definovania rout, čo dáva vývojárom plnú kontrolu.
  • Integrácia: Bezproblémová spolupráca s controllermi a objektom Request na spracovanie požiadaviek.

Či už tvoríte malú aplikáciu alebo komplexný systém, Router vám poskytne nástroje na rýchle a efektívne mapovanie požiadaviek na logiku.

Dependency Injection a Middleware v DotApp

Táto kapitola popisuje dependency injection (DI) a middleware v DotApp frameworku. Zameriava sa na flexibilné volania kontrolerov a middleware cez stringy, polia, callable objekty a anonymné funkcie, s automatickým vkladaním závislostí cez stringToCallable a vylepšenú metódu di.

1. Dependency Injection a Middleware v DotApp

Táto kapitola popisuje dependency injection (DI) a middleware v DotApp frameworku. Vysvetľuje DI kontajner, jeho registráciu závislostí cez bind a singleton, a ako sú tieto závislosti resolvované pomocou resolve. Tiež popisuje flexibilné volania kontrolerov a middleware cez stringy, polia, callable objekty a anonymné funkcie s automatickým DI.

1.1. Čo je Dependency Injection a Middleware?

Dependency Injection (DI) je technika, ktorá umožňuje automaticky vkladať závislosti (napr. DotApp) do metód a funkcií namiesto ich manuálneho vytvárania. V DotApp je DI spravované cez DI kontajner v triede DotApp, ktorý registruje závislosti a resolvuje ich pri volaniach.

Middleware sú funkcie alebo triedy, ktoré spracúvajú požiadavky pred alebo po hlavnej logike, využívajúc DI pre prístup k registrovaným závislostiam.

1.2. DI Kontajner v DotApp

DI kontajner v DotApp je jadrom správy závislostí a pozostáva z troch hlavných metód:

1.2.1. bind(string $key, callable $resolver)

Registruje závislosť, ktorá sa vytvorí nanovo pri každom volaní resolve. Používa sa pre nezdieľané (non-shared) inštancie.

Syntax: bind(string $key, callable $resolver): void

Príklad:


$DotApp->bind('logger', function () {
    return new Logger();
});
$logger = $DotApp->resolve('logger'); // Vytvorí novú inštanciu
$logger2 = $DotApp->resolve('logger'); // Vytvorí ďalšiu novú inštanciu
        

Poznámka: Každé volanie resolve vráti novú inštanciu, pretože shared je nastavené na false.

1.2.2. singleton(string $key, callable $resolver)

Registruje závislosť ako singleton – vytvorí sa iba raz a následne sa zdieľa pri všetkých volaniach resolve. Používa sa pre zdieľané inštancie, ako je napríklad samotný DotApp.

Syntax: singleton(string $key, callable $resolver): void

Príklad:


// V konštruktore DotApp
$this->singleton(DotApp::class, function () {
    return $this;
});
$dotApp1 = $DotApp->resolve(DotApp::class); // Vráti tú istú inštanciu
$dotApp2 = $DotApp->resolve(DotApp::class); // Vráti tú istú inštanciu
        

Poznámka: Singleton zaručuje, že existuje iba jedna inštancia danej závislosti, uložená v $this->instances.

1.2.3. resolve(string $key)

Resolvuje zaregistrovanú závislosť. Ak je to singleton, vráti zdieľanú inštanciu; ak je to bind, vytvorí novú.

Syntax: resolve(string $key)

Príklad:


$DotApp->singleton('db', function () {
    return new Database();
});
$db = $DotApp->resolve('db'); // Vráti singleton inštanciu
        

Výnimka: Ak kľúč nie je registrovaný, vyhodí Exception.

1.2.4. Ako funguje DI v praxi

DI kontajner ukladá závislosti v poli $this->bindings s informáciou, či sú zdieľané (shared). Pri resolúcii kontroluje, či už existuje inštancia (pre singletony) alebo volá resolver (pre bind). V konštruktore DotApp je napríklad DotApp::class zaregistrovaný ako singleton, aby bol prístupný všade:


$this->singleton(DotApp::class, function () { return $this; });
        

1.3. Možnosti volania kontrolerov a middleware

DotApp využíva DI kontajner na automatické vkladanie závislostí do callbackov:

1.3.1. String-based volania

Syntax: "modul:controller@funkcia".

Príklad:


$DotApp->router->get('/login', "auth:controllers\\UserController@login");
        

Výstup pri GET /login:


Prihlásenie bolo úspešné: GET
        
1.3.2. Pole ako callback

Syntax: ['modul', 'class', 'function'].

Príklad:


$DotApp->router->get('/login', ['auth', 'controllers\\UserController', 'login']);
        

Výstup pri GET /login:


Prihlásenie bolo úspešné: GET
        
1.3.3. Callable (Closure) s DI

Anonymné funkcie využívajú DI kontajner na resolúciu závislostí.

Príklad:


$DotApp->router->get('/status', function (DotApp $dotApp) {
    echo "Aktuálna metóda: " . $dotApp->router->request->getMethod();
});
        

Výstup pri GET /status:


Aktuálna metóda: GET
        
1.3.4. Dynamické volania cez di

Príklad:


class AuthService extends Dotsystems\App\Parts\Library {
    public function logAction(DotApp $dotApp) {
        echo "Akcia: " . $dotApp->router->request->getMethod();
    }
}
$service = new AuthService('auth', $DotApp);
$service->di->logAction();
        

Výstup:


Akcia: GET
        
1.3.5. Statické volania cez controller2::call

Príklad:


auth\controllers\UserController::call("login");
        

Výstup:


Prihlásenie bolo úspešné: GET
        

1.4. Práca s Middleware

Middleware využíva DI kontajner pre prístup k závislostiam:

1.4.1. String-based middleware

Príklad:


$DotApp->router->before(["/user/*"], "auth:controllers\\UserController@checkAuth");
$DotApp->router->get('/user/profile', "auth:controllers\\UserController@showProfile");
        

Výstup pri GET /user/profile:


Profil zobrazený: GET
        
1.4.2. Pole v middleware

Príklad:


$DotApp->middleware("authCheck", ['auth', 'controllers\\UserController', 'checkAuth']);
$DotApp->router->get('/secure', $DotApp->middleware("authCheck"));
        

Výstup pri GET /secure:


Overenie úspešné
        
1.4.3. Closure s DI v middleware

Príklad:


$DotApp->router->before(["/admin/*"], function (DotApp $dotApp, LogService $log) {
    echo "Metóda: " . $dotApp->router->request->getMethod() . ", Log: " . $log->getLogName() . "
"; }); $DotApp->router->get('/admin/dashboard', function () { echo "Admin panel"; });

Výstup pri GET /admin/dashboard:


Metóda: GET, Log: AdminLog
Admin panel
1.4.4. Manuálne volanie middleware

Príklad:


$DotApp->middleware("logAccess", function (DotApp $dotApp, ...$args) {
    auth\controllers\UserController::call("login", ...$args);
});
$DotApp->router->get('/access', $DotApp->middleware("logAccess"));
        

Výstup pri GET /access:


Prihlásenie bolo úspešné: GET
        

1.5. Praktické príklady

1. Registrácia a resolúcia singletonu:


$DotApp->singleton('cache', function () {
    return new CacheService();
});
$cache1 = $DotApp->resolve('cache');
$cache2 = $DotApp->resolve('cache');
echo ($cache1 === $cache2) ? "Rovnaká inštancia" : "Rôzne inštancie";
        

Výstup:


Rovnaká inštancia
        

2. Middleware s Closure a DI:


$DotApp->router->before(["/api/*"], function (DotApp $dotApp) {
    echo "Kontrola: " . $dotApp->router->request->getMethod() . "
"; }); $DotApp->router->get('/api/users', "auth:controllers\\UserController@showProfile");

Výstup pri GET /api/users:


Kontrola: GET
Profil zobrazený: GET

3. Kombinácia bind a Closure:


$DotApp->bind('logger', function () {
    return new Logger();
});
$DotApp->router->get('/log', function (DotApp $dotApp, Logger $logger) {
    echo "Log: " . $logger->getLogId() . ", Metóda: " . $dotApp->router->request->getMethod();
});
        

Výstup pri GET /log:


Log: 12345, Metóda: GET
        

1.6. Poznámky

  • Singleton vs. Bind: Používajte singleton pre zdieľané inštancie (napr. DotApp, databáza), bind pre nové inštancie pri každom volaní.
  • Closure DI: Funguje automaticky v routeri a middleware vďaka di.
  • Middleware parametre: Dynamické parametre z middlewareCall ešte nie sú implementované – odovzdávajte manuálne.
  • Best practice: Registrujte kľúčové služby ako singletony v inicializácii modulu.

DotBridge

DotBridge je kľúčovou súčasťou DotApp Frameworku, ktorá zabezpečuje bezpečnú a efektívnu komunikáciu medzi PHP na strane servera a JavaScriptom na strane klienta prostredníctvom AJAX požiadaviek. Umožňuje definovať PHP funkcie, ktoré sú volateľné z front-endu, spravovať vstupy a chrániť komunikáciu pred neoprávneným prístupom alebo zneužitím. DotBridge je navrhnutý tak, aby poskytoval flexibilitu, bezpečnosť a jednoduchú integráciu dynamických funkcií do webových aplikácií.

1.1 Čo je DotBridge?

DotBridge v DotApp Frameworku je trieda Dotsystems\App\Parts\Bridge, ktorá slúži ako most medzi back-endovou logikou v PHP a front-endovými akciami v JavaScripte. Umožňuje volanie PHP funkcií z HTML prvkov (napr. tlačidiel, formulárových vstupov) cez AJAX požiadavky na cestu /dotapp/bridge. Jeho hlavnou úlohou je zjednodušiť prepojenie klient-server, pričom zabezpečuje, aby komunikácia bola overená, šifrovaná a chránená pred útokmi, ako sú CSRF alebo opakované odosielanie požiadaviek.

DotBridge je integrovaný priamo do jadra frameworku, takže ho nemusíte samostatne inštalovať ani konfigurovať – stačí ho používať cez $dotApp->bridge.

1.2 Kľúčové vlastnosti

DotBridge ponúka širokú škálu funkcií, ktoré uľahčujú vývoj bezpečných a dynamických aplikácií:

  • Bezpečná komunikácia: Používa šifrovanie dát, overovanie kľúčov a CRC kontrolu na ochranu požiadaviek.
  • Volanie PHP funkcií: Definovanie PHP funkcií volateľných z JavaScriptu s podporou before a after callbackov.
  • Validačné filtre: Real-time validácia vstupov (napr. email, URL, heslo) s vizuálnou spätnou väzbou na strane klienta.
  • Rate Limiting: Možnosť nastaviť limity požiadaviek za určitý čas (rateLimit(pocet_sekund,pocet_kliknuti)).
  • Jednorazové kľúče: Podpora oneTimeUse a regenerateId pre vyššiu bezpečnosť.
  • Flexibilita: Podpora rôznych udalostí (napr. click, keyup) a dynamických vstupov z HTML.
  • Chaining: Reťazenie metód pre prehľadnejší kód na strane PHP aj JavaScriptu.

1.3 Základné princípy fungovania

DotBridge spracováva komunikáciu medzi klientom a serverom v nasledujúcich krokoch:

  1. Generuje unikátny kľúč pre reláciu a registruje PHP funkcie na strane servera cez fn().
  2. V HTML sa definujú udalosti (napr. dotbridge:on(click)) a vstupy (napr. dotbridge:input), ktoré sú prepojené s PHP funkciami.
  3. Pri spustení udalosti (napr. kliknutí) sa odošle AJAX požiadavka na /dotapp/bridge s šifrovanými dátami.
  4. Server overí kľúč, dešifruje dáta, skontroluje limity požiadaviek a spustí požadovanú PHP funkciu.
  5. Výsledok sa vráti ako JSON odpoveď, ktorú môže JavaScript ďalej spracovať.

Komunikácia je chránená šifrovaním dát, overovaním kľúčov a limitmi, aby sa predišlo zneužitiu alebo neoprávnenému prístupu.

Príklad základného použitia:

<button {{ dotbridge:on(click)="sayHello" }}>Klikni ma</button>
        

$dotApp->bridge->fn("sayHello", function($data) {
    return ["status" => 1, "message" => "Ahoj zo servera!"];
});
        

Po kliknutí na tlačidlo sa zavolá PHP funkcia sayHello a vráti JSON odpoveď s textom "Ahoj zo servera!".

2. Začíname

Táto kapitola vás prevedie základmi práce s DotBridge v DotApp Frameworku – od inicializácie až po definovanie prvej funkcie, pridanie udalosti na front-end a spracovanie odpovede v JavaScripte.

2.1 Inicializácia DotBridge

DotBridge je automaticky inicializovaný ako súčasť inštancie $dotApp pri vytvorení aplikácie v DotApp Frameworku. Prístup k nemu získate cez $dotApp->bridge. Pri inicializácii sa vygeneruje unikátny kľúč relácie (uložený v _bridge.key v relácii) a nastavia sa predvolené hodnoty, ako sú limity požiadaviek a vstavané validačné filtre.

Technické detaily:

  • Bridge je inštancia triedy Dotsystems\App\Parts\Bridge.
  • Prijíma referenciu na $dotApp, čím získava prístup k šifrovacím metódam, reláciám (dsm) a routeru.
  • Automaticky registruje cestu /dotapp/bridge v routeri pre spracovanie AJAX požiadaviek.

2.2 Definovanie PHP funkcie

Na strane servera definujete volateľnú PHP funkciu pomocou metódy fn(), ktorá prijíma názov funkcie a callback. Callback dostane parameter $data, ktorý obsahuje dáta odoslané z front-endu (napr. hodnoty vstupov). Funkcia by mala vrátiť pole s kľúčmi ako status a status_txt pre konzistentnú odpoveď.

Príklad:

$dotApp->bridge->fn("sendMessage", function($data) {
    $message = $data["message"] ?? "Žiadna správa";
    return ["status" => 1, "status_txt" => "Správa prijatá: " . $message];
});
    

Funkcia sendMessage je teraz pripravená na volanie z front-endu a vráti JSON odpoveď s prijatou správou.

2.3 Pridanie udalosti na front-end

V HTML použite atribút {{ dotbridge:on(event)="functionName(params)" }} na prepojenie udalosti (napr. click, keyup) s PHP funkciou. Môžete definovať vstupy pomocou {{ dotbridge:input="name" }} a pridať parametre ako rateLimit(60,10) alebo oneTimeUse pre kontrolu správania.

Príklad:

<input type="text" {{ dotbridge:input="user.message" }}>
<button {{ dotbridge:on(click)="sendMessage(user.message)" rateLimit(60,10) }}>Odoslať</button>
    

Po kliknutí na tlačidlo sa hodnota z inputu user.message pošle do funkcie sendMessage s limitom 10 požiadaviek za minútu.

2.4 Spracovanie odpovede v JavaScripte

Na strane klienta môžete pomocou $dotapp.bridge() definovať before a after callbacky na spracovanie stavu pred odoslaním požiadavky a po prijatí odpovede. Tieto callbacky umožňujú dynamicky meniť UI na základe odpovede od servera.

Príklad:

$dotapp.bridge("sendMessage", "click")
    .before(function(data, element) {
        $(element).text("Odosiela sa...");
    })
    .after(function(data, element) {
        $(element).text("Hotovo!");
        alert(data["status_txt"]);
    });
    

Pred odoslaním sa text tlačidla zmení na "Odosiela sa..." a po prijatí odpovede sa zobrazí správa z PHP funkcie.

3. Pokročilé použitie

Táto kapitola pokrýva pokročilé funkcie DotBridge, ako sú validačné filtre, rate limiting, chaining metód a práca s dynamickými dátami. Tieto nástroje umožňujú vytvárať robustnejšie a bezpečnejšie aplikácie s väčšou kontrolou nad správaním.

3.1 Validačné filtre

DotBridge poskytuje vstavané validačné filtre na overovanie vstupov v reálnom čase priamo na strane klienta. Filtre, ako email, url, phone alebo password, aplikujú regulárne výrazy a vizuálnu spätnú väzbu (CSS triedy) na základe validity vstupu. Používajú sa v HTML cez atribút dotbridge-result="0" dotbridge-input="name".

Dostupné argumenty:

  • filter: Názov filtra (napr. email).
  • start_checking_length: Minimálny počet znakov pre začatie validácie.
  • class_ok: CSS trieda pre platný vstup.
  • class_bad: CSS trieda pre neplatný vstup.
Príklad:

<input type="text" {{ dotbridge:input="user.email(email, 5, 'valid-email', 'invalid-email')" }}>
    

Input user.email sa začne validovať po zadaní 5 znakov. Ak je email platný, pridá sa trieda valid-email, ak nie, invalid-email.

3.2 Rate Limiting a bezpečnostné mechanizmy

DotBridge umožňuje obmedziť počet požiadaviek pomocou parametrov rateLimit(sekundy,kliknutia), čím chráni aplikáciu pred zneužitím. Ďalšie bezpečnostné funkcie zahŕňajú oneTimeUse (jednorazové použitie kľúča) a regenerateId (regenerácia ID po každom použití).

Použitie:

  • rateLimit(sekundy,kliknutia): Maximálne kliknutia požiadaviek za počet sekúnd sekundy.
  • oneTimeUse: Kľúč je platný iba pre jedno použitie.
  • regenerateId: Vygeneruje nový kľúč po každom volaní.
Príklad:

<button {{ dotbridge:on(click)="submitForm" rateLimit(60,10) rateLimit(3600,100) }}>Submit</button>
<button {{ dotbridge:on(click)="submitForm" oneTimeUse }}>Submit</button>
<button {{ dotbridge:on(click)="submitForm" regenerateId }}>Submit</button>
    

1. príklad - Tlačidlo umožní maximálne 10 kliknutí za minútu, 100 za hodinu.

2. príklad - Tlačidlo umožní len jedno kliknutie a listener zanikne.

3. príklad - Tlačidlo regeneruje ID na každom kliknutí.

3.3 Chaining metód

DotBridge podporuje reťazenie metód na strane PHP aj JavaScriptu, čo zjednodušuje definovanie funkcií a callbackov. Na PHP použite fn(), before() a after(), na JS $dotapp.bridge() s before() a after().

Príklad:

// PHP
$dotApp->bridge->fn("processData", function($data) {
    return ["status" => 1, "result" => $data["value"]];
})
->before(function($data) {
    $data["value"] = trim($data["value"] ?? "");
    return $data;
})
->after(function($result, $data) {
    $result["timestamp"] = time();
    return $result;
});
    

// JavaScript
$dotapp.bridge("processData", "click")
    .before(function(data, element) {
        $(element).addClass("loading");
    })
    .after(function(data, element) {
        $(element).removeClass("loading");
        console.log(data["result"]);
    });
    

Pred spracovaním sa vstup očistí (before), po spracovaní sa pridá časová pečiatka (after) a na klientovi sa zobrazí stav načítavania.

3.4 Práca s dynamickými dátami

DotBridge umožňuje posielať a spracovávať dynamické dáta z viacerých vstupov definovaných v HTML. Vstupy sú identifikované pomocou dotbridge:input a odoslané v $_POST['data'], kde ich PHP funkcia môže spracovať.

Príklad:




    

$dotApp->bridge->fn("saveUser", function($data) {
    $name = $data["user.name"] ?? "";
    $email = $data["user.email"] ?? "";
    return ["status" => 1, "status_txt" => "Užívateľ $name ($email) uložený"];
});
    

Po kliknutí sa hodnoty z inputov user.name a user.email pošlú do funkcie saveUser a vrátia sa v odpovedi.

4. Best Practices a tipy

Táto kapitola ponúka odporúčania a tipy na efektívne a bezpečné používanie DotBridge. Zahŕňa optimalizáciu bezpečnosti, ladenie problémov a integráciu s ostatnými časťami DotApp Frameworku.

4.1 Optimalizácia bezpečnosti

Bezpečnosť je kľúčovým aspektom pri používaní DotBridge. Nasledujúce odporúčania pomôžu minimalizovať riziká a zabezpečiť robustnú komunikáciu:

  • Používajte rate limiting: Vždy nastavte rateLimitM a rateLimitH pre akcie citlivé na opakované volania, aby ste predišli zneužitiu (napr. brute force útokom).
  • Aktivujte jednorazové kľúče: Pre kritické operácie (napr. odoslanie formulára) použite oneTimeUse alebo regenerateId, čím zabránite opakovanému použitiu rovnakého kľúča.
  • Overujte vstupy: Kombinujte validačné filtre s dodatočnou kontrolou na serveri (napr. filter_var()), aby ste zaistili konzistentnú validáciu.
  • Monitorujte relácie: Pravidelne kontrolujte a čistite staré kľúče v _bridge.objects, aby ste predišli preplneniu pamäte.
  • Používajte šifrovanie: Využite vstavané šifrovanie DotApp (encrypt(), decrypt()) pre citlivé dáta v komunikácii.
Príklad:


    

$dotApp->bridge->fn("secureAction", function($data) {
    return ["status" => 1, "status_txt" => "Akcia vykonaná bezpečne"];
});
    

Tento príklad obmedzuje akciu na 2 volania za minútu a umožňuje iba jedno použitie kľúča.

4.2 Ladenie a riešenie problémov

Pri práci s DotBridge sa môžu vyskytnúť chyby. Tu sú bežné problémy a ich riešenia:

  • CRC check failed (error_code 1): Overte, či dáta odoslané z front-endu neboli modifikované. Skontrolujte integritu JavaScript kódu a sieťové požiadavky.
  • Bridge key does not match (error_code 2): Uistite sa, že kľúč v relácii (_bridge.key) zodpovedá tomu, čo posiela klient. Môže ísť o problém s vypršanou reláciou.
  • Function not found (error_code 3): Skontrolujte, či je funkcia správne zaregistrovaná pomocou fn() a či sa názov zhoduje s volaním v HTML.
  • Rate limit exceeded (error_code 4): Prekročili ste nastavený limit. Zvýšte rateLimitM/rateLimitH alebo informujte používateľa o čakaní.

Tip: Zapnite ladenie v DotApp a sledujte odpovede z /dotapp/bridge v nástrojoch prehliadača (Network tab) pre detailné informácie.

4.3 Integrácia s inými časťami DotApp

DotBridge je navrhnutý na súčinnosť s ostatnými komponentmi DotApp, ako sú Router, Request a databáza. Integrácia umožňuje vytvárať komplexné aplikácie s minimálnym úsilím.

Príklady integrácie:

  • S Routerom: DotBridge automaticky využíva $dotApp->router na registráciu cesty /dotapp/bridge.
  • S Requestom: Dáta z $_POST sú sprístupnené v callbacku ako $data.
  • S databázou: Môžete priamo vkladať dáta z front-endu do databázy cez $dotApp->db.
Príklad s databázou:



    

$dotApp->bridge->fn("saveEmail", function($data) use ($dotApp) {
    $email = $data["user.email"] ?? "";
    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $dotApp->db->insertInto("users")
            ->values("email", $email)
            ->execute();
        return ["status" => 1, "status_txt" => "Email uložený"];
    }
    return ["status" => 0, "status_txt" => "Neplatný email"];
});
    

Po kliknutí sa email overí a uloží do databázy, pričom sa vráti odpoveď o úspechu alebo chybe.

Databaser

1. Úvod

1.1. Čo je Databaser?

Databaser je robustná a flexibilná knižnica na správu databázových interakcií, ktorá je integrovanou súčasťou DotApp Frameworku. Navrhnutá je tak, aby poskytovala jednoduchý, bezpečný a efektívny spôsob práce s databázami, či už ide o základné operácie, alebo pokročilé dotazy. Databaser eliminuje potrebu písania surových SQL dotazov (aj keď túto možnosť stále ponúka) a prináša moderný prístup k manipulácii s dátami prostredníctvom intuitívneho QueryBuildera a voliteľného ORM (Object-Relational Mapping) systému. Jeho hlavným cieľom je uľahčiť vývojárom prácu s databázami a zároveň zachovať vysokú mieru prispôsobiteľnosti a výkonu.

Databaser je priamo zabudovaný do jadra DotApp Frameworku, takže nie je potrebné ho samostatne inštalovať ani konfigurovať mimo frameworku. Po nastavení databázových pripojení v rámci DotAppu je pripravený na okamžité použitie.

1.2. Kľúčové vlastnosti

Databaser ponúka širokú škálu funkcií, ktoré ho robia výnimočným nástrojom pre prácu s databázami:

  • Jednoduché vytváranie a vykonávanie SQL dotazov: Podpora prepared statements zaisťuje bezpečnosť a jednoduchosť pri práci s dátami.
  • Správa viacerých databázových pripojení: Možnosť definovať a prepínať medzi rôznymi databázami s uloženými prihlasovacími údajmi.
  • Podpora vlastných driverov: Okrem predvolených driverov (MySQLi a PDO) je možné implementovať vlastné databázové drivery.
  • Voliteľný ORM: Pre MySQLi aj PDO je k dispozícii ORM s triedami Entity (jednotlivý riadok) a Collection (súbor riadkov), ktoré zjednodušujú prácu s dátami ako s objektmi.
  • Lazy loading a vzťahy: Podpora vzťahov typu HasOne, HasMany, MorphOne a MorphMany s možnosťou prispôsobiť dotazy pomocou callbackov.
  • Pokročilé vzťahy: Možnosť upraviť QueryBuilder v rámci vzťahov (napr. pridať limit, orderBy, where) cez voliteľný callback parameter.
  • Validácia a hromadné operácie: ORM obsahuje vstavanú validáciu dát a podporu hromadných operácií nad kolekciami.
  • Integrovaný QueryBuilder: Intuitívny nástroj na tvorbu dotazov, ktorý pokrýva od jednoduchých SELECTov až po zložité JOINy a subdotazy.
  • Callbacky pre SUCCESS a ERROR: Každá operácia vracia výsledky a debugovacie dáta cez callbacky, čo zjednodušuje spracovanie úspechov aj chýb.
  • Podpora transakcií: Jednoduchá 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 medzi nimi závisí od potrieb vášho projektu:

  • RAW MÓD:
    • Vracajú sa priamo výsledky databázových dotazov (napr. polia alebo databázové zdroje).
    • Ideálne pre jednoduché aplikácie, rýchle prototypy alebo situácie, kde potrebujete maximálnu kontrolu nad SQL dotazmi.
    • Príklad použitia: Jednoduchý SELECT na získanie zoznamu používateľov bez potreby objektovej manipulácie.
    • Výhody: Rýchle vykonanie, minimálna réžia, plná flexibilita pri písaní dotazov.
  • ORM MÓD:
    • Dáta sú mapované na objekty (Entity pre jeden riadok, Collection pre viac riadkov), čo uľahčuje prácu s dátami ako s objektmi.
    • Vhodné pre komplexné aplikácie, kde potrebujete vzťahy medzi tabuľkami, validáciu dát alebo hromadné operácie.
    • Príklad použitia: Správa používateľov s ich príspevkami (vzťah HasMany) a automatické ukladanie zmien.
    • Výhody: Objektovo-orientovaný prístup, podpora vzťahov, jednoduchá manipulácia s dátami.
Kedy použiť ktorý prístup?

  • Ak potrebujete rýchly výkon a jednoduché dotazy, zvoľte RAW.
  • Ak pracujete s komplexnými dátovými štruktúrami a chcete elegantné riešenie, siahnite po ORM.

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
  • Legacy aj moderný prístup s ORM.
  • Vhodný pre projekty, ktoré už používajú MySQLi, alebo pre jednoduchšie aplikácie s MySQL databázami.
  • Podporuje všetky funkcie QueryBuildera a ORM.
PDO
  • Moderný prístup s podporou viacerých databáz (MySQL, PostgreSQL, SQLite atď.).
  • Flexibilnejší vďaka dynamickému DSN (Data Source Name), čo umožňuje pripojenie k rôznym typom databáz.
  • Rovnako podporuje QueryBuilder aj 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á konkrétneho databázového systému.

1.5. Integrovaný QueryBuilder

QueryBuilder je srdcom Databaseru. Umožňuje vytvárať SQL dotazy pomocou reťaziteľných metód, čím zjednoduš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: raw s podporou otáznikov (?) aj pomenovaných premenných (:name).

QueryBuilder automaticky spravuje prepared statements a bindings, čím zaisťuje ochranu pred SQL injection útokmi. Každá hodnota, ktorá sa použije v dotaze (napr. v podmienkach where alebo pri vkladaní dát cez insert), je automaticky escapovaná a nahradená placeholdermi (? alebo pomenovanými premennými :name). Tým sa minimalizuje riziko bezpečnostných zraniteľností a zároveň sa zvyšuje prehľadnosť kódu.

1.6. Callbacky pre SUCCESS a ERROR

Databaser používa systém callbackov na spracovanie výsledkov a chýb. Každá operácia (napr. execute(), save()) môže prijať dva voliteľné callbacky:

SUCCESS callback

Spustí sa pri úspešnom vykonaní operácie. Dostáva tri parametre:

  • $result: Výsledok operácie (napr. pole dát v RAW móde, objekt v ORM móde).
  • $db: Inštancia Databaseru, ktorá umožňuje ďalšie dotazy.
  • $debug: Debugovacie dáta (napr. vygenerovaný SQL dotaz, bindings).
ERROR callback

Spustí sa pri chybe. Dostáva rovnako tri parametre:

  • $error: Pole s informáciami o chybe (error – text chyby, errno – kód chyby).
  • $db: Inštancia Databaseru pre prípadné ďalšie operácie.
  • $debug: Debugovacie dáta pre analýzu problému.

Tento prístup zjednodušuje asynchrónne spracovanie a umožňuje reťazenie operácií priamo v callbackoch. Napríklad, ak pri vykonaní jedného dotazu potrebujete okamžite spustiť ďalší, môžete to urobiť priamo v SUCCESS callbacku pomocou $db->q(). Tento systém zároveň zvyšuje flexibilitu a čitateľnosť kódu, pretože logika pre úspech a chybu je oddelená a prehľadne definovaná. Ak nie je nastavený ERROR callback, je možné zachytiť chybu pomocou TRY-CATCH bloku. Naopak, ak je ERROR callback nastavený, TRY-CATCH blok nebude fungovať, pretože správa chýb je v tomto prípade plne delegovaná na callback.

2. Začíname

2.1. Inštalácia a konfigurácia Databaseru

Databaser je neoddeliteľnou súčasťou DotApp Frameworku, takže nie je potrebné ho samostatne inštalovať. Ak ste už nastavili DotApp Framework vo vašom projekte, Databaser je automaticky k dispozícii cez inštanciu $DotApp->DB. Predpokladáme, že máte framework nakonfigurovaný a pripravený na použitie.

2.2. Pridanie databázového pripojenia

Databaser umožňuje pridať a spravovať viacero databázových pripojení. Pripojenie sa definuje pomocou metódy add(), ktorá je volaná na inštancii $DotApp->DB. Príklad:

$DotApp->DB->add(
    'main',           // Názov pripojenia
    'localhost',      // Server
    'root',           // Používateľské meno
    'password123',    // Heslo
    'moja_databaza',  // Názov databázy
    'utf8mb4',        // Kódovanie
    'mysql'           // Typ databázy
);

2.3. Výber drivera (MySQLi alebo PDO)

Databaser podporuje MySQLi aj PDO. Výber drivera:

// Použitie MySQLi drivera
$DotApp->DB->driver('mysqli');

// Použitie PDO drivera
$DotApp->DB->driver('pdo');

2.4. Prvé spojenie s databázou

Po definovaní pripojenia a výbere drivera je potrebné aktivovať pripojenie:

$DotApp->DB->selectDb('main');

Príklad prvého jednoduchého dotazu:

$DotApp->DB
    ->driver('pdo')
    ->return('raw')
    ->selectDb('main')
    ->q(function ($qb) {
        $qb->select('*', 'users');
    })
    ->execute(
        function ($result, $db, $debug) {
            echo "Vygenerovaný dotaz: " . $debug['query'] . "\n";
            var_dump($result);
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']} (kód: {$error['errno']})\n";
        }
    );

Vysvetlenie
  • driver('pdo'): Nastavíme PDO ako aktívny driver.
  • return('raw'): Nastavíme RAW formát výstupu.
  • selectDb('main'): Aktivujeme pripojenie "main".
  • q() (alias qb()): Spustíme QueryBuilder a definujeme dotaz (v tomto prípade SELECT * FROM users).
  • execute(): Vykonáme dotaz s callbackmi pre úspech a chybu.
  • $result: Pole s výsledkami (v RAW móde).
  • $debug: Obsahuje vygenerovaný SQL dotaz a ďalšie informácie.

Výstup (príklad):

Vygenerovaný dotaz: SELECT * FROM users

array(2) {
  [0] => array(3) {
    ["id"] => string(1) "1"
    ["name"] => string(4) "Jano"
    ["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ým nástrojom Databaseru, ktorý umožňuje vytvárať SQL dotazy pomocou reťaziteľných metód. Jeho hlavnou výhodou je jednoduchosť, čitateľnosť a bezpečnosť – automaticky spravuje prepared statements a bindings, čím chráni pred SQL injection útokmi. V tejto kapitole podrobne rozoberieme jeho fungovanie, dostupné metódy a ukážeme príklady od jednoduchých až po zložité dotazy.

3.1. Základné princípy QueryBuildera

QueryBuilder je objekt triedy Dotsystems\App\Parts\QueryBuilder, ktorý sa používa v rámci metódy q() alebo qb() na inštancii $DotApp->DB. Funguje tak, že postupne budujete dotaz volaním metód, pričom každá metóda pridáva časť SQL príkazu (napr. select, where, join). Na konci sa dotaz vykoná pomocou metód ako execute(), first() alebo all().

Základné vlastnosti:

  • Reťaziteľnosť: Metódy vracajú inštanciu QueryBuildera, takže ich môžete spájať do reťazca.
  • Prepared Statements: Všetky hodnoty sú automaticky escapované a nahrádzané placeholdermi (?).
  • Flexibilita: Podpora surových SQL dotazov cez metódu raw() pre špeciálne prípady.
  • Debugovateľnosť: Po vykonaní dotazu dostanete v $debug vygenerovaný SQL a bindings.

Príklad základného použitia:

$DotApp->DB->q(function ($qb) {
    $qb->select('*', 'users')->where('age', '>', 18);
})->execute(
    function ($result, $db, $debug) {
        echo $debug['query']; // "SELECT * FROM users WHERE age > ?"
        var_dump($debug['bindings']); // [18]
        var_dump($result);
    }
);

3.2. Zoznam metód QueryBuildera

Tu je podrobný prehľad všetkých hlavných metód QueryBuildera s vysvetlením a príkladmi.

3.2.1. select

Metóda select() definuje, ktoré stĺpce a z ktorej tabuľky chcete vybrať dáta.

Syntax: select($columns = '*', $table = null)

Parametre:

  • $columns: Reťazec alebo pole stĺpcov (napr. 'id, name' alebo ['id', 'name']).
  • $table: Názov tabuľky (voliteľné, ak použijete from()).

SQL ekvivalent: SELECT stĺpce FROM tabuľka

Príklad:

$qb->select('id, name', 'users');
// SQL: SELECT id, name FROM users
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 s dátami (stĺpec => hodnota).

SQL ekvivalent: INSERT INTO tabuľka (stĺpce) VALUES (hodnoty)

Príklad:

$qb->insert('users', ['name' => 'Jano', 'age' => 25]);
// SQL: INSERT INTO users (name, age) VALUES (?, ?)
// Bindings: ['Jano', 25]
3.2.3. update

Metóda update() a set() aktualizuje existujúce riadky.

Syntax: update($table) + set(array $data)

Parametre:

  • $table: Názov tabuľky.
  • $data: Asociatívne pole s aktualizovanými hodnotami.
  • SQL ekvivalent: UPDATE tabuľka SET stĺpec = hodnota

    Príklad:

    $qb->update('users')->set(['age' => 26])->where('id', '=', 1);
    // SQL: UPDATE users 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 tabuľka

    Príklad:

    $qb->delete('users')->where('id', '=', 1);
    // SQL: DELETE FROM users 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. =, >, <).
    • $value: Hodnota alebo Closure pre subdotaz.
    • $boolean: Logický spoj (predvolené AND).

    SQL ekvivalent: WHERE stĺpec operátor hodnota

    Príklad:

    $qb->select('*', 'users')
       ->where('age', '>', 18)
       ->orWhere('name', '=', 'Jano');
    // SQL: SELECT * FROM users WHERE age > ? OR name = ?
    // Bindings: [18, 'Jano']
    
    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 subdotaz (QueryBuilder).
    • $first: Prvý stĺpec podmienky.
    • $operator: Operátor spojenia.
    • $second: Druhý stĺpec podmienky.
    • $type: Typ spojenia (INNER, LEFT).

    SQL ekvivalent: INNER JOIN tabuľka ON podmienka

    Príklad:

    $qb->select('users.name', 'users')
       ->join('posts', 'users.id', '=', 'posts.user_id');
    // SQL: SELECT users.name FROM users INNER JOIN posts ON users.id = 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 stĺpce

    Príklad:

    $qb->select('age', 'users')->groupBy('age');
    // SQL: SELECT age FROM users 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 stĺpec operátor hodnota

    Príklad:

    $qb->select('age', 'users')->groupBy('age')->having('age', '>', 20);
    // SQL: SELECT age FROM users GROUP BY age HAVING age > ?
    // Bindings: [20]
    
    3.2.9. orderBy

    Metóda orderBy() zoraďuje výsledky.

    Syntax: orderBy($column, $direction = 'ASC')

    Parametre:

    • $column: Stĺpec.
    • $direction: Smer (ASC alebo DESC).

    SQL ekvivalent: ORDER BY stĺpec smer

    Príklad:

    $qb->select('*', 'users')->orderBy('age', 'DESC');
    // SQL: SELECT * FROM users ORDER BY age DESC
    
    3.2.11. limit a offset

    Metódy limit() a offset() obmedzujú počet výsledkov.

    Syntax: limit($limit) + offset($offset)

    Parametre:

    • $limit: Počet riadkov.
    • $offset: Počiatočný posun.

    SQL ekvivalent: LIMIT počet OFFSET posun

    Príklad:

    $qb->select('*', 'users')->limit(5)->offset(10);
    // SQL: SELECT * FROM users LIMIT ? OFFSET ?
    // Bindings: [5, 10]
    
    3.2.11. raw Query

    Metóda raw() umožňuje použiť surový SQL dotaz.

    Syntax: raw($sql, array $bindings = [])

    Parametre:

    • $sql: Surový SQL reťazec.
    • $bindings: Pole hodnôt pre placeholdery.

    SQL ekvivalent: Priamo zadaný dotaz.

    Príklad:

    $qb->raw('SELECT * FROM users WHERE age > ?', [18]);
    // SQL: SELECT * FROM users WHERE age > ?
    // Bindings: [18]
    

    3.3. Príklady od jednoduchých po zložité dotazy

    Jednoduchý select

    $qb->select('*', 'users');
    // SQL: SELECT * FROM users
    

    select s where podmienkou

    $qb->select('name', 'users')->where('age', '>', 18);
    // SQL: SELECT name FROM users WHERE age > ?
    // Bindings: [18]
    

    Vnorené podmienky (Closure)

    $qb->select('*', 'users')->where(function ($qb) {
        $qb->where('age', '>', 18)->orWhere('name', '=', 'Jano');
    });
    // SQL: SELECT * FROM users WHERE (age > ? OR name = ?)
    // Bindings: [18, 'Jano']
    

    join s viacerými tabuľkami

    $qb->select('users.name, posts.title', 'users')
       ->join('posts', 'users.id', '=', 'posts.user_id')
       ->leftJoin('comments', 'posts.id', '=', 'comments.post_id');
    // SQL: SELECT users.name, posts.title FROM users
    //      INNER JOIN posts ON users.id = posts.user_id
    //      LEFT JOIN comments ON posts.id = comments.post_id
    

    Subquery ako hodnota

    $qb->select('name', 'users')->where('id', '=', function ($qb) {
        $qb->select('user_id', 'posts')->where('title', '=', 'Novinka');
    });
    // SQL: SELECT name FROM users WHERE id = (SELECT user_id FROM posts WHERE title = ?)
    // Bindings: ['Novinka']
    

    raw dotaz s pomenovanými premennými

    $qb->raw('SELECT * FROM users WHERE age > :age AND name = :name', [
        'age' => 18,
        'name' => 'Jano'
    ]);
    // SQL: SELECT * FROM users WHERE age > ? AND name = ?
    // Bindings: [18, 'Jano']
    

    4. Práca s Databaserom v DotApp

    V tejto kapitole sa zameriame na praktické použitie Databaseru v rámci DotApp Frameworku. Ukážeme, ako nastaviť typ návratu, vykonávať dotazy, pracovať s ORM, spravovať transakcie a debugovať výsledky. Databaser je navrhnutý tak, aby poskytoval flexibilitu a jednoduchosť, či už preferujete RAW prístup, alebo objektovo-orientovaný ORM.

    4.1. Nastavenie typu návratu (RAW vs. ORM)

    Databaser umožňuje definovať, aký typ dát chcete dostať ako výsledok dotazu. Typ návratu sa nastavuje pomocou metódy return() a ovplyvňuje, ako budú dáta spracované po vykonaní dotazu.

    • RAW: Vráti surové dáta (napr. pole riadkov alebo databázový zdroj). Predvolený typ.
    • ORM: Vráti dáta ako objekty (Entity pre jeden riadok, Collection pre viac riadkov).

    Syntax: $DotApp->DB->return($type)

    $type: Reťazec 'RAW' alebo 'ORM' (nezáleží na veľkosti písmen).

    Príklad nastavenia RAW:

    $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->select('*', 'users');
    })->execute(
        function ($result, $db, $debug) {
            var_dump($result); // Pole riadkov
        }
    );
    

    Príklad nastavenia ORM:

    $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->execute(
        function ($result, $db, $debug) {
            var_dump($result); // Inštancia Collection
        }
    );
    

    Typ návratu môžete meniť pred každým dotazom, čo vám dáva flexibilitu pri práci s rôznymi časťami aplikácie.

    4.2. Metódy pre vykonanie dotazov

    Databaser ponúka niekoľko metód na vykonanie dotazov vytvorených QueryBuilderom. Každá metóda má svoje špecifické použitie.

    4.2.1. execute()

    Metóda execute() je najuniverzálnejšia – vykoná dotaz a výsledky spracuje cez callbacky.

    Syntax: execute($success = null, $error = null)

    Parametre:

    • $success: Callback pre úspech (function ($result, $db, $debug)).
    • $error: Callback pre chybu (function ($error, $db, $debug)).

    Výstup: Závisí od typu návratu (RAW: pole/zdroj, ORM: Collection/Entity).

    Príklad:

    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users')->where('age', '>', 18);
    })->execute(
        function ($result, $db, $debug) {
            echo "Dotaz: " . $debug['query'] . "\n";
            var_dump($result);
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    
    4.2.2. first()

    Metóda first() vráti prvý riadok výsledkov.

    Syntax: first()

    Výstup: RAW – pole, ORM – Entity alebo null.

    Príklad:

    $result = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    if ($result) {
        echo $result->name; // Prístup k atribútom v ORM
    }
    
    4.2.3. all()

    Metóda all() vráti všetky riadky výsledkov.

    Syntax: all()

    Výstup: RAW – pole riadkov, ORM – Collection.

    Príklad:

    $users = $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    foreach ($users as $user) {
        echo $user['name'] . "\n";
    }
    
    4.2.4. raw()

    Metóda raw() vráti surový výsledok dotazu (napr. databázový zdroj).

    Syntax: raw()

    Výstup: Závisí od drivera (napr. mysqli_result alebo PDO statement).

    Príklad:

    $result = $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users');
    })->raw();
    while ($row = $DotApp->DB->fetchArray($result)) {
        echo $row['name'] . "\n";
    }
    

    4.3. Práca s ORM

    ORM mód Databaseru umožňuje pracovať s dátami ako s objektmi, čo zjednodušuje manipuláciu a vzťahy medzi tabuľkami.

    4.3.1. Entity a Collection

    Entity: Predstavuje jeden riadok tabuľky. Má atribúty zodpovedajúce stĺpcom a metódy na manipuláciu.

    Collection: Zoskupenie viacerých Entity objektov s podporou iterácie a hromadných operácií.

    Príklad:

    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    foreach ($users as $user) {
        echo $user->name . "\n"; // Collection vracia Entity objekty
    }
    
    4.3.2. Ukladanie dát (save())

    Metóda save() na Entity uloží zmeny do databázy.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->age = 26;
    $user->save(
        function ($result, $db, $debug) {
            echo "Používateľ uložený!\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    
    4.3.3. Vzťahy (hasOne, hasMany)

    ORM podporuje vzťahy medzi tabuľkami:

    • hasOne: Jeden k jednému.
    • hasMany: Jeden k viacerým.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $posts = $user->hasMany('posts', 'user_id'); // Získa všetky príspevky používateľa
    foreach ($posts as $post) {
        echo $post->title . "\n";
    }
    
    4.3.4. Lazy Loading a Collection metódy

    Vzťahy sa načítajú lenivým spôsobom (lazy loading), pokiaľ ich explicitne nevyvoláte. Collection ponúka metódy ako filter(), map(), pluck().

    Príklad:

    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $names = $users->pluck('name'); // Získa pole mien
    var_dump($names->all());
    

    4.4. Transakcie

    Databaser podporuje transakcie na zabezpečenie konzistencie dát.

    4.4.1. transaction(), commit(), rollback()

    Ručné ovládanie transakcií:

    $DotApp->DB->transaction();
    $DotApp->DB->q(function ($qb) {
        $qb->insert('users', ['name' => 'Jano']);
    })->execute();
    $DotApp->DB->commit(); // Alebo rollback() pri chybe
    

    Automatická transakcia s transact():

    $DotApp->DB->transact(function ($db) {
        $db->q(function ($qb) {
            $qb->insert('users', ['name' => 'Jano']);
        })->execute();
    }, function ($result, $db, $debug) {
        echo "Transakcia úspešná!\n";
    }, function ($error, $db, $debug) {
        echo "Chyba: {$error['error']}\n";
    });
    

    4.5. Debugovanie a práca s výstupom

    Každá operácia vracia tri hlavné prvky:

    • result: Výsledok dotazu (RAW: pole, ORM: objekty).
    • db: Inštancia Databaseru pre ďalšie dotazy.
    • debug: Pole s informáciami (napr. query, bindings).

    Príklad debugovania:

    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users')->where('age', '>', 18);
    })->execute(
        function ($result, $db, $debug) {
            echo "SQL: " . $debug['query'] . "\n";
            echo "Bindings: " . implode(', ', $debug['bindings']) . "\n";
            var_dump($result);
        }
    );
    

    5. Praktické príklady

    V tejto kapitole ukážeme praktické príklady použitia Databaseru v DotApp Frameworku. Zameriame sa na bežné scenáre, ako sú CRUD operácie (Create, Read, Update, Delete), pokročilé dotazy s join a subquery, práca s transakciami a ladenie chýb. Použijeme metódy insertedId() a affectedRows() namiesto priameho prístupu k $db->statement['execution_data'].

    5.1. Základné CRUD operácie v RAW móde

    Create (Vytvorenie):

    $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->insert('users', ['name' => 'Jano', 'age' => 25]);
    })->execute(
        function ($result, $db, $debug) {
            $id = $db->insertedId(); // Získanie ID nového záznamu
            echo "Nový používateľ s ID: $id bol vytvorený.\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    

    Read (Čítanie):

    $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->select('*', 'users')->where('age', '>', 20);
    })->execute(
        function ($result, $db, $debug) {
            foreach ($result as $user) {
                echo "Meno: {$user['name']}, Vek: {$user['age']}\n";
            }
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    

    Update (Aktualizácia):

    $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->update('users')->set(['age' => 26])->where('name', '=', 'Jano');
    })->execute(
        function ($result, $db, $debug) {
            $rows = $db->affectedRows(); // Počet ovplyvnených riadkov
            echo "$rows riadkov bolo aktualizovaných.\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    

    Delete (Odstránenie):

    $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->delete('users')->where('name', '=', 'Jano');
    })->execute(
        function ($result, $db, $debug) {
            $rows = $db->affectedRows();
            echo "$rows riadkov bolo odstránených.\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    

    5.2. Základné CRUD operácie v ORM móde

    Create:

    $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->insert('users', ['name' => 'Maria', 'age' => 30]);
    })->execute(
        function ($result, $db, $debug) {
            $id = $db->insertedId();
            $user = $db->q(function ($qb) use ($id) {
                $qb->select('*', 'users')->where('id', '=', $id);
            })->first();
            echo "Vytvorený používateľ: {$user->name}\n";
        }
    );
    

    Read:

    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    foreach ($users as $user) {
        echo "Meno: {$user->name}, Vek: {$user->age}\n";
    }
    

    Update:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('name', '=', 'Maria');
    })->first();
    if ($user) {
        $user->age = 31;
        $user->save(
            function ($result, $db, $debug) {
                echo "Používateľ {$user->name} aktualizovaný.\n";
            },
            function ($error, $db, $debug) {
                echo "Chyba: {$error['error']}\n";
            }
        );
    }
    

    Delete:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('name', '=', 'Maria');
    })->first();
    if ($user) {
        $DotApp->DB->q(function ($qb) use ($user) {
            $qb->delete('users')->where('id', '=', $user->id);
        })->execute(
            function ($result, $db, $debug) {
                $rows = $db->affectedRows();
                echo "$rows riadkov bolo odstránených.\n";
            }
        );
    }
    

    5.3. Pokročilé príklady s join a subquery

    join s viacerými tabuľkami:

    $DotApp->DB->return('RAW')->q(function ($qb) {
        $qb->select('users.name, posts.title', 'users')
           ->join('posts', 'users.id', '=', 'posts.user_id')
           ->where('users.age', '>', 25);
    })->execute(
        function ($result, $db, $debug) {
            foreach ($result as $row) {
                echo "Používateľ: {$row['name']}, Príspevok: {$row['title']}\n";
            }
        }
    );
    

    Subquery v ORM:

    $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')
           ->where('id', '=', function ($subQb) {
               $subQb->select('user_id', 'posts')
                      ->where('title', '=', 'Novinka');
           });
    })->execute(
        function ($users, $db, $debug) {
            foreach ($users as $user) {
                echo "Používateľ s novinkou: {$user->name}\n";
            }
        }
    );
    

    5.4. Práca s transakciami

    Automatická transakcia:

    $DotApp->DB->transact(function ($db) {
        $db->q(function ($qb) {
            $qb->insert('users', ['name' => 'Peter', 'age' => 28]);
        })->execute();
        $id = $db->insertedId();
        $db->q(function ($qb) use ($id) {
            $qb->insert('posts', ['user_id' => $id, 'title' => 'Prvý príspevok']);
        })->execute();
    }, function ($result, $db, $debug) {
        echo "Transakcia úspešná, ID používateľa: " . $db->insertedId() . "\n";
    }, function ($error, $db, $debug) {
        echo "Chyba v transakcii: {$error['error']}\n";
    });
    

    Ručná transakcia:

    $DotApp->DB->transaction();
    $DotApp->DB->q(function ($qb) {
        $qb->insert('users', ['name' => 'Anna', 'age' => 22]);
    })->execute(
        function ($result, $db, $debug) {
            $id = $db->insertedId();
            $db->q(function ($qb) use ($id) {
                $qb->insert('posts', ['user_id' => $id, 'title' => 'Test']);
            })->execute(
                function ($result, $db, $debug) {
                    $db->commit();
                    echo "Transakcia dokončená.\n";
                },
                function ($error, $db, $debug) {
                    $db->rollback();
                    echo "Rollback: {$error['error']}\n";
                }
            );
        }
    );
    

    5.5. Ladenie a riešenie chýb

    Debugovanie dotazu:

    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users')->where('age', '>', 18);
    })->execute(
        function ($result, $db, $debug) {
            echo "SQL: " . $debug['query'] . "\n";
            echo "Bindings: " . implode(', ', $debug['bindings']) . "\n";
            echo "Počet riadkov: " . $db->affectedRows() . "\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']} (kód: {$error['errno']})\n";
            echo "SQL: " . $debug['query'] . "\n";
        }
    );
    

    Riešenie chyby:

    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'neexistujuca_tabuľka'); // Chybný dotaz
    })->execute(
        function ($result, $db, $debug) {
            echo "Úspech\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
            // Možné ďalšie dotazy na opravu
            $db->q(function ($qb) {
                $qb->select('*', 'users'); // Skúsime inú tabuľku
            })->execute(
                function ($result, $db, $debug) {
                    echo "Opravený dotaz úspešný.\n";
                }
            );
        }
    );
    

    6. Práca so SchemaBuilderom

    SchemaBuilder je výkonný nástroj v Databaseri, ktorý slúži na definovanie a správu databázovej štruktúry. Umožňuje vytvárať, upravovať a odstraňovať tabuľky priamo z kódu bez nutnosti písania surových SQL príkazov na správu schémy. Je integrovaný do QueryBuildera a používa sa cez metódy ako createTable(), alterTable() a dropTable(). V tejto kapitole vysvetlíme jeho funkcie, vstupy a ukážeme praktické príklady.

    6.1. Základné princípy SchemaBuildera

    SchemaBuilder je trieda Dotsystems\App\Parts\SchemaBuilder, ktorá sa používa v callbacku metód schema() alebo priamo v createTable(), alterTable() a dropTable(). Jeho cieľom je poskytnúť programový spôsob definovania tabuliek, stĺpcov, indexov a cudzích kľúčov. Výsledné príkazy sú automaticky prevedené na SQL a vykonané cez aktívny driver (MySQLi alebo PDO).

    Kľúčové vlastnosti:

    • Reťaziteľné metódy: Podobne ako QueryBuilder, aj SchemaBuilder umožňuje reťazenie.
    • Abstrakcia: Funguje nezávisle od databázového drivera, hoci niektoré špecifické funkcie môžu závisieť od databázového systému.
    • Jednoduchosť: Umožňuje rýchlo definovať schému bez hlbokých znalostí SQL syntaxe.

    6.2. Dostupné metódy SchemaBuildera

    Tu je prehľad hlavných metód s vysvetlením a vstupmi:

    6.2.1. id()

    Pridá primárny kľúč typu BIGINT UNSIGNED AUTO_INCREMENT.

    Syntax: id($name = 'id')

    Parametre:

    • $name: Názov stĺpca (predvolené 'id').

    SQL ekvivalent: id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY

    Príklad:

    $schema->id(); // Vytvorí stĺpec `id`
    
    6.2.2. string()

    Pridá textový stĺpec typu VARCHAR.

    Syntax: string($name, $length = 255, $nullable = false)

    Parametre:

    • $name: Názov stĺpca.
    • $length: Dĺžka (predvolené 255).
    • $nullable: Povolí NULL hodnoty (predvolené false).

    SQL ekvivalent: VARCHAR(dĺžka) [NOT NULL | NULL]

    Príklad:

    $schema->string('name', 100, true); // `name` VARCHAR(100) NULL
    
    6.2.3. integer()

    Pridá číselný stĺpec typu INT.

    Syntax: integer($name, $nullable = false)

    Parametre:

    • $name: Názov stĺpca.
    • $nullable: Povolí NULL hodnoty (predvolené false).

    SQL ekvivalent: INT [NOT NULL | NULL]

    Príklad:

    $schema->integer('age'); // `age` INT NOT NULL
    
    6.2.4. timestamps()

    Pridá stĺpce created_at a updated_at pre časové značky.

    Syntax: timestamps()

    SQL ekvivalent:

    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    

    Príklad:

    $schema->timestamps();
    
    6.2.5. foreign()

    Pridá cudzí kľúč.

    Syntax: foreign($column, $references = 'id', $on = null)

    Parametre:

    • $column: Stĺpec s cudzím kľúčom.
    • $references: Referenčný stĺpec (predvolené 'id').
    • $on: Referenčná tabuľka (ak nie je zadaná, odvodí sa z $column).

    SQL ekvivalent: FOREIGN KEY (column) REFERENCES table (references) ON DELETE CASCADE

    Príklad:

    $schema->foreign('user_id', 'id', 'users'); // Cudzí kľúč na `users(id)`
    
    6.2.6. index()

    Pridá index na stĺpec.

    Syntax: index($column)

    Parametre:

    • $column: Názov stĺpca.

    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)

    Parametre:

    • $name: Názov stĺpca.
    • $type: Typ (napr. VARCHAR, INT).
    • $length: Dĺžka (voliteľné).
    • $nullable: Povolí NULL (predvolené false).

    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 sa používa v kombinácii s metódami QueryBuildera: createTable(), alterTable() a dropTable().

    6.3.1. Vytvorenie tabuľky

    Metóda createTable() vytvorí novú tabuľku.

    Príklad:

    $DotApp->DB->q(function ($qb) {
        $qb->createTable('users', function ($schema) {
            $schema->id();
            $schema->string('name', 50);
            $schema->integer('age', true);
            $schema->timestamps();
            $schema->index('name');
        });
    })->execute(
        function ($result, $db, $debug) {
            echo "Tabuľka 'users' bola vytvorená.\n";
            echo "SQL: {$debug['query']}\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    

    Vygenerovaný SQL:

    CREATE TABLE users (
        `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        `name` VARCHAR(50) NOT NULL,
        `age` INT NULL,
        `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        INDEX (`name`)
    )
    
    6.3.2. Úprava tabuľky

    Metóda alterTable() upraví existujúcu tabuľku.

    Príklad:

    $DotApp->DB->q(function ($qb) {
        $qb->alterTable('users', function ($schema) {
            $schema->addColumn('email', 'VARCHAR', 100, true);
            $schema->foreign('user_id', 'id', 'users');
            $schema->dropColumn('age');
        });
    })->execute(
        function ($result, $db, $debug) {
            echo "Tabuľka 'users' bola upravená.\n";
            echo "SQL: {$debug['query']}\n";
        }
    );
    

    Vygenerovaný SQL:

    ALTER TABLE users 
        ADD `email` VARCHAR(100) NULL,
        ADD FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
        DROP COLUMN `age`
    
    6.3.3. Odstránenie tabuľky

    Metóda dropTable() odstráni tabuľku.

    Príklad:

    $DotApp->DB->q(function ($qb) {
        $qb->dropTable('users');
    })->execute(
        function ($result, $db, $debug) {
            echo "Tabuľka 'users' bola odstránená.\n";
        }
    );
    

    Vygenerovaný SQL:

    DROP TABLE users
    

    6.4. Pokročilé príklady

    Vytvorenie tabuľky s cudzími kľúčmi:

    $DotApp->DB->q(function ($qb) {
        $qb->createTable('posts', function ($schema) {
            $schema->id();
            $schema->string('title', 200);
            $schema->integer('user_id');
            $schema->foreign('user_id', 'id', 'users');
            $schema->timestamps();
        });
    })->execute(
        function ($result, $db, $debug) {
            echo "Tabuľka 'posts' vytvorená.\n";
        }
    );
    

    Hromadná migrácia:

    $DotApp->DB->transact(function ($db) {
        $db->q(function ($qb) {
            $qb->createTable('users', function ($schema) {
                $schema->id();
                $schema->string('name');
            });
        })->execute();
        $db->q(function ($qb) {
            $qb->createTable('posts', function ($schema) {
                $schema->id();
                $schema->integer('user_id');
                $schema->foreign('user_id');
            });
        })->execute();
    }, function ($result, $db, $debug) {
        echo "Migrácia úspešná.\n";
    });
    

    6.5. Poznámky a obmedzenia

    Kompatibilita: Niektoré funkcie (napr. ON DELETE CASCADE) nemusia fungovať rovnako vo všetkých databázach (napr. SQLite má obmedzenia).

    Transakcie: Pri väčších zmenách schémy používajte transact() na zaistenie konzistencie.

    Debugovanie: Vždy kontrolujte $debug['query'] na overenie vygenerovaného SQL.

    7. CacheDriverInterface

    Databaser v DotApp Frameworku podporuje integráciu s cachingom, čo umožňuje ukladať výsledky dotazov a zvyšovať výkon aplikácie pri opakovaných požiadavkách na rovnaké dáta. Aby bolo možné caching využiť, je potrebné implementovať rozhranie CacheDriverInterface, ktoré definuje štandardné metódy pre prácu s cache. V tejto kapitole vysvetlíme, čo toto rozhranie obsahuje, aké metódy musí cache driver podporovať, a ukážeme, ako ho používať s Databaserom.

    7.1. Čo je CacheDriverInterface?

    CacheDriverInterface je rozhranie, ktoré určuje, ako má vyzerať driver pre ukladanie a načítanie dát z cache. Databaser ho používa na komunikáciu s ľubovoľným caching systémom (napr. Memcached, Redis, súborový systém), pričom logiku ukladania a správy cache si implementuje používateľ. Po nastavení cache drivera cez metódu cache() sa Databaser automaticky pokúsi načítavať výsledky z cache pred vykonaním dotazu a ukladať nové výsledky po jeho úspešnom dokončení.

    Výhody:

    • Zníženie zaťaženia databázy.
    • Rýchlejší prístup k často používaným dátam.
    • Flexibilita – môžete použiť akýkoľvek caching systém.

    7.2. Zloženie CacheDriverInterface

    Rozhranie CacheDriverInterface definuje štyri povinné metódy, ktoré musí každý cache driver implementovať:

    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 cache na základe kľúča.

    Parameter:

    • $key: Reťazec – unikátny kľúč pre uložené dáta.

    Návratová hodnota: Uložená hodnota alebo null, ak kľúč neexistuje.

    Účel: Databaser volá túto metódu, aby skontroloval, či už výsledok dotazu existuje v cache.

    7.2.2. set($key, $value, $ttl = null)

    Uloží hodnotu do cache s daným kľúčom.

    Parametre:

    • $key: Reťazec – kľúč pre uloženie.
    • $value: Dáta na uloženie (môžu byť pole, objekt atď.).
    • $ttl: Čas životnosti v sekundách (voliteľné, null znamená bez expirácie).

    Návratová hodnota: Žiadna (alebo true/false podľa implementácie).

    Účel: Po úspešnom vykonaní dotazu Databaser ukladá výsledok do cache.

    7.2.3. delete($key)

    Odstráni konkrétny kľúč z cache.

    Parameter:

    • $key: Reťazec – kľúč na odstránenie.

    Návratová hodnota: Žiadna (alebo true/false).

    Účel: Používa sa na explicitné mazanie konkrétneho záznamu z cache.

    7.2.4. deleteKeys($pattern)

    Odstráni viacero kľúčov na základe vzoru.

    Parameter:

    • $pattern: Reťazec – vzor kľúčov (napr. "users:*").

    Návratová hodnota: Žiadna (alebo počet odstránených kľúčov).

    Účel: Databaser volá túto metódu pri aktualizácii dát (napr. save() v ORM), aby invalidoval súvisiace cache záznamy.

    7.3. Implementácia vlastného CacheDrivera

    Tu je príklad jednoduchej implementácie cache drivera využívajúceho súborový systém:

    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); // Expirované, odstránime
            }
            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 neexpirovali.
    • set(): Uloží dáta do súboru s voliteľným TTL.
    • delete(): Odstráni konkrétny súbor.
    • deleteKeys(): Odstráni súbory podľa vzoru (používa fnmatch).

    7.4. Použitie CacheDrivera s Databaserom

    Po implementácii cache drivera ho nastavíte pomocou metódy cache():

    $cacheDriver = new FileCacheDriver('/tmp/myapp_cache');
    $DotApp->DB->cache($cacheDriver);
    
    // Príklad dotazu s cachingom
    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users')->where('age', '>', 18);
    })->execute(
        function ($result, $db, $debug) {
            echo "Výsledky (z cache alebo DB):\n";
            var_dump($result);
        }
    );
    

    Ako to funguje:

    • Databaser vygeneruje kľúč (napr. users:RAW:hash_dotazu).
    • Skontroluje cache cez get(). Ak nájde platné dáta, vráti ich bez dotazu na DB.
    • Ak dáta nie sú v cache, vykoná dotaz a uloží výsledok cez set() s TTL (predvolené 3600 sekúnd).
    • Pri aktualizácii (napr. save() v ORM) invaliduje súvisiace kľúče cez deleteKeys().

    7.5. Príklad s pokročilým cachingom

    Caching s ORM a invalidáciou:

    $cacheDriver = new FileCacheDriver();
    $DotApp->DB->cache($cacheDriver);
    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    
    $user = $users->first();
    $user->age = 40;
    $user->save(
        function ($result, $db, $debug) {
            echo "Používateľ uložený, cache invalidovaná.\n";
        }
    );
    

    Čo sa deje:

    • Prvý dotaz uloží Collection do cache.
    • Pri save() sa zavolá deleteKeys("users:ORM:*"), čím sa invalidujú všetky ORM záznamy pre users.

    7.6. Poznámky a tipy

    TTL: Nastavte rozumnú hodnotu TTL (napr. 3600 sekúnd) podľa potreby aplikácie.

    Vzory kľúčov: Databaser používa formát tabuľka:typ:hash, takže vzory ako "users:*" sú efektívne.

    Výkon: Pre produkčné prostredie zvážte rýchle cache systémy ako Redis namiesto súborov.

    Testovanie: Overte, či deleteKeys() správne invaliduje cache, aby ste predišli zastaraným dátam.

    8. Práca s Entity

    Entity je základnou stavebnou jednotkou ORM (Object-Relational Mapping) v Databaseri, ktorá reprezentuje jeden riadok v databázovej tabuľke ako objekt. Umožňuje jednoduchú manipuláciu s dátami, definovanie vzťahov a validáciu. Táto kapitola podrobne vysvetlí jej štruktúru, všetky dostupné metódy a ukáže ich praktické použitie.

    8.1. Čo je Entity?

    Entity je dynamicky generovaný objekt, ktorý mapuje riadok tabuľky na objekt s atribútmi zodpovedajúcimi stĺpcom. Vytvára sa automaticky, keď nastavíte typ návratu na 'ORM' a vykonáte dotaz vracajúci jeden riadok (napr. cez first()) alebo viac riadkov (v Collection cez all()). Poskytuje objektovo-orientovaný prístup k dátam, čím zjednodušuje prácu s databázou.

    Kľúčové vlastnosti:

    • Atribúty: Prístup k stĺpcom tabuľky ako k vlastnostiam objektu (napr. $entity->name).
    • Vzťahy: Podpora hasOne, hasMany, morphOne, morphMany pre prepojenie tabuliek.
    • Validácia: Možnosť definovať pravidlá pred uložením dát.
    • Lazy a Eager Loading: Vzťahy sa načítajú lenivo, s možnosťou prednačítania.

    8.2. Zloženie Entity

    Entity je anonymná trieda definovaná v createMysqliDriver() alebo createPdoDriver(). Obsahuje tieto hlavné prvky:

    Súkromné atribúty:

    • $attributes: Pole aktuálnych hodnôt stĺpcov (napr. ['id' => 1, 'name' => 'Jano']).
    • $originalAttributes: Pôvodné hodnoty pre sledovanie zmien.
    • $db: Referencia na inštanciu Databaseru.
    • $table: Názov tabuľky (odvodený z dotazu alebo odhadnutý).
    • $primaryKey: Primárny kľúč (predvolené 'id').
    • $rules: Pole validačných pravidiel.
    • $relations: Pole načítaných vzťahov (napr. hasMany).
    • $with: Pole vzťahov pre eager loading.
    • $morphRelations: Pole polymorfných vzťahov.

    Metódy:

    • with($relations): Nastaví vzťahy pre eager loading.
    • loadRelations(): Načíta vzťahy definované v $with.
    • setPrimaryKey($key): Nastaví primárny kľúč.
    • setRules(array $rules): Definuje validačné pravidlá.
    • save($callbackOk = null, $callbackError = null): Uloží zmeny do databázy.
    • hasOne($relatedTable, $foreignKey, $localKey = null, $callback = null): Definuje vzťah jeden k jednému.
    • hasMany($relatedTable, $foreignKey, $localKey = null, $callback = null): Definuje vzťah jeden k viacerým.
    • morphOne($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null): Definuje polymorfný vzťah jeden k jednému.
    • morphMany($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null): Definuje polymorfný vzťah jeden k viacerým.
    • toArray(): Vráti atribúty ako pole.

    8.3. Základné použitie Entity

    Získanie Entity:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    
    echo $user->name; // Prístup k atribútu
    $user->age = 26;  // Zmena atribútu
    $user->save();    // Uloženie zmien
    

    8.4. Podrobný prehľad metód Entity

    Tu je detailný popis všetkých verejne dostupných metód Entity:

    8.4.1. with($relations)

    Nastaví vzťahy, ktoré sa majú prednačítať (eager loading) namiesto lenivého načítania (lazy loading).

    Syntax: with($relations)

    Parameter:

    • $relations: Reťazec alebo pole názvov vzťahov (napr. 'hasMany:posts' alebo ['hasOne:profile', 'hasMany:posts']).

    Návratová hodnota: $this (reťaziteľné).

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->with('hasMany:posts');
    $user->loadRelations();
    foreach ($user->hasMany('posts', 'user_id') as $post) {
        echo $post->title . "\n";
    }
    
    8.4.2. loadRelations()

    Načíta všetky vzťahy definované v $with.

    Syntax: loadRelations()

    Návratová hodnota: Žiadna.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->with(['hasOne:profile', 'hasMany:posts']);
    $user->loadRelations();
    echo $user->hasOne('profiles', 'user_id')->bio;
    
    8.4.3. setPrimaryKey($key)

    Nastaví primárny kľúč namiesto predvoleného 'id'.

    Syntax: setPrimaryKey($key)

    Parameter:

    • $key: Reťazec – názov stĺpca (napr. 'user_id').

    Návratová hodnota: $this.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('user_id', '=', 1);
    })->first();
    $user->setPrimaryKey('user_id');
    $user->user_id = 2;
    $user->save();
    
    8.4.4. setRules(array $rules)

    Definuje validačné pravidlá pre atribúty pred uložením.

    Syntax: setRules(array $rules)

    Parameter:

    • $rules: Asociatívne pole (napr. 'name' => ['required', 'string', 'max:50']).

    Podporované pravidlá: required, integer, string, min:X, max:X, email.

    Návratová hodnota: $this.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->setRules([
        'name' => ['required', 'string', 'max:50'],
        'age' => ['integer', 'min:18']
    ]);
    $user->name = '';
    $user->save(
        null,
        function ($error, $db, $debug) {
            echo "Validácia zlyhala: {$error['error']}\n";
        }
    );
    
    8.4.5. save($callbackOk = null, $callbackError = null)

    Uloží zmeny v Entity do databázy (vkladá nový záznam alebo aktualizuje existujúci).

    Syntax: save($callbackOk = null, $callbackError = null)

    Parametre:

    • $callbackOk: Callback pri úspechu (function ($result, $db, $debug)).
    • $callbackError: Callback pri chybe (function ($error, $db, $debug)).

    Návratová hodnota: Žiadna.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->age = 27;
    $user->save(
        function ($result, $db, $debug) {
            echo "Používateľ uložený, SQL: {$debug['query']}\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    

    Vkladanie nového záznamu:

    $user = $DotApp->DB->newEntity();
    $user->table('users'); // Nastavenie tabuľky
    $user->name = 'Maria';
    $user->age = 30;
    $user->save(
        function ($result, $db, $debug) {
            echo "Nový používateľ s ID: {$db->insertedId()} vytvorený.\n";
        },
        function ($error, $db, $debug) {
            echo "Chyba: {$error['error']}\n";
        }
    );
    
    8.4.6. hasOne($relatedTable, $foreignKey, $localKey = null, $callback = null)

    Definuje vzťah jeden k jednému.

    Syntax: hasOne($relatedTable, $foreignKey, $localKey = null, $callback = null)

    Parametre:

    • $relatedTable: Názov súvisiacej tabuľky.
    • $foreignKey: Cudzí kľúč v súvisiacej tabuľke.
    • $localKey: Lokálny kľúč (predvolené 'id').
    • $callback: Closure na úpravu dotazu.

    Návratová hodnota: Entity alebo null.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $profile = $user->hasOne('profiles', 'user_id', 'id', function ($qb) {
        $qb->where('active', '=', 1);
    });
    echo $profile->bio;
    
    8.4.7. hasMany($relatedTable, $foreignKey, $localKey = null, $callback = null)

    Definuje vzťah jeden k viacerým.

    Syntax: hasMany($relatedTable, $foreignKey, $localKey = null, $callback = null)

    Parametre: Rovnaké ako hasOne.

    Návratová hodnota: Pole Entity objektov.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $posts = $user->hasMany('posts', 'user_id', null, function ($qb) {
        $qb->orderBy('created_at', 'DESC')->limit(2);
    });
    foreach ($posts as $post) {
        echo $post->title . "\n";
    }
    
    8.4.8. morphOne($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null)

    Definuje polymorfný vzťah jeden k jednému.

    Syntax: morphOne($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null)

    Parametre:

    • $typeField: Stĺpec s typom (napr. 'imageable_type').
    • $idField: Stĺpec s ID (napr. 'imageable_id').
    • $typeValue: Hodnota typu (napr. 'User').

    Návratová hodnota: Entity alebo null.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $image = $user->morphOne('images', 'imageable_type', 'imageable_id', 'User');
    echo $image->url;
    
    8.4.9. morphMany($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null)

    Definuje polymorfný vzťah jeden k viacerým.

    Syntax: morphMany($relatedTable, $typeField, $idField, $typeValue, $localKey = null, $callback = null)

    Parametre: Rovnaké ako morphOne.

    Návratová hodnota: Pole Entity objektov.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $images = $user->morphMany('images', 'imageable_type', 'imageable_id', 'User');
    foreach ($images as $image) {
        echo $image->url . "\n";
    }
    
    8.4.10. toArray()

    Vráti atribúty Entity ako asociatívne pole.

    Syntax: toArray()

    Návratová hodnota: Pole s aktuálnymi hodnotami $attributes.

    Príklad:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $data = $user->toArray();
    var_dump($data); // ['id' => 1, 'name' => 'Jano', 'age' => 26]
    

    8.5. Praktické príklady

    Úprava a uloženie s validáciou:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->setRules(['name' => 'required', 'age' => 'integer']);
    $user->name = 'Peter';
    $user->age = 28;
    $user->save(
        function ($result, $db, $debug) {
            echo "Používateľ uložený: {$user->name}, vek: {$user->age}\n";
        }
    );
    

    Eager Loading viacerých vzťahov:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first()->with(['hasOne:profile', 'hasMany:posts']);
    $user->loadRelations();
    echo $user->hasOne('profiles', 'user_id')->bio . "\n";
    foreach ($user->hasMany('posts', 'user_id') as $post) {
        echo $post->title . "\n";
    }
    

    Polymorfný vzťah s filtrom:

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $recentImages = $user->morphMany('images', 'imageable_type', 'imageable_id', 'User', null, function ($qb) {
        $qb->orderBy('created_at', 'DESC')->limit(3);
    });
    foreach ($recentImages as $image) {
        echo "Najnovší obrázok: {$image->url}\n";
    }
    

    8.6. Poznámky

    Lazy vs. Eager Loading: Pre časté prístupy k vzťahom vždy používajte with() a loadRelations(), aby ste znížili počet dotazov.

    Validácia: Pravidlá kontrolujte pred save(), inak sa uložia nevalidné dáta.

    Prispôsobenie: Callbacky v save() a vzťahoch umožňujú flexibilitu bez potreby rozširovania triedy.

    9. Práca s Collection

    Collection je trieda v ORM (Object-Relational Mapping) vrstve Databaseru, ktorá slúži na správu viacerých Entity objektov – teda súbor riadkov z databázy. Umožňuje iteráciu, filtrovanie, transformáciu a hromadné operácie nad dátami. Táto kapitola podrobne vysvetlí jej štruktúru, všetky dostupné metódy a ukáže ich praktické použitie s HTML výstupmi.

    9.1. Čo je Collection?

    Collection je dynamicky generovaný objekt, ktorý uchováva zoznam Entity objektov vrátených z dotazu, keď nastavíte typ návratu na 'ORM' a použijete metódu all() alebo získate vzťah typu hasMany/morphMany. Poskytuje objektovo-orientovaný prístup k viacerým záznamom naraz, čím zjednodušuje manipuláciu s dátami.

    Kľúčové vlastnosti:

    • Iterovateľnosť: Implementuje IteratorAggregate pre jednoduchú iteráciu cez foreach.
    • Hromadné operácie: Podpora metód ako saveAll() na uloženie zmien vo všetkých entitách.
    • Filtrovanie a transformácia: Metódy ako filter(), map(), pluck() na prácu s dátami.
    • Vzťahy: Možnosť eager loadingu vzťahov pre optimalizáciu dotazov.

    9.2. Zloženie Collection

    Collection je trieda definovaná v Databaseri. Obsahuje tieto hlavné prvky:

    Súkromné atribúty:

    • $items: Pole Entity objektov (napr. [0 => Entity, 1 => Entity]).
    • $db: Referencia na inštanciu Databaseru.
    • $with: Pole vzťahov pre eager loading (napr. ['hasMany:posts']).

    Metódy:

    • with($relations): Nastaví vzťahy pre eager loading.
    • loadRelations(): Načíta vzťahy definované v $with.
    • all(): Vráti pole všetkých Entity objektov.
    • first(): Vráti prvú Entity alebo null.
    • filter($callback): Filtruje entity podľa podmienky.
    • map($callback): Transformuje entity pomocou callbacku.
    • pluck($field): Extrahuje hodnoty konkrétneho stĺpca.
    • saveAll($callbackOk = null, $callbackError = null): Uloží zmeny vo všetkých entitách.
    • toArray(): Vráti kolekciu ako pole polí.
    • count(): Vráti počet entít v kolekcii.

    9.3. Základné použitie Collection

    Získanie Collection:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    
    foreach ($users as $user) {
        echo "Meno: {$user->name}, Vek: {$user->age}\n";
    }
        

    9.4. Podrobný prehľad metód Collection

    Tu je detailný popis všetkých verejne dostupných metód Collection:

    9.4.1. with($relations)

    Nastaví vzťahy, ktoré sa majú prednačítať (eager loading) namiesto lenivého načítania.

    Syntax: with($relations)

    Parameter:

    • $relations: Reťazec alebo pole názvov vzťahov (napr. 'hasMany:posts' alebo ['hasOne:profile', 'hasMany:posts']).

    Návratová hodnota: $this (reťaziteľné).

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all()->with('hasMany:posts');
    $users->loadRelations();
    
    $html = "";
    foreach ($users as $user) {
        $posts = implode(', ', array_map(fn($post) => $post->title, $user->hasMany('posts', 'user_id')));
        $html .= "";
    }
    $html .= "
    MenoPríspevky
    {$user->name}{$posts}
    "; echo $html;

    Výstup:

    
    
    MenoPríspevky
    JanoPríspevok 1, Príspevok 2
    MariaNovinka
    9.4.2. loadRelations()

    Načíta všetky vzťahy definované v $with.

    Syntax: loadRelations()

    Návratová hodnota: Žiadna.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all()->with(['hasOne:profile']);
    $users->loadRelations();
    
    $html = "
      "; foreach ($users as $user) { $html .= "
    • {$user->name} - Profil: {$user->hasOne('profiles', 'user_id')->bio}
    • "; } $html .= "
    "; echo $html;

    Výstup:

    
    
    • Jano - Profil: Milovník technológií
    • Maria - Profil: Cestovateľka
    9.4.3. all()

    Vráti pole všetkých Entity objektov v kolekcii.

    Syntax: all()

    Návratová hodnota: Pole Entity objektov.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $allUsers = $users->all();
    
    $html = "";
    foreach ($allUsers as $user) {
        $html .= "";
    }
    $html .= "
    IDMeno
    {$user->id}{$user->name}
    "; echo $html;

    Výstup:

    
    
    IDMeno
    1Jano
    2Maria
    9.4.4. first()

    Vráti prvú Entity z kolekcie alebo null, ak je prázdna.

    Syntax: first()

    Návratová hodnota: Entity alebo null.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $firstUser = $users->first();
    
    $html = $firstUser ? "

    Prvý používateľ: {$firstUser->name}

    " : "

    Žiadni používatelia

    "; echo $html;

    Výstup:

    
    

    Prvý používateľ: Jano

    9.4.5. filter($callback)

    Filtruje kolekciu na základe podmienky definovanej v callbacku.

    Syntax: filter($callback)

    Parameter:

    • $callback: Funkcia (function ($entity)) vracajúca true pre zachovanie položky.

    Návratová hodnota: Nová inštancia Collection.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $adults = $users->filter(function ($user) {
        return $user->age >= 18;
    });
    
    $html = "
      "; foreach ($adults as $adult) { $html .= "
    • {$adult->name} ({$adult->age})
    • "; } $html .= "
    "; echo $html;

    Výstup:

    
    
    • Jano (25)
    • Maria (30)
    9.4.6. map($callback)

    Transformuje každú Entity v kolekcii pomocou callbacku.

    Syntax: map($callback)

    Parameter:

    • $callback: Funkcia (function ($entity)) vracajúca upravenú Entity.

    Návratová hodnota: Nová inštancia Collection.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $updated = $users->map(function ($user) {
        $user->age += 1;
        return $user;
    });
    
    $html = "";
    foreach ($updated as $user) {
        $html .= "";
    }
    $html .= "
    MenoNový vek
    {$user->name}{$user->age}
    "; echo $html;

    Výstup:

    
    
    MenoNový vek
    Jano26
    Maria31
    9.4.7. pluck($field)

    Extrahuje hodnoty konkrétneho atribútu zo všetkých Entity v kolekcii.

    Syntax: pluck($field)

    Parameter:

    • $field: Názov atribútu (napr. 'name').

    Návratová hodnota: Pole hodnôt.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $names = $users->pluck('name');
    
    $html = "
      "; foreach ($names as $name) { $html .= "
    • {$name}
    • "; } $html .= "
    "; echo $html;

    Výstup:

    
    
    • Jano
    • Maria
    9.4.8. saveAll($callbackOk = null, $callbackError = null)

    Uloží zmeny vo všetkých Entity v kolekcii do databázy.

    Syntax: saveAll($callbackOk = null, $callbackError = null)

    Parametre:

    • $callbackOk: Callback pri úspechu (function ($result, $db, $debug)).
    • $callbackError: Callback pri chybe (function ($error, $db, $debug)).

    Návratová hodnota: Žiadna.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $updated = $users->map(function ($user) {
        $user->age += 1;
        return $user;
    });
    $updated->saveAll(
        function ($result, $db, $debug) {
            $html = "

    Zmeny uložené, ovplyvnených riadkov: {$db->affectedRows()}

    "; echo $html; }, function ($error, $db, $debug) { echo "Chyba: {$error['error']}\n"; } ); $html = ""; foreach ($updated as $user) { $html .= ""; } $html .= "
    MenoNový vek
    {$user->name}{$user->age}
    "; echo $html;

    Výstup:

    
    

    Zmeny uložené, ovplyvnených riadkov: 2

    MenoNový vek
    Jano26
    Maria31
    9.4.9. toArray()

    Vráti kolekciu ako pole asociatívnych polí.

    Syntax: toArray()

    Návratová hodnota: Pole polí.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $data = $users->toArray();
    
    $html = "";
    foreach ($data as $user) {
        $html .= "";
    }
    $html .= "
    IDMenoVek
    {$user['id']}{$user['name']}{$user['age']}
    "; echo $html;

    Výstup:

    
    
    IDMenoVek
    1Jano25
    2Maria30
    9.4.10. count()

    Vráti počet Entity objektov v kolekcii.

    Syntax: count()

    Návratová hodnota: Celé číslo.

    Príklad:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $count = $users->count();
    
    $html = "

    Počet používateľov: {$count}

    "; echo $html;

    Výstup:

    
    

    Počet používateľov: 2

    9.5. Praktické príklady

    Filtrovanie a hromadné uloženie:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $adults = $users->filter(function ($user) {
        return $user->age >= 18;
    })->map(function ($user) {
        $user->status = 'Dospelý';
        return $user;
    })->saveAll(
        function ($result, $db, $debug) {
            echo "Dospelí používatelia aktualizovaní.\n";
        }
    );
    
    $html = "";
    foreach ($adults as $user) {
        $html .= "";
    }
    $html .= "
    MenoStav
    {$user->name}{$user->status}
    "; echo $html;

    Výstup:

    
    
    MenoStav
    JanoDospelý
    MariaDospelý

    Eager Loading vzťahov:

    
    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all()->with(['hasMany:posts', 'hasOne:profile']);
    $users->loadRelations();
    
    $html = "";
    foreach ($users as $user) {
        $posts = implode(', ', array_map(fn($post) => $post->title, $user->hasMany('posts', 'user_id')));
        $html .= "";
    }
    $html .= "
    MenoProfilPríspevky
    {$user->name}{$user->hasOne('profiles', 'user_id')->bio}{$posts}
    "; echo $html;

    Výstup:

    
    
    MenoProfilPríspevky
    JanoMilovník technológiíPríspevok 1, Príspevok 2
    MariaCestovateľkaNovinka

    9.6. Poznámky

    • Výkon: Pri veľkých kolekciách zvážte použitie limit() v dotaze, aby ste znížili zaťaženie pamäte.
    • Eager Loading: Používajte with() a loadRelations() na zníženie počtu dotazov pri práci so vzťahmi.
    • Flexibilita: Reťazenie metód ako filter() a map() umožňuje plynulé spracovanie dát.

    10. Migrácie

    Migrácie v Databaseri umožňujú definovať a spravovať štruktúru databázy pomocou kódu. Používajú sa na vytváranie, úpravu alebo odstraňovanie tabuliek a ich stĺpcov, pričom podporujú transakcie pre hromadné operácie. Táto kapitola podrobne popisuje prácu s migráciami, vrátane ich definície, dostupných metód a praktických príkladov.

    10.2. Čo sú migrácie?

    Migrácie sú nástroj na správu databázovej schémy, ktorý umožňuje programovo definovať štruktúru tabuliek a ich vzťahov. V Databaseri sa migrácie realizujú cez SchemaBuilder a môžu byť zabalené do transakcií pomocou metódy transact(). Hlavné výhody:

    • Automatizácia: Zmena databázy je súčasťou kódu a môže byť verzionovaná.
    • Transakcie: Hromadné operácie sú bezpečné a reverzibilné pri chybe.
    • Multiplatformovosť: Podpora rôznych driverov (MySQLi, PDO) s prispôsobením syntaxe.

    10.3. Základné princípy migrácií

    Migrácie v Databaseri fungujú na základe týchto princípov:

    • SchemaBuilder: Používa sa na definovanie tabuliek a stĺpcov (napr. id(), string(), foreign()).
    • Metóda migrate(): Slúži na spúšťanie migrácií ('up' pre vytvorenie, 'down' pre rollback).
    • Transakcie: Hromadné migrácie sa vykonávajú cez transact(), kde sa viaceré operácie spúšťajú ako jeden celok.
    • Podpora driverov: MySQLi aj PDO prispôsobujú syntax podľa typu databázy (napr. MySQL, PostgreSQL, SQLite).

    10.4. Použitie migrácií

    Základné použitie migrácií zahŕňa definovanie štruktúry databázy a jej aplikáciu. Príklad vytvorenia tabuľky:

    
    $DotApp->DB->schema(function ($schema) {
        $schema->createTable('users', function ($table) {
            $table->id();
            $table->string('name');
        });
    }, function ($result, $db, $debug) {
        echo "Tabuľka 'users' bola úspešne vytvorená.\n";
    });
        

    Výstup:

    
    Tabuľka 'users' bola úspešne vytvorená.
        

    Hromadná migrácia s transakciou (viac tabuliek):

    
    $DotApp->DB->transact(function ($db) {
        $db->q(function ($qb) {
            $qb->createTable('users', function ($schema) {
                $schema->id();
                $schema->string('name');
            });
        })->execute();
        $db->q(function ($qb) {
            $qb->createTable('posts', function ($schema) {
                $schema->id();
                $schema->integer('user_id');
                $schema->foreign('user_id');
            });
        })->execute();
    }, function ($result, $db, $debug) {
        echo "Migrácia úspešná.\n";
    });
        

    Výstup:

    
    Migrácia úspešná.
        

    10.5. Dostupné metódy pre migrácie

    Databaser ponúka nasledujúce metódy pre prácu s migráciami:

    10.5.1. schema($callback, $success, $error)

    Definuje a vykoná jednu migračnú operáciu (napr. vytvorenie tabuľky).

    Syntax: schema(callable $callback, callable $success = null, callable $error = null)

    Parametre:

    • $callback: Closure, ktorá definuje operáciu cez SchemaBuilder.
    • $success: Callback pri úspechu.
    • $error: Callback pri chybe.

    Príklad:

    
    $DotApp->DB->schema(function ($schema) {
        $schema->createTable('users', function ($table) {
            $table->id();
            $table->string('email', 100);
        });
    }, function ($result, $db, $debug) {
        echo "

    Tabuľka vytvorená.

    "; });

    Výstup:

    
    

    Tabuľka vytvorená.

    10.5.2. migrate($direction, $success, $error)

    Spúšťa migráciu v smere 'up' (vytvorenie) alebo 'down' (odstránenie).

    Syntax: migrate(string $direction = 'up', callable $success = null, callable $error = null)

    Parametre:

    • $direction: 'up' alebo 'down'.
    • $success: Callback pri úspechu.
    • $error: Callback pri chybe.

    Príklad:

    
    $DotApp->DB->migrate('up', function ($result, $db, $debug) {
        echo "

    Migrácia 'up' dokončená.

    "; });

    Výstup:

    
    

    Migrácia 'up' dokončená.

    10.5.3. transact($operations, $success, $error)

    Spúšťa hromadnú migráciu v rámci transakcie.

    Syntax: transact(callable $operations, callable $success = null, callable $error = null)

    Parametre:

    • $operations: Closure s viacerými migračnými operáciami.
    • $success: Callback pri úspechu.
    • $error: Callback pri chybe.

    Príklad:

    
    $DotApp->DB->transact(function ($db) {
        $db->q(function ($qb) {
            $qb->createTable('comments', function ($schema) {
                $schema->id();
                $schema->integer('post_id');
            });
        })->execute();
    }, function ($result, $db, $debug) {
        echo "

    Hromadná migrácia úspešná.

    "; });

    Výstup:

    
    

    Hromadná migrácia úspešná.

    10.5.4. SchemaBuilder::createTable($table, $callback)

    Vytvorí novú tabuľku s definovanou štruktúrou.

    Syntax: createTable(string $table, callable $callback)

    Príklad:

    
    $DotApp->DB->schema(function ($schema) {
        $schema->createTable('products', function ($table) {
            $table->id();
            $table->string('name');
            $table->decimal('price', 8, 2);
        });
    });
        
    10.5.5. SchemaBuilder::alterTable($table, $callback)

    Upraví existujúcu tabuľku (napr. pridá stĺpec).

    Syntax: alterTable(string $table, callable $callback)

    Príklad:

    
    $DotApp->DB->schema(function ($schema) {
        $schema->alterTable('users', function ($table) {
            $table->addColumn('age', 'INT', null, true);
        });
    });
        
    10.5.6. SchemaBuilder::dropTable($table)

    Odstráni tabuľku.

    Syntax: dropTable(string $table)

    Príklad:

    
    $DotApp->DB->schema(function ($schema) {
        $schema->dropTable('users');
    });
        

    10.6. Praktické príklady

    Vytvorenie tabuliek s cudzím kľúčom:

    
    $DotApp->DB->transact(function ($db) {
        $db->q(function ($qb) {
            $qb->createTable('users', function ($schema) {
                $schema->id();
                $schema->string('username');
            });
        })->execute();
        $db->q(function ($qb) {
            $qb->createTable('posts', function ($schema) {
                $schema->id();
                $schema->string('title');
                $schema->integer('user_id');
                $schema->foreign('user_id', 'id', 'users');
            });
        })->execute();
    }, function ($result, $db, $debug) {
        echo "

    Migrácia tabuliek dokončená.

    "; });

    Výstup:

    
    

    Migrácia tabuliek dokončená.

    Úprava tabuľky (pridanie stĺpca):

    
    $DotApp->DB->schema(function ($schema) {
        $schema->alterTable('users', function ($table) {
            $table->string('email', 100);
        });
    }, function ($result, $db, $debug) {
        echo "

    Email stĺpec pridaný.

    "; });

    Výstup:

    
    

    Email stĺpec pridaný.

    Rollback migrácie:

    
    $DotApp->DB->migrate('down', function ($result, $db, $debug) {
        echo "

    Tabuľka 'migrations' odstránená.

    "; });

    Výstup:

    
    

    Tabuľka 'migrations' odstránená.

    10.7. Poznámky

    • Transakcie: Používajte transact() pre hromadné migrácie, aby ste zaistili konzistenciu.
    • Podpora driverov: Syntax sa prispôsobuje podľa drivera (napr. MySQL vs. SQLite), ale niektoré funkcie (napr. ON UPDATE v Oracle) nemusia byť plne podporované.
    • Obmedzenia: Metóda migrate() aktuálne podporuje len základné operácie; pre komplexné migrácie použite schema() alebo transact().

    11. Prípadová štúdia: E-shop s ORM

    Táto kapitola predstavuje praktickú prípadovú štúdiu, ktorá ukazuje, ako použiť Databaser a jeho ORM na vytvorenie jednoduchého e-shopu. Navrhneme databázovú štruktúru, vytvoríme tabuľky, naplníme ich dátami a ukážeme, ako pracovať s dátami pomocou Entity a Collection. Súčasťou budú aj error callbacky na správu chýb, aby sa používatelia naučili robustné techniky.

    11.1. Návrh databázovej štruktúry

    Pre e-shop navrhneme tieto tabuľky:

    • users: Používatelia (zákazníci a administrátori).
    • products: Produkty v ponuke.
    • product_descriptions: Popisy produktov (jeden produkt môže mať viac popisov, napr. v rôznych jazykoch).
    • orders: Objednávky.
    • order_items: Položky v objednávkach (prepojenie produktov a objednávok).
    SQL na vytvorenie tabuliek

    Používateľ môže tieto príkazy skopírovať a spustiť v MySQL databáze:

    -- Tabuľka používateľov
    CREATE TABLE users (
        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
    );
    
    -- Tabuľka produktov
    CREATE TABLE 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
    );
    
    -- Tabuľka popisov produktov
    CREATE TABLE 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 products(id) ON DELETE CASCADE
    );
    
    -- Tabuľka objednávok
    CREATE TABLE orders (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        user_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 (user_id) REFERENCES users(id) ON DELETE CASCADE
    );
    
    -- Tabuľka položiek objednávok
    CREATE TABLE 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 orders(id) ON DELETE CASCADE,
        FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
    );
    
    SQL na naplnenie dát

    Tieto príkazy naplnia tabuľky ukážkovými dátami:

    -- Používatelia
    INSERT INTO users (name, email, role) VALUES
    ('Jano Novák', 'jano@example.com', 'customer'),
    ('Admin Peter', 'admin@example.com', 'admin');
    
    -- Produkty
    INSERT INTO products (name, price, stock) VALUES
    ('Tričko biele', 15.99, 50),
    ('Topánky čierne', 49.99, 20),
    ('Bunda zimná', 89.99, 10);
    
    -- Popisy produktov
    INSERT INTO product_descriptions (product_id, language, description) VALUES
    (1, 'sk', 'Pohodlné biele tričko z bavlny.'),
    (1, 'en', 'Comfortable white cotton t-shirt.'),
    (2, 'sk', 'Elegantné čierne topánky na každú príležitosť.'),
    (3, 'sk', 'Teplá zimná bunda s kapucňou.');
    
    -- Objednávky
    INSERT INTO orders (user_id, total_price, status) VALUES
    (1, 65.98, 'pending'),
    (1, 89.99, 'shipped');
    
    -- Položky objednávok
    INSERT INTO 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

    Predpokladáme, že Databaser je nakonfigurovaný a pripojený k databáze (viď kapitola 2). Použijeme ORM na prácu s týmito tabuľkami, vrátane správy chýb.

    11.2.1. Získanie používateľa a jeho objednávok

    Ukážeme, ako získať používateľa s jeho objednávkami a položkami.

    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first(
        function ($user, $db, $debug) {
            $user->with(['hasMany:orders']);
            $user->loadRelations(
                function ($result, $db, $debug) {
                    echo "Používateľ: {$user->name} ({$user->email})\n";
                    foreach ($user->hasMany('orders', 'user_id') as $order) {
                        echo "Objednávka #{$order->id}, Celková cena: {$order->total_price}, Stav: {$order->status}\n";
                    }
                },
                function ($error, $db, $debug) {
                    echo "Chyba pri načítaní vzťahov: {$error['error']}\n";
                }
            );
        },
        function ($error, $db, $debug) {
            echo "Chyba pri načítaní používateľa: {$error['error']}\n";
        }
    );
    
    11.2.2. Pridanie nového produktu s popisom

    Vytvoríme nový produkt a pridáme mu popis v slovenčine.

    $DotApp->DB->transact(function ($db) {
        $product = $db->newEntity();
        $product->table('products');
        $product->name = 'Šál zelený';
        $product->price = 19.99;
        $product->stock = 30;
        $product->save(
            function ($result, $db, $debug) {
                $productId = $db->insertedId();
                $description = $db->newEntity();
                $description->table('product_descriptions');
                $description->product_id = $productId;
                $description->language = 'sk';
                $description->description = 'Teplý zelený šál na zimu.';
                $description->save(
                    null,
                    function ($error, $db, $debug) {
                        echo "Chyba pri ukladaní popisu: {$error['error']}\n";
                    }
                );
            },
            function ($error, $db, $debug) {
                echo "Chyba pri ukladaní produktu: {$error['error']}\n";
            }
        );
    }, function ($result, $db, $debug) {
        echo "Produkt 'Šál zelený' pridaný s popisom.\n";
    }, function ($error, $db, $debug) {
        echo "Chyba v transakcii: {$error['error']}\n";
    });
    
    11.2.3. Zobrazenie produktu s popismi

    Získame produkt a jeho popisy pomocou hasMany.

    $product = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'products')->where('id', '=', 1);
    })->first(
        function ($product, $db, $debug) {
            $product->with('hasMany:product_descriptions');
            $product->loadRelations(
                function ($result, $db, $debug) {
                    echo "Produkt: {$product->name}, Cena: {$product->price} €\n";
                    foreach ($product->hasMany('product_descriptions', 'product_id') as $desc) {
                        echo "Popis ({$desc->language}): {$desc->description}\n";
                    }
                },
                function ($error, $db, $debug) {
                    echo "Chyba pri načítaní popisov: {$error['error']}\n";
                }
            );
        },
        function ($error, $db, $debug) {
            echo "Chyba pri načítaní produktu: {$error['error']}\n";
        }
    );
    
    11.2.4. Vytvorenie objednávky

    Vytvoríme novú objednávku pre používateľa a pridáme položky.

    $DotApp->DB->transact(function ($db) {
        $order = $db->newEntity();
        $order->table('orders');
        $order->user_id = 1;
        $order->total_price = 35.98;
        $order->status = 'pending';
        $order->save(
            function ($result, $db, $debug) {
                $orderId = $db->insertedId();
                $item = $db->newEntity();
                $item->table('order_items');
                $item->order_id = $orderId;
                $item->product_id = 1;
                $item->quantity = 2;
                $item->price = 15.99;
                $item->save(
                    function ($result, $db, $debug) {
                        $product = $db->return('ORM')->q(function ($qb) {
                            $qb->select('*', 'products')->where('id', '=', 1);
                        })->first(
                            function ($product, $db, $debug) {
                                $product->stock -= 2;
                                $product->save(
                                    null,
                                    function ($error, $db, $debug) {
                                        echo "Chyba pri aktualizácii skladu: {$error['error']}\n";
                                    }
                                );
                            },
                            function ($error, $db, $debug) {
                                echo "Chyba pri načítaní produktu: {$error['error']}\n";
                            }
                        );
                    },
                    function ($error, $db, $debug) {
                        echo "Chyba pri ukladaní položky: {$error['error']}\n";
                    }
                );
            },
            function ($error, $db, $debug) {
                echo "Chyba pri ukladaní objednávky: {$error['error']}\n";
            }
        );
    }, function ($result, $db, $debug) {
        echo "Objednávka vytvorená a sklad aktualizovaný.\n";
    }, function ($error, $db, $debug) {
        echo "Chyba v transakcii: {$error['error']}\n";
    });
    
    11.2.5. Zobrazenie objednávky s položkami

    Získame objednávku a jej položky.

    $order = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'orders')->where('id', '=', 1);
    })->first(
        function ($order, $db, $debug) {
            $order->with('hasMany:order_items');
            $order->loadRelations(
                function ($result, $db, $debug) {
                    echo "Objednávka #{$order->id}, Celková cena: {$order->total_price}, Stav: {$order->status}\n";
                    foreach ($order->hasMany('order_items', 'order_id') as $item) {
                        $db->return('ORM')->q(function ($qb) use ($item) {
                            $qb->select('name', 'products')->where('id', '=', $item->product_id);
                        })->first(
                            function ($product, $db, $debug) use ($item) {
                                echo "Položka: {$product->name}, Množstvo: {$item->quantity}, Cena: {$item->price} €\n";
                            },
                            function ($error, $db, $debug) {
                                echo "Chyba pri načítaní produktu: {$error['error']}\n";
                            }
                        );
                    }
                },
                function ($error, $db, $debug) {
                    echo "Chyba pri načítaní položiek: {$error['error']}\n";
                }
            );
        },
        function ($error, $db, $debug) {
            echo "Chyba pri načítaní objednávky: {$error['error']}\n";
        }
    );
    

    11.3. Použitie validácie

    Pridáme validáciu pre produkt pred uložením.

    $product = $DotApp->DB->newEntity();
    $product->table('products');
    $product->setRules([
        'name' => ['required', 'string', 'max:100'],
        'price' => ['required', 'numeric', 'min:0'],
        'stock' => ['integer', 'min:0']
    ]);
    $product->name = 'Dlhý názov produktu, ktorý presahuje 100 znakov a je neplatný kvôli maximálnej dĺžke';
    $product->price = -5;
    $product->stock = 10;
    $product->save(
        function ($result, $db, $debug) {
            echo "Produkt úspešne uložený.\n";
        },
        function ($error, $db, $debug) {
            echo "Validácia zlyhala: {$error['error']}\n";
        }
    );
    

    11.4. Poznámky k štúdii

    Transakcie: Použitie transact() zaisťuje konzistenciu; error callbacky informujú o zlyhaniach.

    Vzťahy: hasMany a eager loading (with()) zjednodušujú prácu s dátami, s chybovou kontrolou.

    Validácia: Pravidlá chránia pred neplatnými dátami, s jasným hlásením chýb.

    Chybová správa: error callbacky umožňujú používateľovi reagovať na problémy (napr. logovanie, upozornenia).

    12. Tipy a triky

    V tejto kapitole ponúkneme praktické rady, ako efektívne využívať Databaser v DotApp Frameworku. Zameriame sa na optimalizáciu dotazov, zabezpečenie bezpečnosti a možnosti rozšírenia.

    12.1. Optimalizácia dotazov

    Efektívne dotazy sú kľúčom k rýchlej aplikácii. Tu je niekoľko tipov:

    • Vyberajte len potrebné stĺpce: Namiesto select('*', 'users') používajte konkrétne stĺpce, napr. select('id, name', 'users'). Znižuje to objem prenesených dát.
    $DotApp->DB->q(function ($qb) {
        $qb->select('id, name', 'users')->where('age', '>', 18);
    })->execute(
        function ($result, $db, $debug) {
            var_dump($result);
        }
    );
    
    • Využívajte indexy: Pri častých filtroch v where() (napr. id, age) pridajte indexy v databáze pomocou schema():
    $DotApp->DB->schema(function ($schema) {
        $schema->alterTable('users', function ($table) {
            $table->index('age');
        });
    });
    
    • Limitujte výsledky: Používajte limit() a offset() na stránkovanie, aby ste neťahali zbytočne veľké dátové sady.
    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users')->limit(10)->offset(20);
    })->all();
    
    • Cache pri opakovaných dotazoch: Ak máte cache driver implementovaný, využite ho na ukladanie výsledkov:
    $DotApp->DB->cache($myCacheDriver)->q(function ($qb) {
        $qb->select('*', 'users');
    })->execute(
        function ($result, $db, $debug) {
            echo "Výsledky z cache alebo DB: ";
            var_dump($result);
        }
    );
    

    12.2. Bezpečnosť (SQL Injection prevencia)

    Databaser je navrhnutý s ohľadom na bezpečnosť, no je dobré poznať osvedčené postupy:

    • Vždy používajte prepared statements: QueryBuilder automaticky escapuje hodnoty, takže nikdy nevkladajte premenné priamo do dotazu.

    Správne:

    $DotApp->DB->q(function ($qb) {
        $qb->select('*', 'users')->where('name', '=', 'Jano');
    })->execute();
    

    Nesprávne:

    $name = "Jano'; DROP TABLE users; --";
    $DotApp->DB->q(function ($qb) use ($name) {
        $qb->raw("SELECT * FROM users WHERE name = '$name'");
    })->execute(); // Nebezpečné!
    
    • Surové dotazy s RAW: Ak používate raw(), vždy zadávajte hodnoty cez bindings:
    $DotApp->DB->q(function ($qb) {
        $qb->raw('SELECT * FROM users WHERE age > ?', [18]);
    })->execute();
    
    • Validačné pravidlá v ORM: Pri ukladaní dát cez Entity nastavte pravidlá:
    $user = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users')->where('id', '=', 1);
    })->first();
    $user->setRules(['name' => 'required|string|max:50']);
    $user->name = 'Jano';
    $user->save();
    

    12.3. Rozšírenie Databaseru o vlastné drivery

    Databaser umožňuje pridať vlastné databázové drivery pomocou metódy addDriver(). Napríklad, ak chcete podporu pre neštandardnú databázu:

    Príklad vlastného drivera (simulovaný):

    $DotApp->DB->addDriver('custom', 'execute', function ($success, $error) use ($DotApp) {
        $queryData = $this->qb->getQuery();
        $query = $queryData['query'];
        $bindings = $queryData['bindings'];
        // Simulácia vlastnej logiky
        $result = ['id' => 1, 'name' => 'Test']; // Napr. volanie API alebo iného systému
        if ($result) {
            if (is_callable($success)) {
                $success($result, $this, ['query' => $query, 'bindings' => $bindings]);
            }
        } else {
            if (is_callable($error)) {
                $error(['error' => 'Custom driver failed'], $this, ['query' => $query]);
            }
        }
    });
    
    // Použitie
    $DotApp->DB->driver('custom')->q(function ($qb) {
        $qb->select('*', 'users');
    })->execute(
        function ($result, $db, $debug) {
            echo "Výsledok z vlastného drivera: ";
            var_dump($result);
        }
    );
    

    Kroky:

    • Definujte logiku pre kľúčové operácie (select_db, execute, atď.).
    • Pridajte ich cez addDriver().
    • Prepínajte na driver pomocou driver('custom').

    Toto umožňuje integrovanie Databaseru s akýmkoľvek databázovým systémom, pokiaľ implementujete potrebné metódy.

    12.4. Eager Loading vzťahov v ORM

    Pri práci s ORM môžete optimalizovať načítanie vzťahov pomocou with():

    Príklad:

    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all()->with('hasMany:posts');
    foreach ($users as $user) {
        foreach ($user->hasMany('posts', 'user_id') as $post) {
            echo "Používateľ: {$user->name}, Príspevok: {$post->title}\n";
        }
    }
    

    Výhoda: Zníži počet dotazov z N+1 na jeden hromadný dotaz.

    12.5. Hromadné operácie s Collection

    Collection v ORM ponúka metódy na hromadné spracovanie:

    $users = $DotApp->DB->return('ORM')->q(function ($qb) {
        $qb->select('*', 'users');
    })->all();
    $users->map(function ($user) {
        $user->age += 1;
        return $user;
    })->saveAll(
        function ($result, $db, $debug) {
            echo "Všetci používatelia boli aktualizovaní.\n";
        }
    );
    

    Metódy: filter(), map(), pluck(), saveAll().