Prejsť na obsah

Šablónový systém

DotApp vykresľuje HTML fasádou Renderer a malou sadou direktív {{ … }}. Šablóny sú PHP súbory, ktoré vlastní modul. Kontroléry odovzdávajú dáta cez setViewVar(). Neexistuje samostatný runtime šablónového jazyka: direktívy sa skompilujú do PHP a výsledok vyhodnotí sandbox.

Živé stránky na tomto webe sa riadia tými istými pravidlami. Hello World: /helloworld. Bezpečné formuláre: /documentation/examples/run/forms2. Podrobný návod je Sprievodca krok za krokom.

1.1 Čo je šablónový systém?

Každý modul drží prezentáciu v app/modules/{Module}/views/. View je celá stránka alebo obal. Layout je znovupoužiteľný fragment (hlavička, riadok zoznamu, nadpis). Kontrolér vyberie súbory, priradí premenné a vráti HTML reťazec z renderView() alebo renderLayout().

Hodnotu vypíšte cez {{ var: $title }}. Je to jediná podporovaná syntax výpisu. {{ $title }} nie je direktíva a premennú nevypíše.

1.2 Súbory a priečinky

Typ Cesta Vyberá sa cez
View app/modules/{Module}/views/{name}.view.php setView('name')
Layout app/modules/{Module}/views/layouts/{path}.layout.php setLayout('path') alebo {{ layout:path }}
Iný modul Rovnaká štruktúra v danom module setView('Shop:home'), {{ layout: Shop:partials/header }}
Základný layout app/parts/views/layouts/ iba {{ baselayout:name }}
Assets app/modules/{Module}/assets/... /assets/modules/{Module}/...

{{ layout:partials/header }} načíta views/layouts/partials/header.layout.php. Adresár layouts je už koreňom — nepíšte layout:layouts/header. Vnorené include sa zastavia pri hĺbke 20.

1.3 Fasáda Renderer

Renderer vytvoríte cez Renderer::new(), nasmerujete ho na modul a nastavíte view. Pomenované inštancie (Renderer::new('docs')) sa znovu používajú ako singletony. Pre stránku začnite čerstvý reťazec cez Renderer::new().


use Dotsystems\App\Parts\Logger;
use Dotsystems\App\Parts\Renderer;
use Dotsystems\App\Parts\Response;

$html = Renderer::new()
    ->module('HelloWorld')
    ->setView('hello')
    ->setViewVar('title', 'Hello World')
    ->setViewVar('message', 'DotApp 2.0 is running.')
    ->renderView();

if ($html === '') {
    Logger::use()->error('HelloWorld view produced empty output');
    return new Response(500, 'Template error');
}
return $html;
        

Neexistuje množné setViewVars() ani verejné getView() / getLayout(). Jednu hodnotu prečítate cez getViewVar('title') (chýbajúci kľúč vráti "") alebo celý balík cez getViewVars().

1.4 Chýbajúce súbory zlyhajú potichu

Chýbajúci view alebo layout nevyhodí výnimku. Renderer zaloguje varovanie a vráti prázdny reťazec. Prázdna stránka zvyčajne znamená zlé meno súboru, nesprávny modul, alebo že sa setView() nikdy nespustilo. Vždy odovzdajte záložné meno a otestujte návratovú hodnotu:


$html = Renderer::new()
    ->module('Shop')
    ->setView('home', 'fallback/empty')
    ->setLayout('catalog/list', 'catalog/empty')
    ->setViewVar('title', $title)
    ->renderView();
        

Druhý argument setView() a setLayout() je záložný súbor, nie obalový layout. loadViewStatic() nekontroluje, či súbor existuje — uprednostnite setView() / loadView().

2.1 Prvé vykreslenie

Živý modul Hello World je minimálny vzor: jeden view, dve premenné, žiadny vnorený layout.

app/modules/HelloWorld/views/hello.view.php:


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>{{ var: $title }}</title>
  <link rel="stylesheet" href="/assets/modules/HelloWorld/css/hello.css" />
</head>
<body>
  <main>
    <h1>{{ var: $title }}</h1>
    <p>{{ var: $message }}</p>
  </main>
</body>
</html>
        

Zavolajte setView('hello') pred akýmkoľvek setViewVar(). Neskoršia zmena view zahodí predchádzajúci balík premenných.

2.2 Premenné view verzus premenné layoutu

renderView() vyhodnotí skompilovanú šablónu s balíkom premenných view. Hodnoty nastavené iba cez setLayoutVar() sa v tom výstupe neobjavia. Keď stránku vykresľujete cez renderView(), každú hodnotu, ktorú view aj jeho vložené layouty potrebujú, odovzdajte cez setViewVar().

