You are browsing the unmaintained documentation for old Nette 2.3. See documentation for current Nette.

Jak fungují aplikace?

Seznámíme se s tím, jak se vlastně v Nette Framework tvoří aplikace. Po přečtení budete znát:

  • MVC, adresářovou strukturu a soubor bootstrap.php
  • co jsou to presentery a akce
  • jak se používají šablony
  • co jsou persistentní parametry

Model-View-Controller (MVC)

Model-View-Controller je softwarová architektura, která vznikla z potřeby oddělit u aplikací s grafickým rozhraním kód obsluhy (controller) od kódu aplikační logiky (model) a od kódu zobrazujícího data (view). Tím jednak aplikaci zpřehledňuje, usnadňuje budoucí vývoj a umožňuje testování jednotlivých části zvlášť.

Model

Model je datový a zejména funkční základ celé aplikace. Je v něm obsažena aplikační logika. Jakákoliv akce uživatele (přihlášení, vložení zboží do košíku, změna hodnoty v databázi) představuje akci modelu. Model si spravuje svůj vnitřní stav a ven nabízí pevně dané rozhraní. Voláním funkcí tohoto rozhraní můžeme zjišťovat či měnit jeho stav. Model o existenci view nebo kontroleru neví.

Pojem Model představuje celou vrstvu, nikoliv samostatnou třídu.

View

View, tedy pohled, je vrstva aplikace, která má na starost zobrazení výsledku požadavku. Obvykle používá šablonovací systém a ví, jak se má zobrazit ta která komponenta nebo výsledek získaný z modelu.

Controller

Řadič, který zpracovává požadavky uživatele a na jejich základě pak volá patřičnou aplikační logiku (tj. model) a poté požádá view o vykreslení dat. Obdobou kontrolerů v Nette Framework jsou presentery.

Adresářová struktura

Když se po stažení distribuce Nette Framework podíváte do adresáře sandbox, uvidíte doporučovanou adresářovou strukturu:

<b>sandbox/</b>
├── <b>app/</b>                  ← adresář s aplikací
│   ├── <b>config/</b>           ← konfigurační soubory
│   │   ├── <b>config.neon</b>   ← konfigurační soubor
│   │   └── <b>config.local.neon</b>
│   │
│   ├── <b>forms/</b>            ← třídy formulářů
│   ├── <b>model/</b>            ← modelová vrstva a její třídy
│   ├── <b>presenters/</b>       ← třídy presenterů
│   │   ├── <b>HomepagePresenter.php</b>  ← třída presenteru Homepage
│   │   └── <b>templates/</b>    ← adresář se šablonami
│   │       ├── <b>@layout.latte</b> ← šablona společného layoutu
│   │       └── <b>Homepage/</b>     ← šablony presenteru Homepage
│   │           └── <b>default.latte</b>  ← šablona akce default
│   ├── <b>router/</b>           ← třídy routerů
│   │
│   └── <b>bootstrap.php</b>     ← zaváděcí soubor aplikace
│
├── <b>log/</b>                  ← obsahuje logy, error logy atd.
├── <b>temp/</b>                 ← pro dočasné soubory, cache, ...
│
├── <b>vendor/</b>               ← adresář na knihovny (např. třetích stran)
│   ├── <b>nette/</b>            ← všechny knihovny Nette Frameworku
│   │   └── <b>nette/Nette</b>   ← oblíbený framework nainstalovaný Composerem
│   ├── ...
│   │
│   └── <b>autoload.php</b>      ← soubor který se stará o načítání tříd nainstalovaných balíčků
│
└── <b>www/</b>                  ← veřejný adresář, document root projektu
    ├── <b>.htaccess</b>         ← pravidla pro mod_rewrite
    ├── <b>index.php</b>         ← který spouští aplikaci
    └── <b>images/</b>           ← další adresáře, třeba pro obrázky

Krom toho se v některých složkách nacházejí soubory .htaccess resp. web.config, které zakazují přístup z prohlížeče (pro Apache resp. IIS). Ujistěte se, že to funguje a že se skutečně z prohlížeče do složky app/ nebo libs/ nedostanete.

Adresářům log/ a temp/ nezapomeňte nastavit práva pro zápis (chmod 0777).

index.php & bootstrap.php

Všechny požadavky posílá prohlížeč přes jediný PHP soubor, který se nachází ve veřejném adresáři www/, a tím je soubor index.php. V jeho těle se neděje takřka nic. Pouze se předává řízení do aplikace (tj. do adresáře app/) zaváděcímu souboru bootstrap.php.

Nette Framework jednotně všechny cesty ukládá bez pravostranného lomítka.

Výše uvedená adresářová struktura je tak skutečně jen doporučená, protože ji můžeme snadno jakkoliv změnit nebo složky přejmenovat a bude stačit pouze přenastavit cesty v souboru bootstrap.php.

Soubor bootstrap.php nejprve načte Nette Framework a všechny knihovny na kterých závisíme:

require __DIR__ . '/../vendor/autoload.php';

Třída Configurator vytváří tzv. systémový DI kontejner a stará se o inicializaci aplikace.

$configurator = new Nette\Configurator;

Aktivuje debugger a logger ve striktním režimu:

//$configurator->setDebugMode(TRUE);
$configurator->enableDebugger(__DIR__ . '/../log');

Nastavíme složku do které se budou zapisovat dočasné soubory

$configurator->setTempDirectory(__DIR__ . '/../temp');

Aktivujeme autoloading, který zajistí automatické načínání všech souborů s našimi třídami

$configurator->createRobotLoader()
	->addDirectory(__DIR__)
	->addDirectory(__DIR__ . '/../vendor/others')
	->register();

A podle konfiguračních souborů se generuje systémový DI kontejner. Od něj se bude odvíjet všechno další.

Tento DI kontejner vrátíme jako výsledek volání app/boostrap.php

$configurator->addConfig(__DIR__ . '/config/config.neon');
$configurator->addConfig(__DIR__ . '/config/config.local.neon');
return $configurator->createContainer();

a uložíme si ho do lokální proměnné ve www/index.php a aplikaci spustíme:

$container = require __DIR__ . '/../app/bootstrap.php';
$container->getService('application')->run();

To je vše.

Zpracování akce presenteru

Nyní prosím zbystřete. Každý požadavek na naši aplikaci se dostane přes soubory index.php a bootstrap.php do objektu $application. Ten ale HTTP požadavkům nerozumí, proto požádá router, aby mu ho přeložil do řeči, které už rozumí. Tedy aby mu řekl, pro který presenter je požadavek určen a kterou akci s ním chce vykonat. Router kupříkladu odpoví, že uživatel chce akci show presenteru Product (je dobrý zvykem to zapisovat jako Product:show) a dále předává parametr id = 123. Česky by se řeklo: uživatele chce zobrazit produkt s id=123.

Tomu už $application rozumí a přistoupí k plnění přání. Vyrobí objekt třídy ProductPresenter, která reprezentuje presenter Product. (Abychom byli zcela přesní, o výrobu objektu presenteru požádá službu presenterFactory). A pak bude presenter požádán o provedení akce (show a parametrem id).

Presenter je objekt, který vezme požadavek přeložený routerem a vymyslí odpověď. Odpovědí může být HTML stránka, obrázek, XML dokument, soubor na disku, JSON, přesměrování nebo cokoliv potřebujete. Konkrétně ProductPresenter požádá model o data a ty poté předá do šablony k vykreslení. Tohle se zpravidla odehraje v metodě renderShow, kde slovo Show odpovídá názvu akce a parametr požadavku id bude předán jako parametr této metodě:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function renderShow($id)
	{
		// získáme data z modelu a předáme do šablony
		$this->template->product = $this->model->getProduct($id);
	}
}

Pole všech parametrů odeslaných aplikaci požadavkem GET najdete v $this->request->getParameters(). V běžných případech byste je ale neměli potřebovat, využívejte routování a parametrů akce.

Obdobně pole všech hodnot přijatých přes POST naleznete v $this->request->getPost(). Ani to byste neměli potřebovat, pokud nezpracováváte požadavek z jiné aplikace. Nejčastěji zpracováváte totiž formuláře vlastní aplikace a na ty máme formulářovou komponentu.

Následně přistoupí presenter k vykreslení šablony.

Moduly

U složitějších aplikací můžeme složky s presentery a šablonami rozčlenit do podadresářů, kterým říkáme moduly. Pokud by naše aplikace obsahovala například moduly Front a Admin, její struktura by mohla vypadat takto:

<b>sandbox/</b>
	<b>app/</b>                ← adresář s aplikací
		<b>AdminModule/</b>    ← modul Admin
			<b>presenters/</b> ← a jeho presentery
			<b>templates/</b>  ← a šablony

		<b>FrontModule/</b>    ← modul Front
			<b>presenters/</b> ← a jeho presentery
			<b>templates/</b>  ← a šablony

		...

Moduly nemusí tvořit jen plochou strukturu, lze vytvářet i submoduly atd.

Pokud by součástí modulu Front byl presenter Product. Akce show se pak zapíše jako Front:Product:show a třída ProductPresenter se umístí do jmenného prostoru FrontModule:

namespace FrontModule;

class ProductPresenter extends \Nette\Application\UI\Presenter
{
	// ...
}