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:

Arraycount, implode, explode, in_array, array_key_exists
Stringstrlen, substr, strtolower, strtoupper, ucfirst, ucwords, trim, str_replace, nl2br
Numbernumber_format, round, ceil, floor, abs, min, max
Datedate, strtotime
Typeis_array, is_numeric, is_string
JSONjson_encode, json_decode
Hash/Encodemd5, sha1, base64_encode, base64_decode, urlencode, urldecode
HTMLhtmlspecialchars, htmlspecialchars_decode
Regexpreg_match, preg_replace
Othermt_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>
Custom tagy se zpracovávají při kompilaci Twig cache. Po změně je nutné smazat cache: php moony clear view

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.tsTří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
Samotný php moony assets compile nekompiluje TypeScript. Je třeba nejdříve spustit tsc ručně, který vytvoří typescript-temp.js, a teprve poté assets compile tento soubor minifikuje.

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...
        }
    }
}
Každá komponenta by měla: 1. mít guard proti dvojité inicializaci, 2. registrovat se na account:submenu:page-change event pro re-init po AJAX navigaci, 3. používat data-*-init atribut na elementech proti opakovanému setupu.
Pro AJAX volání, cookies, storage vždy používej frameworkové wrappery z libs/, nikdy nativní API. Kompletní dokumentace viz sekce JavaScript → Libs & Standards v menu.

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í:

  1. PurgeCSS odstraní nepoužívané styly (jen pro bootstrap.css a app.css)
  2. CleanCSS minifikuje
  3. Výstup se uloží jako {název}.{hash}.css do assets/{sekce}/css/dist/
  4. 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:

  1. TypeScript kompilace (tsc) — zkompiluje všechny .ts soubory z assets/{sekce}/js/src/ do jednoho typescript-temp.js podle tsconfig.json
  2. 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'
    ]
]
php moony assets compile sám nekompiluje TypeScript! Nejdříve spusť tsc, který vytvoří typescript-temp.js, a teprve poté assets compile ho minifikuje. Pokud typescript-temp.js neexistuje, JS se nezkompiluje.
Hash v názvu souboru (main.6afed05aefaf.js) se generuje z obsahu — stejný kód = stejný hash = žádné zbytečné cache busting u klientů.