View
Třída Moony\bootstrap\core\services\View obaluje Twig šablonovací systém. Controller vrací instanci View a framework ji automaticky vyrenderuje.
Základní použití
// Controller vrací View — automaticky se zobrazí
#[Route('/dashboard')]
public function dashboard(): View
{
return new View('account/dashboard', [
'title' => 'Dashboard',
'items' => ItemService::getAll()
]);
}
// Cesta k šabloně: app/views/account/dashboard.twig
// Přípona .twig se přidá automaticky
Metody
| get() | Vrátí vyrenderovaný HTML string (bez zobrazení) |
| display() | Vyrenderuje a zobrazí šablonu |
| addGlobal(string $name, $value) | Přidá globální proměnnou dostupnou ve všech šablonách |
| addFunction(string $name, callable $callback) | Přidá vlastní Twig funkci |
| getRedirectedVariable(string $name) | Statická — vrátí proměnnou předanou přes redirect |
Twig šablony
Výpis proměnných
{# Výpis proměnné (automaticky escapováno) #}
{{ title }}
{# Bez escapování (raw HTML) #}
{{ content|raw }}
{# Výchozí hodnota #}
{{ name|default('Neznámý') }}
Podmínky
{% if user.isAdmin %}
Admin
{% elseif user.isModerator %}
Moderátor
{% else %}
Uživatel
{% endif %}
{# Ternární operátor #}
{{ user.active ? 'Aktivní' : 'Neaktivní' }}
Cykly
{% for item in items %}
{{ item.name }}
{% endfor %}
{# S indexem #}
{% for key, item in items %}
{{ key }}: {{ item.name }}
{% endfor %}
{# Prázdný seznam #}
{% for item in items %}
{{ item.name }}
{% else %}
Žádné položky
{% endfor %}
{# Loop proměnné #}
{% for item in items %}
{{ loop.index }} {# 1, 2, 3, ... #}
{{ loop.index0 }} {# 0, 1, 2, ... #}
{{ loop.first }} {# true pro první #}
{{ loop.last }} {# true pro poslední #}
{{ loop.length }} {# celkový počet #}
{% endfor %}
Include a Extends
{# Hlavní šablona (layout) #}
{% extends '/account/layout/default.twig' %}
{% block pageTitle %}Moje stránka{% endblock %}
{% block content %}
Obsah stránky
{% endblock %}
{# Include partialu #}
{% include '/account/partials/header.twig' %}
{# Include s proměnnými #}
{% include '/account/partials/card.twig' with {title: 'Test', count: 5} %}
Filtry
{{ name|upper }} {# VELKÝMI PÍSMENY #}
{{ name|lower }} {# malými písmeny #}
{{ name|capitalize }} {# První velké #}
{{ name|trim }} {# Ořízne mezery #}
{{ name|length }} {# Délka stringu/pole #}
{{ price|number_format(2, ',', ' ') }} {# 1 234,50 #}
{{ text|nl2br }} {# Nové řádky na
#}
{{ html|raw }} {# Bez escapování #}
{{ date|date('d.m.Y') }} {# Formátování datumu #}
{{ items|json_encode }} {# Pole na JSON #}
Dostupné funkce v šablonách
Frameworkové funkce
| {{ route('name', param1, param2) }} | Vygeneruje URL z pojmenované route |
| {{ config('key', 'subkey') }} | Přečte konfigurační hodnotu |
| {{ lang('Group.Key') }} | Přeloží jazykový klíč |
| {{ currentLanguage() }} | Aktuální jazyk (en, cs, ...) |
| {{ currentRouteName() }} | Název aktuální route |
| {{ currentUrl() }} | Aktuální URL bez domény |
| {{ currentTheme() }} | Aktuální téma (light/dark) |
| {csrf} | Vloží hidden input s CSRF tokenem |
| {{ admin('RIGHT_NAME') }} | Zkontroluje admin právo (vrátí true/false) |
| {{ enumValue(enum) }} | Vrátí hodnotu PHP enumu |
| {{ enumName(enum) }} | Vrátí název PHP enumu |
Objekt app
V každé šabloně je automaticky dostupný objekt app s přístupem ke službám:
| {{ app.request.getUrl() }} | Request služba — všechny statické metody |
| {{ app.user.isLogged() }} | User služba — kontrola přihlášení, get() atd. |
| {{ app.str.contains(text, 'hledaný') }} | Str helper — práce s řetězci |
| {{ app.arr.depth(pole) }} | Arr helper — práce s poli |
PHP nativní funkce
V šablonách jsou registrované běžné PHP funkce:
| Array | count, implode, explode, in_array, array_key_exists |
| String | strlen, substr, strtolower, strtoupper, ucfirst, ucwords, trim, str_replace, nl2br |
| Number | number_format, round, ceil, floor, abs, min, max |
| Date | date, strtotime |
| Type | is_array, is_numeric, is_string |
| JSON | json_encode, json_decode |
| Hash/Encode | md5, sha1, base64_encode, base64_decode, urlencode, urldecode |
| HTML | htmlspecialchars, htmlspecialchars_decode |
| Regex | preg_match, preg_replace |
| Other | mt_rand |
Custom HTML tagy
Twig cache automaticky transformuje některé custom HTML tagy:
Ikony
<!-- Inline SVG z assets/global/libs/custom-icons/dist/ -->
<icon>android</icon>
<icon>feather/chevron-right</icon>
<icon style="width:20px;height:20px;">logo/white</icon>
Stavové hlášky
<!-- Automaticky se transformují na .alert.alert-xxx -->
<success>Operace proběhla úspěšně.</success>
<danger>Něco se <b>pokazilo</b>.</danger>
<info>Informační zpráva.</info>
<warning>Pozor na toto.</warning>
TypeScript / JavaScript
Veškerý klientský JavaScript pro sekci /account/ se píše v TypeScriptu ve složce assets/account/js/src/. Vše je v namespace Account.
Struktura
assets/account/js/src/
├── Account.ts // Hlavní třída — Global.init(), registrace komponent
├── tsconfig.json // TypeScript konfigurace
├── components/ // UI komponenty
│ ├── DateTimePicker.ts
│ ├── DateRangePicker.ts
│ ├── Dropdown.ts
│ ├── Modal.ts
│ ├── Offcanvas.ts
│ ├── RadioButton.ts
│ ├── Select.ts
│ ├── Submenu.ts
│ └── TabCard.ts
└── libs/ // Služby a utility
├── Cookie.ts
├── HttpClient.ts
├── LocalStorage.ts
└── SessionStorage.ts
Kam co patří
| components/ | UI komponenty — viditelné prvky, které interagují s DOM (modal, select, datepicker, tabs, ...) |
| libs/ | Služby a utility — HTTP klient, práce s cookies/storage, pomocné funkce bez přímého UI |
| Account.ts | Třída Global — inicializace všech komponent, globální event handlery, funkce sdílené napříč stránkami |
Namespace Account
Všechny třídy jsou v namespace Account a volají se jako Account.NázevTřídy.metoda().
// UI komponenty
Account.Modal.open({size: 600});
Account.Offcanvas.open('right', 'panel-id');
Account.Select.getValue(element);
// Služby
let http = new Account.HttpClient();
let response = await http.getAsync('/api/data');
Account.Cookie.set('key', 'value', {expires: 7});
Inicializace
Komponenty se inicializují v Account.Global.init(), která se volá v <head> layoutu. Pořadí:
// Account.ts — Global.init()
Account.Select.init();
Account.RadioButton.init();
Account.TabCard.init();
Account.DateTimePicker.init({
months: ['Leden', 'Únor', ...],
days: ['Po', 'Út', ...],
selectTime: 'Vyberte čas',
proceed: 'Potvrdit'
});
Account.DateRangePicker.init();
Account.Dropdown.init();
Kompilace
TypeScript se kompiluje do jednoho souboru, který se následně minifikuje:
# 1. Kompilace TS → typescript-temp.js
tsc -p assets/account/js/src/tsconfig.json
# 2. Minifikace + hash → main.{hash}.js
php moony assets compile
Nová komponenta — vzor
// assets/account/js/src/components/MyComponent.ts
namespace Account
{
export class MyComponent
{
private static initialized: boolean = false;
public static init(): void
{
if(this.initialized) {
return;
}
this.initialized = true;
// DOM ready
$(function() {
Account.MyComponent.initAll();
});
// Re-init po AJAX submenu navigaci
window.addEventListener('account:submenu:page-change', () => {
this.initAll();
});
}
private static initAll(): void
{
document.querySelectorAll('.my-component').forEach((el) => {
this.setup(el as HTMLElement);
});
}
private static setup(el: HTMLElement): void
{
if(el.hasAttribute('data-mc-init')) {
return;
}
el.setAttribute('data-mc-init', '1');
// Logika komponenty...
}
}
}
Assets & Kompilace
Příkaz php moony assets compile zpracuje CSS, JS a SVG ikony. Kompilační skripty jsou v bootstrap/core/moony/commands/commands/assets/.
CSS
Zdrojové CSS soubory v assets/{sekce}/css/src/*.css se zpracují:
- PurgeCSS odstraní nepoužívané styly (jen pro bootstrap.css a app.css)
- CleanCSS minifikuje
- Výstup se uloží jako {název}.{hash}.css do assets/{sekce}/css/dist/
- Seznam souborů se zapíše do app/constants/{sekce}/assets/css.php
Pořadí CSS souborů se řídí komentářem /* ORDER X */ na prvním řádku zdrojového souboru. Nižší číslo = dříve:
/* ORDER 30 */
.my-styles {
...
}
assets/account/css/
├── src/
│ ├── bootstrap.css // ORDER neurčen — purguje se
│ ├── app.css // ORDER neurčen — purguje se
│ ├── custom.css // ORDER 30
│ ├── dark-mode.css // ORDER 1000
│ └── login.css
└── dist/
├── bootstrap.d5e34c150ee0.css
├── app.51977463fa69.css
├── custom.b83b6aa43b14.css
├── dark-mode.4687f4f620c5.css
└── login.277e200b9a73.css
JavaScript
Kompilace JS probíhá ve dvou krocích:
- TypeScript kompilace (tsc) — zkompiluje všechny .ts soubory z assets/{sekce}/js/src/ do jednoho typescript-temp.js podle tsconfig.json
- Minifikace (php moony assets compile) — vezme typescript-temp.js, minifikuje přes UglifyJS a uloží jako main.{hash}.js. Poté smaže typescript-temp.js.
# Kompletní build
tsc -p assets/account/js/src/tsconfig.json # 1. TS → typescript-temp.js
php moony assets compile # 2. minifikace + hash
assets/account/js/
├── src/
│ ├── tsconfig.json // outFile: "../typescript-temp.js"
│ ├── Account.ts
│ ├── components/
│ └── libs/
├── typescript-temp.js // dočasný (smaže se po kompilaci)
└── main.6afed05aefaf.js // finální minifikovaný
Cesta k výslednému JS se zapíše do app/constants/{sekce}/assets/js.php.
SVG ikony
Třetí krok kompilace zpracuje SVG ikony v assets/global/libs/custom-icons/ — optimalizuje přes SVGO.
Načítání v šablonách
Zkompilované soubory se načítají v head layoutu přes closure() funkci, která načte PHP pole s cestami:
{# CSS #}
{% for file in closure(app.assets.account.css) %}
<link href="{{ file }}" rel="stylesheet" type="text/css" />
{% endfor %}
{# JS #}
{% for file in closure(app.assets.account.js) %}
<script src="{{ file }}"></script>
{% endfor %}
Objekt app.assets je definován ve View.php a obsahuje Closure, která loadne PHP soubor s poli cest:
// View.php — initParams()
'assets' => [
'account' => [
'css' => fn() => require ROOT . 'app/constants/account/assets/css.php',
'js' => fn() => require ROOT . 'app/constants/account/assets/js.php'
],
'front' => [
'css' => fn() => require ROOT . 'app/constants/front/assets/css.php',
'js' => fn() => require ROOT . 'app/constants/front/assets/js.php'
]
]
