Layout - Jet\MVC_Layout

Rovnou na začátek si řekněme, že layout je ve skutečnosti také view. Tedy jsou to view skripty, úplně stejně je do nich možné předávat data / parametry a s těmi pracovat. Vše co platí pro view platí pro layout a proto zde vysvětlím pouze to co layout oproti view rozšiřuje a odlišuje.

Layout je singleton

O view jsme si řekli, že instancí view můžete mít kolik potřebujete a do výstupu jednoho view můžete zahrnout výstup view druhého. Pro layout je to opačně. Z principu věcí musí existovat právě jedna instance layoutu - singleton. A s touto instancí se pracuje a především layoutu předáváme dílčí části výstupu, to jest obsah určený na určitou pozici.

Inicializace layoutu se provádí takto:

MVC_Layout::setCurrentLayout(
    
Factory_MVC::getLayoutInstance(
        
scripts_dir'/some/dir/',
        
script_name'layout-script-name'
    
)
);

A nyní pár faktických věcí:

  • Vytváření instance je doporučené provádět přes továrnu.
  • Při vytváření instance layoutu předáváte jak název adresáře, kde layout skripty jsou, tak rovnou název layout skriptu (název layout skriptu je totéž co název view skriptu). Ve view se při inicializaci předává pouze adresář, view skript se určuje až při renderování. Layout potřebuje tyto informace ihned, ovšem i po inicializaci je možné název view skriptu představujícího layout změnit.
  • Pokud používáte Jet MVC, tak inicializaci layoutu neřešíte, protože to za vás dělá stránka.

K layoutu se pak přistupuje jednoduše takto:

MVC_Layout::getCurrentLayout()

Layout skript

Již víte, že layout je technicky odvozený od view. Tedy i layout skript má stejnou podobu a princip jako view skript. Ovšem je v něm řada věcí navíc. Pojďme si jeden layout ukázat:

<?php
use Jet\MVC_Layout;
use 
Jet\MVC;
use 
Jet\Tr;
use 
Jet\SysConf_URI;

/**
 * @var MVC_Layout $this
 */

require 'parts/init_libs.php';
?>
<!DOCTYPE html>
<html lang="<?= MVC::getLocale()->getLanguage() ?>">
<head>
    <title><?= MVC::base()->getLocalizedDataMVC::getLocale() )->getTitle() ?> : <?= MVC::getPage()->getTitle() ?></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="content-language" content="<?= MVC::getLocale()->getLanguage() ?>"/>
        
    
        <link rel="stylesheet" type="text/css" href="/css/packages/b03da990b885626d8669281b9e41d2f1.css"/>

        <script type="text/javascript" src="/js/packages/e28ddecbd1f561371648b86815345bce.js"></script>


    <link rel="icon" href="<?= SysConf_URI::getImages() ?>favicon.png" type="image/png">
</head>
<body>
<div class="main">
    <div class="header">
        <h1><?= Tr::_'Hello WORLD!' ?></h1>
        <small><?= Tr::_"PHP Jet Example Application" ); ?></small>
    </div>

    <jet_module module="UI.Web" action="breadcrumbNavigation" view="default" is_cacheable="true"/>

    <div class="body">
        <div class="left">
            <jet_module module="UI.Web" action="main_menu" is_cacheable="true"/>

            <jet_layout_position name="left"/>
        </div>
        <div class="center">
            <jet_layout_main_position/>
        </div>
        <div class="right">
            <jet_module module="Test.MVC" action="test_mvc_info" is_cacheable="true"/>

            <jet_layout_position name="right"/>
        </div>
    </div>
</div>
</body>
</html>

Krom celkového stylu již známého view jste si určitě všimli několika tagů začínajících na jet_*. Právě tak jsou určené pozice, ale řada dalších důležitých věcí. Tak si je pojďme probrat.

Hlavní pozice: jet_layout_main_position

Každý layout by měl mít nějakou hlavní pozici. Tato pozice nemá, na rozdíl od běžných pozic, definované žádné pojmenování.

