Lekce 4 - Dokončení kalkulačky v Nette
V minulé lekci, První aplikace v Nette, jsme se zaměřili na MVP a dokončili model.
V dnešní lekci se můžeme vrhnout na presenter. Takže bez dalšího otálení jdeme na to!
Presenter
app/Presenters/CalculatorPresenter.php
V šabloně Nette webového projektu máme ve složce
app/Presenters/
již vytvořený soubor
HomepagePresener.php
. My ho však smažeme nebo přejmenujeme a
vytvoříme si zde náš vlastní CalculatorPresenter.php
a v něm
třídu CalculatorPresenter
, která dědí z
Nette\Application\UI\Presenter
. Tím jsme si vytvořili základní
kostru presenteru, do které ještě doplníme výchozí metodu pro
vykreslování šablony, aby ve výsledku vypadala nějak takto:
<?php declare(strict_types=1); namespace App\Presenters; use Nette\Application\UI\Presenter; /** * Presenter kalkulačky. * @package App\Presenters */ final class CalculatorPresenter extends Presenter { /** @var int|null výsledek operace nebo null */ private $result = null; /** Výchozí vykreslovací metoda tohoto presenteru. */ public function renderDefault() { // Předání výsledku do šablony. $this->template->result = $this->result; } }
Jak můžeme vidět, předáváme zde výsledek do šablony, abychom ho mohli
zobrazit. Výsledek je zatím null
, ale to za chvíli napravíme
Získání modelu z presenteru
K tomu, abychom získali výsledek, budeme potřebovat přístup k modelu.
Ten se v Nette standardně zajišťuje pomocí Dependency Injection
(zkráceně DI) a to tak, že v konfiguračním souboru common.neon
zaregistrujeme náš model jako anonymní službu (service) a pak si ho necháme
injektovat do našeho presenteru.
app/config/common.neon
Nejdříve tedy najdeme tento soubor a upravíme přidáním služby:
... services: router: App\Router\RouterFactory::createRouter - App\Model\CalculatorManager
Nyní máme několik možností, jak a kde si nechat službu injektovat. Já
si zvolím standardní a asi nejpoužívanější postup a to přes konstruktor
presenteru. Do naší třídy CalculatorPresenter
tedy přidáme
následující kód:
// ... /** @var CalculatorManager Instance třídy modelu pro práci s operacemi kalkulačky. */ private $calculatorManager; /** * Konstruktor s injektovaným modelem pro práci s operacemi kalkulačky. * @param CalculatorManager $calculatorManager automaticky injektovaná třída modelu pro práci s operacemi kalkulačky */ public function __construct(CalculatorManager $calculatorManager) { parent::__construct(); $this->calculatorManager = $calculatorManager; } // ...
Nesmíme také zapomenout na import třídy
CalculatorManager
:
use App\Model\CalculatorManager;
Nyní máme přístup k našemu modelu přes
$this->calculatorManager
.
Nette formuláře v Presenterech
Máme již vše potřebné pro výpočet a předání výsledku do šablony.
Je tedy potřeba se zamyslet nad tím, jak budeme získávat data pro výpočet
od uživatele. Odpověď je jednoduchá, přes formulář. To v Nette znamená,
že si potřebujeme v presenteru zadefinovat formulář, který pak předáme do
šablony. Z něj získáme data, která předáme modelu pro výpočet a
výsledek uložíme do proměnné $this->result
. Nette nám pro
tento účel přináší systém komponent. Nám jen stačí pomocí ní
zadefinovat podobu formuláře a funkci pro jeho zpracování:
// ... /** Definice konstant pro zprávy formuláře. */ const FORM_MSG_REQUIRED = 'Tohle pole je povinné.', FORM_MSG_RULE = 'Tohle pole má neplatný formát.'; // ... /** * Vrátí formulář kalkulačky. * @return Form formulář kalkulačky */ protected function createComponentCalculatorForm() { $form = new Form; // Získáme existující operace kalkulačky a dáme je do výběru operací. $form->addRadioList('operation', 'Operace:', $this->calculatorManager->getOperations()) ->setDefaultValue(CalculatorManager::ADD) ->setRequired(self::FORM_MSG_REQUIRED); $form->addText('x', 'První číslo:') ->setHtmlType('number') ->setDefaultValue(0) ->setRequired(self::FORM_MSG_REQUIRED) ->addRule(Form::INTEGER, self::FORM_MSG_RULE); $form->addText('y', 'Druhé číslo:') ->setHtmlType('number') ->setDefaultValue(0) ->setRequired(self::FORM_MSG_REQUIRED) ->addRule(Form::INTEGER, self::FORM_MSG_RULE) // Ošetříme dělení nulou. ->addConditionOn($form['operation'], Form::EQUAL, CalculatorManager::DIVIDE) ->addRule(Form::PATTERN, 'Nelze dělit nulou.', '^[^0].*'); $form->addSubmit('calculate', 'Spočítej výsledek'); $form->onSuccess[] = [$this, 'calculatorFormSucceeded']; return $form; } /** * Funkce se vykonaná při úspěšném odeslání formuláře kalkulačky a zpracuje odeslané hodnoty. * @param Form $form formulář kalkulačky * @param ArrayHash $values odeslané hodnoty formuláře */ public function calculatorFormSucceeded(Form $form, ArrayHash $values) { // Necháme si vypočítat výsledek podle zvolené operace a zadaných hodnot. $this->result = $this->calculatorManager->calculate($values->operation, $values->x, $values->y); }
Musíme na začátku souboru zase importovat potřebné třídy:
use Nette\Application\UI\Form; use Nette\Utils\ArrayHash;
Je toho více, takže to vezmeme hezky popořadě. První metoda je
speciální metoda pro tvorbu komponent, která se definuje jako
createComponent<název_komponenty>()
a vrací požadovanou
komponentu. Jen upozorňuji, že u této metody stačí, aby byla
protected
. Pokud metoda splňuje tyto podmínky, můžeme ji poté
zavolat přímo ze šablony a to pomocí speciálního Latte makra
{control}
, což si za chvíli ukážeme.
Dále pár slov k samotnému formuláři. Definujeme zde celkem 4 prvky:
- Pole radio inputů, které slouží pro výběr operace výpočtu. Zde si můžete všimnout volání metody z modelu, která nám vrátí univerzálně všechny dostupné operace. Dále nastavujeme výchozí hodnotu na sčítání a že je povinné toho pole vyplnit.
- Text input pro zadání prvního čísla, kde nastavíme, že se jedná o typ inputu number, což platí pro prohlížeče podporující HTML5. Dále určíme výchozí hodnotu 0 a validační pravidlo, že toto pole musí být vyplněno a pouze hodnotou celého čísla.
- Text input pro zadání druhého čísla, který je identický s tím prvním, pouze obsahuje navíc pravidlo, kde ošetříme dělení nulou, jak jsem slíbil. Teď by asi byla na místě diskuze, proč jsem to ošetřil zde a ne již v modelu. Samozřejmě to lze udělat oběma způsoby. Já jsem se ale rozhodl demonstrovat, že presenter by měl zpracovat všechna data z šablony a do modelu už by měla jít jen ta data, se kterými tento model umí pracovat. Navíc zde může dojít k validaci již na straně uživatele a celkově je tento způsob efektivnější, než například vyhazovat nějaké výjimky z modelu
- Submit button jako jednoduché tlačítko pro odeslání formuláře.
Nakonec se formuláři předá funkce, která se volá při jeho úspěšném
odeslání a do parametru dostane uživatelem zadané hodnoty. Chtěl bych zde
upozornit na to, že úspěšné odeslání formuláře znamená jeho kompletní
validaci na straně klienta i serveru podle zadaných pravidel, tzn. že do
metody calculatorFormSucceeded()
se dostanou už jen hodnoty, se
kterými umí náš model pracovat. V této metodě pak jen stačí zavolat
univerzální výpočetní funkci z modelu, předat jí potřebné hodnoty od
uživatele a výsledek uložit do $this->result
. Nyní je náš
presenter již kompletní, ovšem cesta k němu není úplně jasná. Pojďme to
napravit.
Routování v Nette
Jelikož jsme nahradili původní třídu HomepagePresenter
naší vlastní, musíme upravit i router, který k němu určoval cestu, tj.
pod jakou URL ho najdeme. V Nette k tomu slouží tzv.
RouterFactory
a opět věřím, že ti bystřejší z vás si
všimli její definice v souboru common.neon
při nastavování DI.
Nyní ji potřebujeme upravit tak, aby pod výchozí URL byl právě náš
presenter.
app/router/RouterFactory.php
Takže si otevřeme příslušný soubor a takto si ho upravíme:
<?php declare(strict_types=1); namespace App\Router; use Nette; use Nette\Application\Routers\RouteList; final class RouterFactory { use Nette\StaticClass; public static function createRouter(): RouteList { $router = new RouteList; $router->addRoute('<presenter>/<action>[/<id>]', 'Calculator:default'); return $router; } }
Nyní je pod výchozí URL adresou náš presenter a jeho akce default
(metoda renderDefault()
). Teď už zbývá jen poslední část
skládačky a tou je šablona.
Šablona (View)
Jak již bylo řešeno v první lekci, Nette používá vlastní šablonovací systém Latte. V něm si teď vytvoříme šablonu pro naši aplikaci. Jako vzor si necháme původní šablonu, jen ji trochu upravíme.
app/presenters/templates/Calculator/default.latte
Nejdříve půjdeme do složky app/Presenters/templates/
a zde
přejmenujeme složku Homepage/
na Calculator/
. V ní
se poté nachází soubor default.latte
, který poslouží jako
šablona pro naši akci default a my ho pouze upravíme:
{block content} <div id="banner"> <h1 n:block=title>Kalkulačka</h1> </div> <div id="content"> <h3 n:ifset=$result>Výsledek je: {$result}</h3> {control calculatorForm} </div> <style> ...
Jak je vidět, <style>
zachováme a upravíme pouze kód
nad ním.
Nejprve upravíme <h1>
, který díky makru
n:block=title
slouží zároveň i jako html
<title>
pro celou stránku.
Následně vymažeme obsah <div id="content">
a doplníme
naše vypsání výsledku např. do <h3>
. Pomocí
{$result}
se zde zobrazí data předaná z presenteru a pomocí
makra n:ifset
se výsledek zobrazí pouze pokud není
null
.
Celý definovaný formulář pak vykreslíme do šablony pomocí již
zmíněného makra {control <název_komponenty>}
a názvu
našeho formuláře (metody createComponent*()
). A tím je naše
práce na šabloně i na celém projektu hotová. Pokud nyní otevřeme URL
projektu, uvidíme zde plně funkční kalkulačku:
Můžete na ní vyzkoušet všechny chytáky, které vás napadnou
Pokud vám není cokoli jasné, stáhněte si projekt z přílohy a projeďte si jak se data předávají z modelu do presenteru a odtud do šablony. Kódu v aplikaci tolik nemáme, po chvíli byste se měli zorientovat. Pokud ani to nepomůže, určitě vám MVC/MVP objasní seriál Jednoduchý redakční systém v PHP objektově (MVC).
To je pro dnešní lekci vše.
V příští lekci, Jednoduchý redakční systém v Nette - Struktura projektu, začneme úplně novou pořádnou aplikaci v Nette
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 1337x (877.59 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP