Letos slaví MVC kulaté výročí a zdá se, že je populárnější než kdykoliv předtím. V čem je jeho síla? A hlavně – proč jej každý chápe trošku jinak? Pro odpověď se musíme vrátit do sedmdesátých let minulého století, do doby příchodu aplikací s interaktivním uživatelským rozhraním. Byla otázka, jak takové aplikace vůbec navrhovat. Nejjednodušší cestou bylo spojit uživatelské rozhraní s logikou aplikace v jednolitý celek. Tento přístup se dodnes často používá v Delphi, ASP.NET (Web Forms) nebo PRADO.
Rychle se ale ukázalo, že přímočarost tohoto řešení si vybere daň v okamžiku, kdy je potřeba část aplikace změnit, nebo když aplikace získá na složitosti. Kvůli přílišné provázanosti jsou zásahy do kódu nejen komplikované, ale ani nelze dost dobře testovat. Řešením je oddělit kód uživatelského rozhraní od kódu aplikační logiky, označovaného jako model. Samotné rozhraní lze přitom ještě rozdělit na vrstvu, která obstarává výstup na monitor (view) a vrstvu zpracovávající vstupy z klávesnice nebo myši (controller). Poznámka: v sedmdesátých letech místo monitoru, klávesnice a myši používali tuším elektronku, děrovačku a dřevěnou pramyš.

