Nette Framework
  • Úvodní stránka
  • Download
  • Dokumentace
  • Fórum
  • Blog
  • Přispějte
  • Quick Start
    • Začínáme
    • Adresářová struktura
    • Vytvoření presenteru
    • Připojení šablony
    • Tvorba odkazů
    • Hezčí šablony
    • Zobrazení tabulky
    • Stránkování a řazení
    • Tvoříme komponentu
  • Nette\Application
    • Application
    • AppForm
    • Control
    • Presenter
    • PresenterCompo­nent
    • PresenterRequest
    • MultiRouter
    • Route
    • SimpleRouter
  • Nette\Caching
  • Nette\Component
  • Nette\Debug
    • Základy
    • Logování chyb
    • Firebug
  • Nette\Environment
  • Nette\Forms
  • Nette\IO\SafeS­tream
  • Nette\Loaders
  • Nette\Object
  • Nette\Security
    • Bezpečnost aplikací
    • Identity
    • SimpleAuthenti­cator
    • Permission
    • Dynamická správa rolí a zdrojů
  • Nette\String
  • Nette\Templates
    • Template
    • Template Filters
    • Template Helpers
  • Nette\Web\Html
  • Nette\Web\HttpR­equest
  • Nette\Web\HttpR­esponse
  • Nette\Web\Session
  • Nette\Web\User
Naposledy změněno 16. 11. 2008 Edituj Historie Poslední změny

Nette\Applica­tion\Presenter

Reaguje na události pocházející od uživatele a zajišťuje změny v modelu nebo v pohledu.

Životní cyklus presenteru

Životní cyklus presenteru

Životní cyklus presenteru je rozdělen do několika částí představovaných voláním volitelně existujících metod. Jde o present{View}, prepare{Scene}, handle{Signal} a render{Scene}. Každá metoda se hodí na něco jiného. Ty které mají společné znaky řadíme do společných fází životního cyklu.

Charakteristika fází:

  1. výkonná (execution)
  2. změny vnitřních stavů (state changing)
  3. vykreslovací (rendering)
  4. ukončení činnosti (shutdown)

Následující obrázek ilustruje, jak jsou postupně vykonávány metody presenteru v jeho životním cyklu a do jaké fáze tyto metody začleňujeme.

  • bílé – metody společné pro všechny pohledy
  • žluté – metody pro konkrétní pohled (nebo scénu)
  • modré – metoda, která má na starosti zpracování konkrétního signálu

Popis metod jednotlivých metod

Fáze výkonná (execution)

  1. startup je vyvolána na začátku životního cyklu presenteru. Může obsahovat například zajištění připojení k databázi.
  2. present{View} by měla obsahovat vykonání operací, po kterých může následovat přesměrování. Zde probíhá například automatické přesměrování na jinou jazykovou verzi (např. podle detekce z prohlížeče). Také zde může být logika rozhodování pohledu pro členění na jednotlivé scény.
  • Klíčový moment pro redirect: je zde prostor pro inicializace persistentních parametrů a manipulaci s modelem s možností následného přesměrování, tzn. v tomto stavu se zohlední při redirectu i hodnoty persistentních parametrů.
    Př.: pokud zde nastavím persistentnímu parametru $lang hodnotu 'cs', pak se i tato hodnota zohlední v novém požadavku po přesměrování. Po redirectu se skript ukončí, prohlížeč si vyžádá novou stránku a skript se spustí znovu. Tudíž všechny „obyčejné“ proměnné se ztratí.

Fáze změn vnitřních stavů (state changing)

  1. beforePrepare by měla obsahovat registrace společných komponent, které chceme používat ve více metodách (např: stránkování, řazení, formulář, …) a inicializaci případného AjaxDriveru.
  2. prepare{Scene} má na starosti logiku drobnějšího členění pro konkrétní situace (scény). Pokud není třeba rozlišovat více scén, tak obecně platí: view = scene. Z názvu scény si presenter určuje i soubory s šablonou.
  3. handle{Signal} : zpracování signálů neboli subrequestů. Jako dělané pro zpracování AJAXových požadavků.
  • Mezikrok – uložení vnitřních stavů: dříve než se přejde k další fázi (rendering), uloží se stav všech vnitřních stavů a persistentních proměnných. Zde máme právě poslední možnost změnit hodnoty persistentních parametrů před vykreslováním, protože dále už by změny neměly vliv při vytváření odkazů.

Fáze vykreslovací (rendering)

  1. beforeRender může obsahovat například společné nastavení filtrů pro všechny vykreslovače a nastavení společných proměnných pro šablony všech vykreslovačů.
  2. render{Scene} má na starosti vykreslení a věci s tím spojené (tvorba odkazů v šablonách, přiřzení proměnných do konkrétních šablon, …).
  3. afterRender je společná pro všechny scény, která je zavolána po korektním vykreslení presenteru.

Ukončení činnosti (shutdown)

  1. shutdown je vyvolána při korektním ukončení životního cyklu presenteru. Zde můžeme ukončit databázové připojení, kešování a podobně.