Pozice: jet_layout_position

Všechny ostatní pozice musí již mít atribut name a tedy určené jméno. Jméno pozice mohou tvořit velká a malá písmena, čísla, mezery, pomlčky a podtržítka.

Pozice pro meta tagy: jet_layout_meta_tags

Je to prostá pozice bez atributů a určije místo na které layout umístí vygenerované meta tagy určené aktuální stránkou.

Je důležité zmínit, že tato operace se provádí až na konci zpracování. Tedy vaše aplikační modely a jejich kontrolery mohou obsah meta tagů ovlivňovat.

Pozice pro požadované JavaScript soubory: jet_layout_javascripts

Jak si později ukážeme, tak je sjednocen i způsob jak do stránky vkládat zdroje odkud si má natáhnout JavaScript (knihovny i vaše vlastní skripty) a pokud je aktivní balíčkovač, tak jsou rovnou vytvářeny CSS a JS balíčky. Prozatím si řekněme, že tento tak určuje na jakou pozici má být umístěn kus HTML kódu, který JavaScript "natáhne".

Pozice pro požadované CSS soubory: jet_layout_css

Je to naprosto to samá jako jet_layout_javascripts - pouze se to týká CSS.

Moduly: jet_module

A teď něco zajímavého. Jak již víte, tak stránka definuje co na ní je. Ale co když chcete dát na všechny stránky (s daným layoutem) třeba drobečkovou navigaci, menu, nějaký obecný info box, či cokoliv dalšího? Definovat to u každé stránky by bylo opravdu hodně nepraktické a nepříjemné. Takový obsah který má daný layout vždy obsahovat je logicky spíš věcí layoutu než stránky. A toto je řešení. Do layoutu můžete přímo definovat jaký aplikační modul a jaký jeho kontroler má na dané pozici být.

Pojďme se na jeden z tagů z ukázkového layout skript podívat:

<jet_module module="UI.Web" action="breadcrumbNavigation" view="default" is_cacheable="true"/>

Tak má několik atributů z nichž některé jsem povinné, další volitelný a ostatní jsou plně vámi definovatelné.

Povinné atributy jsou:

Volitelné atributy jsou:
  • controller: Definuje název kontroleru (ne jeho třídu, ale název - viz téma stránka / obsah). Výchozí hodnota je: 'Main'
  • is_cacheable: Určuje zda je obsah statický a tedy zda může být ukládán co keše. Atribut má mít hodnotu false, nebo true, hodnota false je výchozí.

Veškeré ostatní atributy jsou vámi volitelné a stanou se z nich parametry obsahu. Tedy budou k dispozici vašemu kontroleru a můžete tak dále specifikovat co přesně na dané pozici chcete.

Vkládání obsahu na pozice

Layout sice má pozice, ale sám o sobě logicky neví co na nich má být.

Pokud je použito celé Jet MVC, tedy báze, stránky a tak dále, tak to ve skutečnosti ani neřešíte (respektive už je to dáno definicí stránek a jejich obsahu).

Jak již bylo uvedeno u view, tak akce kontroleru posílá obsah zpět stránce a ta jej rovnou předává layoutu. Tedy když vyvíjíte nějaký aplikační modul, tak jste od tohoto procesu odstíněni a oproštěni.

Ovšem i tak je nutné vědět jak do layoutu poslat obsah přímo. Krom toho že je prostě správné chápat jak věci fungují, tak ne vždy musíte používat aplikační moduly a celé Jet MVC, protože layout je použitelný samostatně i bez ostatních částí Jet MVC.

Pojďme se na to tedy kouknout:

MVC_Layout::getCurrentLayout()->addOutputPart(
    
output'<p>Lorem ipsum dolor sit amet</p>',
    
position'left',
    
position_order10
);