Architektura Model–View–Controller
Model o existenci pohledu (tj. view) nebo kontroleru neví. Pohled a kontroler jsou dvojčata, děvče a chlapec. Děvče prezentuje data modelu, jak nejlépe umí, a když uživatel zareaguje, úlohu převezme chlapec a zpracovanou reakci doručí modelu. Alespoň takto se chová historicky první implementace MVC v jazyce Smalltalk-80.
Všechno je jinak
Mám pro vás překvapení: originální návrh MVC z roku 1979 (v PDF, na straně tři je dokonce skvrna od Trygveovy prakávy). Na celé věci je pikantní, že spíše než o MVC jde o MVCE, kde „E“ znamená Editor. K čemu slouží tajemný editor? Kupodivu jde o totéž, co se ve zmíněné první implementaci nazývá Controller a je dokonce společně s View reprezentován jedinou třídou. A pozor: v původním návrhu spojení mezi modelem a controller/editorem obstarává view.
Proč vás tu drtím rádobyzajímavostmi z let, kdy vaši rodiče nacvičovali spartakiádu? Historický exkurz měl ukázat, jak různorodé bylo pojetí MVC už v okamžiku vzniku. Byla to holt doba pionýrská. Nechápejte proto MVC dogmaticky! Jinak budete mít více otázek než odpovědí.
Vhodnější je MVC vnímat obecněji, jako princip mající za cíl přemostit mezeru mezi lidským mentálním modelem a modelem počítačovým, jehož ideálu zatím nebylo dosaženo. Proto se také mohou různé MVC implementace podstatně lišit. Mimochodem, Trygve Reenskaug oznámil, že problematika MVC má daleko více aspektů, než si v roce 1979 uvědomoval, a začal před pár lety pracovat na novém návrhu.
Architektura Nette Frameworku
Nette Framework prošel dlouhým vývojem, na jehož počátku byla (dnes si troufám říci) revoluční myšlenka: nechť je odkaz totéž, co zavolání funkce.
Co je pro programátora přirozenějšího, než zavolat funkci či metodu a předat jí argumenty? Cílem bylo oprostit se od uvažování v rovině URL, kde se nejprve musí nějak poskládat hypertextový odkaz, aby se poté mohl pomocí pravidel mod_rewrite ještě přeskupit a nakonec byl v cílové stránce analyzován. Zároveň jde o protipól přístupu ASP.NET, které umí na odkazy zavěsit handler.
Princip si nejlépe ukážeme na příkladu. Nevím jak vy, ale já bych si dal dobrou kávu, tak co si naprogramovat aplikaci „Automat na kávu“?
Uvnitř automatu je zásobník na suroviny a mince, tedy databáze. Zásobník je součástí modelu, jehož API bude tvořit jediná metoda buyCoffee($money)
a konstanta COFFEE_PRICE
určující cenu nápoje. Všimněte si, že model splňuje požadavek nezávislosti na uživatelském rozhraní a bude i snadno testovatelný. Vzhled aplikace lze měnit bez obav, že bychom museli zasáhnout do datové části nebo aplikační logiky.
class Model extends Object
{
const COFFEE_PRICE = 10;
public function buyCoffee($money)
{
if ($money < self::COFFEE_PRICE) {
return FALSE;
}
... // uschovej peníze, vyrob kávu
return TRUE;
}
}
Přístupme k uživatelské obsluze automatu (angl. vending machine). Tedy třídě, která bude přímo komunikovat s modelem a prezentovat jej v lidsky přívětivé formě. Odsud název presenter. API této třídy bude nabízet metodu pro přijetí vhozených mincí a pro vydání kávy. Zároveň bude počítat, kolik peněz již bylo vhozeno, a komunikovat s uživatelem pomocí displeje (prezentační logika).
Následující kód je nástin, jak by presenter mohl vypadat. Za chvíli jej ještě mírně upravíme.
// use NetteApplicationPresenter;
class MachinePresenter extends Presenter
{
/**
* Hodnota vhozených mincí.
*/
public $money = 0;
public function __construct()
{
// na displeji, který je součástí šablony, zobrazíme výzvu k vhození peněz
$this->template->display = 'Vhoď ' . Model::COFFEE_PRICE . ' Kč';
}
public function insert($coin)
{
// zvýšíme hodnotu vhozených mincí
$this->money += max(0, (int) $coin);
// na displeji zobrazíme celkovou částku
$this->template->display = $this->money . ' Kč';
}
public function buy()
{
// příkaz pro model
$model = new Model;
$result = $model->buyCoffee($this->money);
if ($result) {
$this->money = 0; // vynulujeme částku (automat nevrací)
$this->template->display = 'Dobrou chuť';
$this->giveCoffee(); // vydáme uživateli kávu
} else {
$this->template->display = 'Málo peněz';
}
}
}
// příklad použití
$presenter = new MachinePresenter;
$presenter->insert(5); // vložíme 5 Kč
$presenter->insert(2); // vložíme 2 Kč
$presenter->insert(2); // vložíme ještě 2 Kč
$presenter->buy(); // necháme si vydat kávu
Zbývá nám navrhnout vizuální podobu uživatelského rozhraní. To může vypadat jako skutečný automat na kávu, vedle něhož vyskládáme obrázky mincí, které se kliknutím vhodí dovnitř. Na automat umístíme displej a tlačítko pro vydání kávy. Kromě grafiky připravíme HTML šablonu:
<body>
<div id="machine"><!-- automat -->
<p id="display"><?php echo htmlSpecialChars($display) ?></p>
<a href="..."><img src="images/button.png" alt="Kup kávu" /></a>
</div>
<ul id="coins"><!-- mince -->
<li><a href="..."><img src="images/coin-1.png" alt="Vhoď 1 Kč" /></a></li>
<li><a href="..."><img src="images/coin-2.png" alt="Vhoď 2 Kč" /></a></li>
<li><a href="..."><img src="images/coin-5.png" alt="Vhoď 5 Kč" /></a></li>
<li><a href="..."><img src="images/coin-10.png" alt="Vhoď 10 Kč" /></a></li>
</ul>
</body>
Všechny odkazy zatím míří do prázdna. Jak už víte, Nette Framework chápe odkaz jako zavolání funkce. U jednotlivých odkazů tedy rovnou napíšeme, kterou z výše uvedených metod mají zavolat. Obrázek pětikoruny zavolá insert(5)
. Nemůže to být jednodušší. Nemuže to být přímočařejší!
Je pochopitelné, že nelze do šablony přímo psát, např.:
<a href="<?php $presenter->insert(5) ?>"><img src="images/coin-5.png" alt="Vhoď 5 Kč" />
protože PHP by příkaz vykonalo již při vykreslování šablony. My ho chceme vykonat až poté, co na něj uživatel klikne. Zápis proto jen mírně modifikujeme:
<a href="<?php echo $presenter->link('insert!', 5) ?>"><img src="images/coin-5.png" alt="Vhoď 5 Kč" />
a to je vše! Jakmile uživatel klikne na obrázek mince, zavolá se $presenter->insert(5)
. Nic víc nemusíme řešit!
Z bezpečnostního hlediska ale není dobré, aby bylo takto možné zavolat jakoukoliv metodu třídy MachinePresenter. Volatelné (sic) metody je potřeba nějakým způsobem označit. V úvahu připadá několik způsobů, z nichž nejschůdnější je asi prefixování, tedy přilepení klíčového slova před název metody. Pro naši situaci Nette používá prefix handle
, takže metody insert()
a buy()
přejmenujeme na handleInsert()
a handleBuy()
.
Ještě je třeba zajistit, aby obsah proměnné $money
byl trvalý, aby se nevynulovala při každém HTTP požadavku. K tomu slouží tzv. anotace @persistent
:
/**
* Hodnota vhozených mincí.
* @persistent - proměnná se bude přenášet mezi HTTP požadavky
*/
public $money = 0;
A to je vše. Kompletní aplikaci si stáhněte, rozbalte a v prohlížeči otevřete soubor index.php
, který se nachází v adresáři document_root
. A dejte si kávu…

Nabízíme dobrou brazilskou kávu.
Presenter v Nette Frameworku má podobnou roli jako kontroler v MVC. Vybírá pohled (view) a předává mu model, nebo data z modelu. Udržuje stav persistentních proměnných. A především zpracovává reakce uživatele. Ty se dají rozdělit na tři typy:
- změna pohledu (s tím jsme se v jednoduchém Automatu na kávu nesetkali)
- změna stavu (po vhození mince)
- příkaz modelu (po stisknutí tlačítka)

Architektura Model–View–Presenter v Nette Framework
A co URL?
Ačkoliv Nette Framework nás zcela odstínil od úvah nad URL, neznamená to, že nemůžete jejich podobu ovlivnit. Ba právě naopak! Framework nám dává nad URL absolutní kontrolu. Jen prostě v tuto chvíli by úvahy nad jejich tvarem odváděly pozornost od skutečného úkolu. Podobu URL můžeme rozhodnout později. A bez nutnosti cokoliv měnit v samotné aplikaci.
Vychutnejte si voňavou kávu z Nette automatu, v příštím díle se k němu vrátíme a naučíme ho vařit AJAXově.
Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Pravidelně pořádá školení pro tvůrce webových aplikací, vyvíjí open-source knihovny Texy, dibi a Nette Framework.
Přehled komentářů