Presenter má během svého životního cyklu možnost kdykoliv ukončit svou činnost, pokud je s prací hotový ($presenter->terminate()). To může udělat i během „společných“ metod startup(), beforePrepare(), beforeRender(), případně zde může view měnit changeView(...).

U složitějších aplikací se nevyhnete stromovým strukturám a hierarchii presenterů. To jak je správně navrhovat je řečeno v článku Návrh struktury presenters/views. V takovýchto strukturách nelze abstraktní presenter kvůli bezpečnosti vyvolat URL požadavkem.

TIP: V presenteru, pokud někde dochází k zásadní chybě, tak je vhodné vyhazovat výjimky a další zpracování přenechat exception handleru.

Signál aneb subrequest

Signál (aneb subrequest) je komunikace se serverem pod prahem normálního view, tedy akce, které se dějí, aniž by se změnilo view. View může měnit pouze presenter, proto komponenty pracují vždy pod tímto prahem, tudíž $component->link() vede na signál, $presenter->link() obvykle na view (nebo signál, je-li označen vykřičníkem). Pro úplnost, i komponenta může volat $this->presenter->link('view').

Signál způsobí znovunačtení stránky úplně stejně jako při původním požadavku (kromě případu, kdy je volán AJAXem) a vyvolá metodu signalReceived($signal), jejíž výchozí implementace ve třídě PresenterCompo­nent se pokusí zavolat metodu složenou ze slov handle{signal}.
Další zpracování je na daném objektu. Objekty, které dědí od PresenterComponent (tzn. Control a Presenter) reagují tak, že se snaží zavolat metodu handle{signal} s příslušnými parametry.
Jinými slovy: vezme se definice funkce handle{signal} a všechny parametry, které přišly s požadavkem a k argumentům se podle jména dosadí parametry z URL a pokusí se danou metodu zavolat. Např. jako prametr $id se předá hodnota z parametru id v URL, jako $something se předá something z URL, atd.
Pokud metoda neexistuje, metoda signalReceived vyvolá výjimku.

Signál může přijímat jakákoliv komponenta, presenter nebo objekt, který implementuje rozhraní ISignalReceiver.

Mezi hlavní příjemce signálů budou patřit Presentery a vizuální komponenty dědící od Control (a ty se při přijetí signálu automaticky invalidují, což je důležité pro AJAX).
Signál má sloužit jako znamení pro objekt, že má něco udělat – anketa si má započítat hlas od uživatele, blok s novinkami se má rozbalit a zobrazit dvakrát tolik novinek, formulář byl odeslán a má zpracovat data a podobně.

Signál se vždy volá na aktuálním presenteru a view, tudíž není možné jej směřovat jinam.

URL pro signál vytváříme pomocí metody PresenterComponent::link() (nebo u Ajaxových aplikací ajaxLink, zde však vytváříme u defaultního AjaxDriveru celý kousek JavaScriptového kódu pro použití v ‚eventových‘ atributech HTML – onclick, onsubmit…). Jako parametr $destination předáme řetězec {signal}! a jako $args pole argumentů, které chceme signálu předat. Signál se vždy volá na aktuální view s aktuálními parametry, parametry signálu se jen přidají. Navíc se přidává hned na začátku parametr ?do, který určuje signál.

Jeho formát je buď {signal}, nebo {signalReceiver}-{signal}. {signalReceiver} je název komponenty v presenteru. Proto nemůže být v názvu komponenty pomlčka – používá se k oddělení názvu komponenty a signálu.

Metoda isSignalReceiver() ověří, zda je komponenta (první argument) příjemcem signálu (druhý argument). Druhý argument můžeme vynechat – pak zjišťuje, jestli je komponenta příjemcem jakéhokoliv signálu. Experimentálně lze jako druhý parametr uvést TRUE a tím ověřit, jestli je příjemcem nejen uvedená komponenta, ale také kterýkoliv její potomek.

V kterékoliv fázi předcházející handle{signal} můžeme vykonat signál manuálně zavoláním metody $this->processSignal(), která si bere na starosti vyřízení signálu – vezme komponentu, která se určila jako příjemce signálu (pokud není určen příjemce signálu, je to presenter samotný) a pošle jí signál.

Příklad:

if ($this->isSignalReceiver($this, 'paging') || $this->isSignalReceiver($this, 'sorting')) {
    $this->processSignal();
}

Tím je signál provedený a už se nebude znovu volat.

Subrequest vs. request

Rozdíly mezi signálem a požadavkem:

  • subrequest přenáší všechny komponenty
  • request přenáší označené (persistentní) komponenty

Šablony (Templates)

Presenter se pokusí vykreslit implicitní šablonu, pokud nebylo řečeno metodami changeLayout() & changeScene() jinak. Jméno šablony odvodí od view.

Každý presenter může mít vlastní layout uložený v souboru:

  • /templates/Homepage/@layout.phtml
  • /templates/Homepage.@layout.phtml.
  • nebo se použije společný layout uložený v /templates/@layout.phtml.