setLayoutVar() s renderLayout() použite, keď vykresľujete súbor layoutu samostatne (táto dokumentačná stránka to robí pre časti článkov).

2.3 Obalový view plus obsahový layout

Stránke dajte obalový view, ktorý obsahuje {{ content }}, a vnútorné HTML umiestnite do layoutu vybraného cez setLayout(). Include zvnútra view naďalej používajú {{ layout:… }}.


return Renderer::new()
    ->module('Shop')
    ->setView('home')
    ->setLayout('content/welcome')
    ->setViewVar('title', 'Shop')
    ->setViewVar('items', $items)
    ->renderView();
        

View views/home.view.php:


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>{{ var: $title }}</title>
  <link rel="stylesheet" href="/assets/modules/Shop/css/page.css" />
</head>
<body>
  
  <main>{{ content }}</main>
  <script src="/assets/dotapp/dotapp.js"></script>
  <script src="/assets/modules/Shop/js/page.js"></script>
</body>
</html>
        

Layout views/layouts/content/welcome.layout.php:


<h1>{{_ "Welcome" }}</h1>
{{ foreach $items as $item }}
  <p>{{ var: $item['title'] }}</p>
{{ /foreach }}
        

View môže byť aj kompletný HTML dokument bez setLayout() a bez {{ content }}. Hello World, demá Examples aj demo Users to tak robia.

2.4 Súbory naprieč modulmi

Cestu predponujte názvom druhého modulu a dvojbodkou:


Renderer::new()->module('Checkout')->setView('Shop:home')->renderView();
        


        

Súbor naďalej žije pod views/ (alebo views/layouts/ pri layoutoch) daného modulu.

2.5 Ďalšie metódy vykreslenia

Metóda Použitie
renderView() Bežná stránka. Vyhodnocuje sa s premennými view.
renderLayout() Jeden súbor layoutu. Vyhodnocuje sa s premennými layoutu.
renderCode($code, $vars) Skompiluje a vyhodnotí HTML reťazec, ktorý už máte.
loadView($name) Prečíta súbor view ako text. Chýbajúci súbor: "".

3.1 Výpis hodnôt


{{ var: $title }}
{{ var: $user['name'] }}
{{ var:$title }}
        

Kompilátor to premení na echo. V direktíve nie je automatické escapovanie. Dáta požiadavky z $request->data() sú už chránené proti XSS. Ak z PHP posielate surové HTML, escapujte ho v kontroléri cez htmlspecialchars() pred setViewVar(), alebo ho nechajte chránené a volajte DotApp::DotApp()->unprotect($html) iba vtedy, keď zámerne vykresľujete značkovanie.

{{ var: }} neprijíma výrazy, ??, -> ani volania funkcií. Hodnotu pripravte v kontroléri.

3.2 Preklad


{{_ "Login" }}
{{_ var: $message }}
        

Okolo zdrojového reťazca použite dvojité úvodzovky. Chýbajúci kľúč vypíše pôvodný text. JSON súbory načítajte z modulu a locale nastavte v PHP:


use Dotsystems\App\Parts\Translator;

Translator::loadLocaleFile('Shop:sk_sk.json', 'sk_sk');
Translator::setLocale('sk_sk');
echo Translator::trans('Hello, {{ arg0 }}', $name);
        

Súbory žijú v app/modules/{Module}/translations/{locale}.json. Placeholdery sú {{ arg0 }}, {{ arg1 }}. Neexistuje pluralizácia ani reťazec záložných locale.

3.3 Podmienky


{{ if isset($user) }}
  <p>Signed in</p>
{{ elseif $guest === true }}
  <p>Guest</p>
{{ else }}
  <p>Unknown</p>
{{ /if }}
        