Že je layout singleton již víte, proto jej získáme metodou MVC_Layout::getCurrentLayout(). Řekněme si ještě něco k parametrům metody addOutputPart:

  • output: To je ten kus výstupu (z pravidla kus HTML), který chcete umístit na danou pozici.
  • position: Název pozice do které obsah přidáváte. Parametr není povinný a pokud není specifikován, tak je výchozí pozicí hlavní pozice.
  • position: Pořadí obsahu na cílové pozici. Parametr není povinný. Pokud pořadí není zadáno, tak je obsah zařazen jako poslední na dané pozici.

Vkládání CSS a JavaScript souborů

Aby bylo možné použít balíčkovače, tak je nutné vkládání CSS a JavaScript souborů (například knihoven, ale i vašich CSS a JS) unifikovat. Stále máte možnost dát do kódu tag script s parametrem src="//domena/skript.js", ale pak je vyloučeno, aby se z mnoha souborů stal soubor jeden a tím se urychlilo načítání stránky.

Tedy správné je nevkládat linky přímo do HTML, ale používat metody layoutu, který s balíčkovači přímo spolupracuje. Layout se tedy již postará o použití balíčkovače a na pozice definované příslušnými tagy vloží URL vygenerovaných balíčků. Nebo pokud je balíčkovač neaktivní, tak vloží nalinkování jednotlivých CSS a JS souborů. Připomeňme si, že pro definování pozice určená pro CSS a JS slouží tagy jet_layout_css a jet_layout_javascripts.

Teď se podívejme na to, jak layoutu říct: "chci ty a ty CSS a JS".

Pro vkládání slouží metody requireJavascriptFile, requireMainJavascriptFile a requireCssFile, requireMainCssFile. Společný parametr všech metod je URL požadovaného souboru.

Jaký je mezi těmi metodami s Main a bez Main v názvu? Soubory jsou rozděleny na dvě skupiny a to hlavní a běžné. Nejprve jsou nataženy (nebo v balíčku umístěny) ty hlavní a až pak ty běžné. Můžete tak zabezpečit, že například nějaká klíčová knihovna se natáhne před skriptem, který je na ní závislý. Dále je pořadí v rámci těchto dvou skupin určeno tak v jakém pořadí byla URL vložena. To platí jak pro CSS, tak pro JS

Metody pro vložení CSS mají ještě parametr media. To odpovídá atributu media tagu link. Tedy můžete vkládat CSS pro různá média a pochopitelně takto budou rozděleny i balíčky.

A kde provádět vkládání, tedy jak přímo definovat jaké CSS a JS potřebujete? Koukněme se ještě jednou na malý kousek ukázkového layout skriptu. Tam se nalézá toto: require 'parts/init_libs.php'; To je způsob, který se mi osvědčil. Tedy udělat si skript vkládaný do layout skriptů a v něm definovat jaké soubory budu potřebovat. Ten skript pak může mít tuto podobu: <?php

use Jet\MVC_Layout;
use 
Jet\SysConf_URI;

/**
 * @var MVC_Layout $this
 */

$this->requireMainCssFile'https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' );
$this->requireMainCssFile'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css' );
$this->requireMainCssFileSysConf_URI::getCss() . 'flags.css' );
$this->requireMainCssFileSysConf_URI::getCss() . 'web_main.css?v=1' );

$this->requireMainJavascriptFile'https://code.jquery.com/jquery-3.5.1.js' );
Všimněte si prosím, že k layoutu se opět přistupuje přes $this, protože kód se stane součástí layout skriptu kam je vložen.

Ale pokud například nějaký z vašich aplikačních modulů potřebuje nějakou další knihovnu, pak není problém kdykoliv zavolat: MVC_Layout::getCurrentLayout()->requireJavascriptFile('//domain/something.js');

Posprocesory layoutu

V určitých situacích může být potřebné dále modifikovat finální výstup - tedy dále modifikovat již sestavený layout doplněný o obsah. Například je možné realizovat systém který doplňuje odkazy na stránky na základě kódu. Ukažme si příklad:

namespace JetApplication;

use 
Jet\MVC;
use 
Jet\MVC_Layout;
use 
Jet\MVC_Layout_OutputPostprocessor;

$layout MVC_Layout::getCurrentLayout();

$layout->addOutputPostprocessor( new class($layout) extends MVC_Layout_OutputPostprocessor {
    
    public function 
getId(): string
    
{
        return 
'page_URL_creator';
    }
    
    public function 
processstring $output ): string
    
{
        if(!
preg_match_all('/%URL:([a-z\-0-9A-Z]+)%/'$output$matchesPREG_SET_ORDER)) {
            return 
$output;
        }
        
        foreach( 
$matches as $m ) {
            
$orig_str $m[0];
            
$page_id $m[1];
            
            
$page MVC::getPage$page_id );
            
$URL $page?->getURL()??'';
            
            
$output str_replace($orig_str$URL$output);
        }
        
        return 
$output;

    }
});

Tato ukázka nalezne ve výstupu například tento kód: %URL:about-us% a nahradí jej odkazem na stránku mající ID about-us (pokud tato stránka existuje).

Systém je velice jednoduchý - postprocesor výstupu je jednoduchá třída rozšiřující abstraktní třídu Jet\MVC_Layout_OutputPostprocessor a instanci této třídy předat layoutu.

S postprocesrory lze dále pracovat - viz seznam metod.

Přehled metody třídy Jet\MVC_Layout

Obecné

Metoda Význam
public static getCurrentLayout(
): MVC_Layout|null
Vrátí aktuální instanci layoutu.
public static setCurrentLayout(
MVC_Layout $current_layout
): void
Nastaví aktuální instanci layoutu.
public __construct(
string $scripts_dir,
string $script_name
)
Parametry konstruktoru:
  • scripts_dir: Úplná cesta k adresáři obsahujícímu layout skripty.
  • script_name: Název layout skriptu.
public addOutputPart(
string $output,
string|null $position = null,
int|null $position_order = null
): void
Přidá výstup na určeno pozici. Parametry:
  • output: To je ten kus výstupu (z pravidla kus HTML), který chcete umístit na danou pozici.
  • position: Název pozice do které obsah přidáváte. Parametr není povinný a pokud není specifikován, tak je výchozí pozicí hlavní pozice.
  • position: Pořadí obsahu na cílové pozici. Parametr není povinný. Pokud pořadí není zadáno, tak je obsah zařazen jako poslední na dané pozici.
public render(
): string
Vygeneruje finální výstup včetně umístění veškerého obsahu na jeho pozice a výsledek vrátí jako návratovou hodnotu. Výsledek není automaticky poslán na výstup.
public parseContent(
string $result
): MVC_Page_Content_Interface[]
Najde v layout skriptu všechny tagy jet_module a vrátí připravené instance obsahu.

Metody pro vkládání CSS a JavaScript souborů

Metoda Význam
public requireJavascriptFile(
string $URI
): void
Požadavek na vložení JavaScript souboru s normální prioritou.
public requireMainJavascriptFile(
string $URI
): void
Požadavek na vložení JavaScript souboru s vysokou prioritou.
public requireCssFile(
string $URI,
string $media = ''
): void
Požadavek na vložení CSS souboru s normální prioritou.
public requireMainCssFile(
string $URI,
string $media = ''
): void
Požadavek na vložení CSS souboru s vysokou prioritou.

Metody postprocesoru výstupu

Metoda Význam
public addOutputPostprocessor(
MVC_Layout_OutputPostprocessor $postprocessor
) : void
Přidá postprocesor do layoutu.
public getPostprocessor(
string $id
) : null|MVC_Layout_OutputPostprocessor
Vrátí instanci konkrétního postprocesoru na základě id, nebo null pokud takový postprocesor není definován.
public removePostprocessor(
string $id
) : void
Odstraní postprocesor na základě id.
public performPostprocess(
string $output
) : string
Zajistí provedení činnosti prostprocesorů. Tuto metodu volá layout automaticky a není nutné zajišťovat její volání.
Předchozí kapitola
View - Jet\MVC_View
Další kapitola
Jet\MVC_Layout_OutputPostprocessor