Změnit layout jde metodou changeLayout(), kde parameter FALSE layout zcela vypne, nebo lze předat název layoutu. Např. changeLayout('extra') bude místo souboru ...@layout.phtml hledat ...@extra.phtml.

Teprve když by soubor se šablonou neexistoval, vyhodí se výjimka BadRequestException.

Tohle chování má výhodu v tom, že pokud přidáváme nové view, stačí přidávat nové šablony do příslušné složky a není potřeba psát žádné (prázdné) metody. A naopak, view jsou na šablonách nezávislé, můžeme je zpracovat dřív, než na kreslení šablony dojde. Detailnější popis k šablonám lze nalézt v Nette\Templa­tes.

Persistentní parametry

Persistentní parameter není potřeba uvádět při volání link(...), neboť se předává automaticky. Ale uvést ho samozřejmě možné je a tak mu změnit hodnotu.

Podmínkou persistence je jeho deklarace jako public a uvedení řetězce @persistent v phpDoc syntaxi komentáře proměnné:

/** @persistent int */
public $page = 0;

Persistence zohledňuje hierarchii tříd, tzn. že každý poděděnec má tytéž persistentní parametry jako rodič. Komponenty jsou persistentní samy o sobě, tedy při subrequestu, při zavolání signálu. Je-li persistentní parametr inicializován výchozí hodnotou, jako výše uvedený, pak nejsou tyto hodnoty předávány v URL. Pokud aplikace přijme request, kde je tato výchozí hodnota zadána, provádí se redirect (index.php?page=0 → index.php) z důvodu SEO optimalizace. Jinak by se nám stránka, která zobrazí stejné informace pod dvěmi tvary, zaindexovala dvakrát. Proto je vhodné parametry, které se přenášejí v URL (nejen persistentní, ale i ty, které přenášíme v metodách prepare{Scene} apod.), inicializovat výchozí hodnotou, stejně jako výchozí presentery a pohledy v routách. Nejsou-li parametry inicializovány, mají hodnotu NULL.

V query-stringu funguje i přetypování BOOL a FLOAT hodnot na INT:

  • TRUE → 1
  • FALSE → 0

Presenter a komponenty

Co jsou to komponenty je popsáno jinde. Zde se zaměříme na výhody a úskalí používání komponent pod hlavičkou presenteru. První z takovýchto výhod je propojení komponenty s presenterem, který ji vytvořil.

Svázání komponenty s presenterem

Svázání komponenty s presenterem umožňuje:

  • používat v komponentě persistentní parametry
  • používat signály
  • volat na komponentě funkce závislé na přítomnosti presenteru (link, redirect, endSnippet)

Pokud nic z toho nepotřebujeme (nebo nechceme), není potřeba komponentu s presenterem vázat (respektive není potřeba ani dědit z PresenterComponent nebo Control). Nicméně původní konstruktor by se měl vždy rozhodně volat.

Příklad svázání: SomeControl tedy:

public function __construct(IComponentContainer $parent = NULL, $name = NULL, $someParametr = NULL)
{
    parent::__construct($parent, $name);
    // ... nejaky kod metody
}

a SomePresener

public function renderSomeview($someParametr)
{
    $this->someControl = new SomeControl($this, 'my-control-name', $someParametr);
    $this->template->someControl = $this->someControl;
    // ... nejaky kod metody
}

Persistentní komponenty

Stav komponent se přenáší při přechodu na jiný Presenter podobně, jako v případě persistentních parametrů.

Komponenty je nutné označit jako persistentní jen uvnitř presenteru (subkomponenty uvnitř komponent není třeba nijak značit).

class DefaultPresenter extends /*Nette\Application\*/Presenter
{
        /** @persistent */
        public $fifteen; // musí být public

        public function prepareDefault()
        {
                $this->fifteen = new FifteenControl($this, 'game');
                ...
        }
}

Proč je to nutné? V podstatě z technického důvodu. Mezi presentery se předávají jen data, která jsou jim společná, tedy která jsou deklarována na úrovni společných předků. Ale jak zjistit, že komponentu game deklarovala právě metoda třídy DefaultPresenter a ne nějaký její předek nebo potomek? To zjistit nelze. Lze ale zjistit, která třída deklarovala proměnnou fifteen a toho se právě využívá.

Subrequest přenáší všechny komponenty, request přenáší označené komponenty.

Tedy při subrequestu, při zavolání signálu jsou komponenty persistentní samy o sobě. Je ale nutné, aby na vstupním a cílovém presenteru byla tatáž komponenta zařazená ve stromu pod stejným jménem. Tudíž nemá smysl, aby to fungovalo pro předem neznámé komponenty, ale jen pro komponenty s presenterem nějak pevně svázané.

Viz také:

  • Nette\Applica­tion\Presenter API reference
  • Model-View-Presenter
  • Fully qualified view
  • Generování odkazů a Neplatné odkazy
  • Suggested directory structure
  • Routování
  • View vs. Scene
« Control PresenterCompo­nent »

Nette Framework powered | dibi powered | Texy! powered | Institut Školení PHP