Za {{ dajte medzeru pred if, elseif, else a /if. Uzatváracia značka je {{ /if }}, nie {{ endif }}.

3.4 Cykly


{{ foreach $items as $item }}
  <li>{{ var: $item['title'] }}</li>
{{ /foreach }}

{{ while $i < 5 }}
  <p>{{ var: $i }}</p>
{{ /while }}
        

Uzatváracie značky sú {{ /foreach }} a {{ /while }}. Počítadlá inkrementujte v kontroléri alebo malým PHP blokom v šablóne. Biznis logiku nechajte v kontroléri.

3.5 Include a slot obsahu





{{ content }}
        

Značky layout sú include. Nemajú uzatváraciu značku. {{ content }} sa vyplní iba vtedy, keď ste zavolali setLayout() a potom renderView().

3.6 Formuláre a šifrovanie


<fo-rm method="POST" id="saveForm">
  <input type="text" name="title" />
  {{ formName(saveItem) }}
  <button type="submit">Save</button>
</fo-rm>
<script src="/assets/dotapp/dotapp.js"></script>
        
  • {{ formName(saveItem) }} musí byť medzi <fo-rm> (alebo <form>) a zodpovedajúcou uzatváracou značkou. Značka potrebuje atribút method. Mimo tohto páru renderer token nezmení.
  • Uprednostnite <fo-rm>. dotapp.js ho prevedie na skutočný formulár a odošle s CRC. PHP stále spúšťa $request->crcCheck() a potom $request->form(…).
  • Keď sa formulár odosiela na aktuálnu stránku, vynechajte action a ako posledný argument form() odovzdajte $request->getPath().
  • {{ CSRF }} vypíše obyčajný token. Pre aplikačné formuláre použite formName.

Hodnoty v šablóne šifrujte s vyhradeným extra kľúčom na každé pole:


<option value="{{ enc(Shop.user.id): $u['id'] }}">{{ var: $u['name'] }}</option>
{{ enc: $secret }}
{{ enc(mykey): "literal" }}
        

{{ enc: "literal" }} šifruje počas kompilácie šablóny. {{ enc(key): $var }} šifruje pri behu stránky. Dešifrujte tým istým extra kľúčom: Crypto::decrypt($cipher, 'Shop.user.id'). Zlyhanie je === false. Úplný návod k formulárom: Bezpečné formuláre.

3.7 Bloky

Pomenovaný blok zaregistrujte v initialize($dotApp) a potom v view obalte značkovanie:


Renderer::new()->addBlock('alert', function ($inner, array $args) {
    $kind = $args[0] ?? 'info';
    return '<div class="alert-' . htmlspecialchars($kind, ENT_QUOTES, 'UTF-8') . '">' . $inner . '</div>';
});
        

{{ blockerror: }} Undefined callable function ! {{ /blockerror: }}
        

privateblock uloží fragment ako PHP objekt, ktorý môžete klonovať v tom istom súbore:


<?php $block["row"] = new \Dotsystems\App\Parts\PrivateBlock(base64_decode("Jmx0O2xpJmd0O3t7IHZhcjogJG5hbWUgfX0mbHQ7L2xpJmd0Ow==")); ?>

        

<?php foreach ($items as $it): ?>
    <?php echo $block['row']->set('name', $it['name'])->html(); ?>
<?php endforeach; ?>
        

Natívne PHP v šablóne je povolené, ale sandbox odstráni nebezpečné funkcie (eval, exec, system, file_*, curl_*, mail, header, extract, call_user_func*, …). Ak volanie nič neurobí, je to preto. I/O a dopyty dávajte do kontroléra.

3.8 Nepodporovaná syntax

Nepíšte Píšte
{{ $title }} {{ var: $title }}
{{ endif }} / {{ endforeach }} {{ /if }} / {{ /foreach }}
{{ include 'x' }} v PHP view {{ layout:x }}
extends / section / yield renderView() + {{ content }} alebo {{ layout: }}
{{ $x ?? 'd' }} Hodnotu pripravte v kontroléri

{{ include path }} existuje iba v voliteľnom JavaScript šablónovom engine (časť 8), nikdy vo PHP views.

Značky input-group ako {{ InputKeys('register_form') }} a {{ input:text … }} pochádzajú z Input.php, nie z jadra tabuľky direktív. Pre bežné HTML formuláre uprednostnite formName.

Atribúty Bridge ({{ dotbridge:on(click)="…" }}) sú zdokumentované na DotBridge.

4. Assets

CSS, JS a obrázky ukladajte pod app/modules/{Module}/assets/. Framework ich servíruje ako:


/assets/modules/{Module}/{path}
        

<link rel="stylesheet" href="/assets/modules/Shop/css/page.css" />
<script src="/assets/dotapp/dotapp.js"></script>
<script src="/assets/modules/Shop/js/page.js"></script>
        

Stránky, ktoré odosielajú <fo-rm>, volajú $dotapp().load() alebo používajú Bridge, musia najprv načítať /assets/dotapp/dotapp.js. Táto URL je trasa frameworku. Vkladá kľúče relácie. Na verejnej stránke nelinkujte surový súbor z app/parts/js/.

Voliteľné CSS pomocníky: prepareCss() zreťazí a minifikuje do cache súboru a vypíše značku <link>. removeUnusedCss(true) odstráni selektory, ktoré sa v HTML nevyskytujú ako class="…" — odstráni aj triedy pridané neskôr JavaScriptom. Nechajte to vypnuté, kým neoveríte výstup. Vestavaný helper na cache-busting nie je; ak ho potrebujete, pridajte ?v= sami. Nepovoľujte cache HTML stránky cez useCache(true).

5. Vlastné renderery

Vlastný renderer je callable, ktorý dostane skompilované HTML (a ak je prítomný, balík premenných) a vráti HTML. Zaregistrujte ho raz v initialize($dotApp). Potom beží pri každom vykreslení.


use Dotsystems\App\Parts\Renderer;

Renderer::new()->addRenderer('shop.money', function (string $code, array $vars = []): string {
    $amount = number_format((float) ($vars['price'] ?? 0), 2);
    return str_replace('{{ money }}', $amount, $code);
});
        

Renderer::add($name, $callable) je tá istá registrácia na fasáde. getRenderer($name) vráti callable alebo false. renderWith($name, $code) spustí jeden renderer na reťazci.

Vstavané renderery registrované frameworkom zahŕňajú dotapp.block, reactive a input_form_*. Tento dokumentačný modul registruje Docs.code.replace, aby sa vzorky vo vnútri <pre><code> escapovali.

6. Pipeline, sandbox, ladenie

Typický beh renderView():

  1. Vyrieši vnorené {{ layout: }} / {{ baselayout: }} (hĺbka ≤ 20).
  2. Extrahuje privateblock a spustí vlastné renderery.
  3. Vloží HTML z setLayout() do {{ content }}.
  4. Skompiluje var / if / foreach / while / enc / preklad.
  5. Nahradí {{ CSRF }} a {{ formName() }}.
  6. Spracuje značky Bridge.
  7. Vyhodnotí v RenderingIsolator.

Zlyhanie kompilácie alebo eval vypíše ERROR WHILE EVAL: … do odpovede. Pre skutočné čísla riadkov:


define('__RENDER_TO_FILE__', true);
        

Skompilované PHP sa zapíše pod app/runtime/generator/rendering_*.php, načíta a potom zmaže.

7. Translator API

Metóda Výsledok
trans($text, ...$args) / t() Preložený reťazec, alebo pôvodný text, ak kľúč chýba
setLocale($locale) / getLocale() Aktuálne locale (predvolené en_us)
loadLocaleFile('Module:file.json', $locale) Chýbajúci súbor sa preskočí bez výnimky
has($key, $locale = null) bool — použite na zistenie chýbajúceho kľúča
all($locale = null) Všetky kľúče pre dané locale

Produktové texty, ktoré človek vidí (tlačidlá, prázdne stavy, názvy oprávnení), musia znieť ako hotové UI, nie ako odpoveď na prompt. Kľúče sú zdrojový anglický (alebo zdrojový) reťazec, pri vyhľadávaní sa prevádzajú na malé písmená.

8. Klientske šablóny

Voliteľný skript /assets/dotapp/dotapp.template.js pridáva v prehliadači $dotapp('#box').template('path/to/view', { items: […] }). Rozumie {{ var: }}, {{ if }}, {{ foreach }}, {{ block: }} a {{ include partials/header }}. Predvolená základná cesta je /app/views/. Načítajte ho po dotapp.js. Ak sa doplnok ešte načítava, počkajte na udalosť dotapp-template-ready.

PHP views nikdy nezískajú include. Serverové HTML ostáva na Renderer + {{ layout: }}. JS engine použite, keď aktualizujete zoznam cez $dotapp().load() bez plného vykreslenia stránky. Jadrová reaktivita (variable, databind, computed) je v príklade reaktivity. Vlastné widgety $dotapp().fn sú v príklade JS knižnice. Živé demo zoznamu je /documentation/examples/run/lists.

9. Kontrolný zoznam

  • Súbor view: {name}.view.php. Súbor layoutu: views/layouts/{path}.layout.php.
  • Renderer::new()->module('Name')->setView('name') pred setViewVar().
  • renderView() === '' berte ako chybu.
  • Vypisujte cez {{ var: $x }}. Vetvy uzatvárajte cez {{ /if }} / {{ /foreach }}.
  • Pri volaní renderView() odovzdajte každú hodnotu cez setViewVar().
  • {{ formName(handler) }} vložte do <fo-rm method="…"> a načítajte /assets/dotapp/dotapp.js.
  • Dopyty, autentifikáciu a zápisy nechajte v kontroléri. Sandbox šablóny odstráni nebezpečné PHP.