Java týden Java týden
Pouze tento týden sleva až 80 % na celý Java e-learning!
Brno? Vypsali jsme pro vás nové termíny školení OOP v Brně!

Lekce 4 - Dokončení kalkulačky v Nette

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, První aplikace v Nette, jsme dokončili model a nyní se můžeme přesunout na presenter. Takže bez dalšího otálení jdeme na to! :)

Presenter

app/presenter­s/CalculatorPre­senter.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 výsledku vypadala nějak takto:

<?php

namespace App\Presenters;

use Nette\Application\UI\Presenter;
use App\Model\CalculatorManager;
use Nette\Application\UI\Form;
use Nette\Utils\ArrayHash;

/**
 * Presenter kalkulačky.
 * @package App\Presenters
 */
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 vidíte, 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 config.neon zaregistrujeme náš model jako anonymní službu (service) a pak si ho necháme injektovat do našeho presenteru.

app/config/con­gif.neon

Nejdříve tedy najdeme tento soubor a upravíme přidáním služby:

...
services:
    router: App\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;
}
// ...

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 $this->result. Nette nám pro tento účel přináší systém komponent a speciálně pro formuláře v presenterech má již připravenou třídu Nette\Application\UI\Form. 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:')
        ->setType('number')
        ->setDefaultValue(0)
        ->setRequired(self::FORM_MSG_REQUIRED)
        ->addRule(Form::INTEGER, self::FORM_MSG_RULE);
    $form->addText('y', 'Druhé číslo:')
        ->setType('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, $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);
}

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 jí 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:

  1. 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.
  2. 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.
  3. 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. :)
  4. 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 config.neon při nastavování DI. Nyní ji potřebujeme upravit tak, aby pod výchozí URL byl právě náš presenter.

app/router/Rou­terFactory.php

Takže si otevřeme příslušný soubor a takto si ho upravíme:

<?php

namespace App;

use Nette\Application\IRouter;
use Nette\Application\Routers\Route;
use Nette\Application\Routers\RouteList;
use Nette\StaticClass;

class RouterFactory
{
    use StaticClass;

    /**
     * @return IRouter
     */
    public static function createRouter()
    {
        $router = new RouteList;
        $router[] = new Route('<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/presenter­s/templates/Cal­culator/defau­lt.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řete URL projektu, uvidíte zde plně funkční kalkulačku a 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 opravdu vše, ale nebojte. V příští lekci, Jednoduchý redakční systém v Nette - Struktura projektu, začneme úplně novou pořádnou aplikaci v Nette ;)


 

Stáhnout

Staženo 875x (1.22 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

 

Článek pro vás napsal Jindřich Máca
Avatar
Jak se ti líbí článek?
19 hlasů
Autor se věnuje převážně webovým technologiím, ale má velkou zálibu ve všem vědeckém, nejen ze světa IT. :-)
Předchozí článek
První aplikace v Nette
Všechny články v sekci
Základy Nette frameworku
Miniatura
Následující článek
Jednoduchý redakční systém v Nette - Struktura projektu
Aktivity (10)

 

 

Komentáře

Avatar
Martin Konečný (pavelco1998):2.7.2015 13:10

Pokud si správně pamatuji, pak by se na konstruktor presenteru nemělo sahat.
Místo toho je pro inicializaci proměnných atp. metoda startup().

edit: případně použít inject metody

/** @var Nazev\Tridy */
public $nazevTridy;

/**
 * @param Nazev\Tridy
 */
public function injectNazevTridy(Nazev\Tridy $nz)
{
  $this->nazevTridy = $nz;
}

A nebo ještě by mělo jít auto inject pomocí anotace

/**
 * @var Nazev\Tridy
 * @inject
 */
public $nazevTridy;
Editováno 2.7.2015 13:12
Odpovědět  +1 2.7.2015 13:10
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Martin Konečný (pavelco1998)
Jindřich Máca:2.7.2015 13:27

Bohužel si to pamatuješ asi špatně nebo pro hodně starou verzi, protože v dokumentaci - http://doc.nette.org/cs/2.3/di-usage#… na prvním místě stojí "Předávání konstruktorem". ;)

Ještě bych k tomu doplnil tuto citaci: "Tato deklarace závislostí je vhodná pro povinné závislosti, které třída nezbytně potřebuje ke své funkci, neboť bez ní nepůjde instanci vytvořit." A to je přesně náš případ, protože bez našeho modelu kalkulačka neumí nic spočítat a tudíž ho potřebuje ke své funkci. :)

Editováno 2.7.2015 13:30
 
Odpovědět 2.7.2015 13:27
Avatar
Odpovídá na Jindřich Máca
Martin Konečný (pavelco1998):2.7.2015 17:07

Hmm, pak se asi opět něco v novější verzi měnilo. Dřív k tomu byla metoda startup(), ve které sis mohl sáhnout pro jakoukoliv službu nebo parametr do DI containeru.

public function startup()
{
  parent::startup();
  $this->calculatorManager = $this->context->getService("calculatorManager");
  // $this->context je instancí DI containeru
}

Nicméně pokud je to presenter, pak sice potřebuje calculatorManager ke své funkci, ale nemusí ho nutně dostávat konstruktorem. Při injektování služeb se vytvoří instance automaticky až v případě, že daný objekt potřebuješ (tzn. když použiješ $this->calculatorMa­nager).

V tomhle je tak trochu nevýhoda FW. Uděláš na to pár návodů a za měsíc je můžeš zahodit, protože se způsob použití změní :D

Editováno 2.7.2015 17:08
Odpovědět  +1 2.7.2015 17:07
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Martin Konečný (pavelco1998)
Jindřich Máca:2.7.2015 17:42

Jo, s tou nevýhodou FW máš naprostou pravdu a zrovna Nette prošlo už několikrát radikálními změnami. Ale zase Tě musím trochu poopravit, protože když si pozorně přečteš http://doc.nette.org/cs/2.3/di-usage#…, tak zjistíš 2 věci:

  1. Metody typu inject* a anotaci @inject je vhodné používat jen v určitý případech a za nejčistší řešení povinných závislostí je považováno právě předání konstruktorem. :)
  2. To co říkáš, o automatické instanci služeb v případě jejich potřeby, tak to platí i pro služby předávané přes konstruktor, protože je pořád předáváš pomocí DI. ;)
 
Odpovědět 2.7.2015 17:42
Avatar
Odpovídá na Jindřich Máca
Martin Konečný (pavelco1998):2.7.2015 18:58

Předávání konstruktorem také používám nejčastěji, ale ne u presenterů, tam jsem si zvyknul vytvářet instance pomocí startup() metody nebo pomocí inject* metod (protože to byl dříve naopak nedoporučovaný způsob).
Dneska bych kód svého projektu taky psal jinak, ale jelikož už tam toho je napsaný hodně, je pro mě výhodnější to dodělat takhle a případně pak udělat celkový refaktoring. Mezitim se ale Nette zase trochu změní :D

Odpovědět  +1 2.7.2015 18:58
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Martin Konečný (pavelco1998)
Jindřich Máca:2.7.2015 19:43

Já to chápu a upřímně si myslím, že na tom zase tolik nezáleží, hlavně, že se to tam předá pomocí DI. :D Jenom nechci, aby si čtenáři mysleli, že je učím nějaké špatné návyky. :)

 
Odpovědět 2.7.2015 19:43
Avatar
Odpovídá na Jindřich Máca
Martin Konečný (pavelco1998):2.7.2015 20:31

Jo jo v pohodě, v Nette lze psát více způsoby, jen je někdy těžké si ze začátku určit, který z nich bude nejlepší.
Mým cílem samozřejmě neni tě nějak opravovat, ale když si něčím nejsem jistý, tak se radši poptám. Sice v Nette už nějakou chvíli dělám, ale poslední dobou jsem novinky nepročítal :)

Odpovědět  +1 2.7.2015 20:31
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
filipfr
Člen
Avatar
filipfr:29.8.2015 13:05

Ahoj,
mě asi uniká, kdy a kdo vytvoří dané instance modelu a presenteru. O to se nějak stará fw?
Díky

 
Odpovědět  +1 29.8.2015 13:05
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na filipfr
Jindřich Máca:6.9.2015 14:47

Ano, o instance se stará Nette. ;-)

 
Odpovědět 6.9.2015 14:47
Avatar
Milan Gallas
Redaktor
Avatar
Milan Gallas:29.9.2015 20:27

Projekt jsem si stáhnul a spustil. Vyhodilo mi to chybovou hlášku:

Parse Error

syntax error, unexpected '['    search►

Source file

File: ...\app\presenters\CalculatorPresenter.php:89

79:                ->addRule(Form::INTEGER, self::FORM_MSG_RULE);
80:            $form->addText('y', 'Druhé číslo:')
81:                ->setType('number')
82:                ->setDefaultValue(0)
83:                ->setRequired(self::FORM_MSG_REQUIRED)
84:                ->addRule(Form::INTEGER, self::FORM_MSG_RULE)
85:                // Ošetříme dělení nulou.
86:                ->addConditionOn($form['operation'], Form::EQUAL, CalculatorManager::DIVIDE)
87:                ->addRule(Form::PATTERN, 'Nelze dělit nulou.', '^[^0].*');
88:            $form->addSubmit('calculate', 'Spočítej výsledek');
89:            $form->onSuccess[] = [$this, 'calculatorFormSucceeded'];
90:            return $form;
91:        }
92:
93:        /**

Jak je to s tím řádkem

$form->onSuccess[] = [$this, 'calculatorFormSucceeded'];

????

 
Odpovědět 29.9.2015 20:27
Avatar
Odpovídá na Milan Gallas
Martin Konečný (pavelco1998):29.9.2015 20:53

Zřejmě máš starší verzi PHP, která nepodporuje zápis pole pomocí hranatých závorek.
Zkus použít

$form->onSuccess[] = array($this, 'calculatorFormSucceeded';

// nebo
$form->onSuccess[] = $this->calculatorFormSucceeded;
Odpovědět  +1 29.9.2015 20:53
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Milan Gallas
Redaktor
Avatar
 
Odpovědět 29.9.2015 21:05
Avatar
johnsilver2010:9.11.2015 12:39

V presenteru při vytváření komponenty to hodí chybu - Call to a member function getOperations() on null . Co dělám špatně ?

 
Odpovědět 9.11.2015 12:39
Avatar
Vít Cigánek:11.11.2015 19:00

Hm, mi to hlasí tohle:

Nette\InvalidStateException

Service of type App\Forms\SignFormFactory used in @var annotation at App\Presenters\SignPresenter::$factory not found. Did you register it in configuration file?       search►

Source file

Call stack

...\vendor\nette\di\src\DI\Extensions\InjectExtension.php:41    source  Nette\DI\Extensions\InjectExtension::   checkType (arguments)

...\vendor\nette\di\src\DI\Extensions\InjectExtension.php:29    source  Nette\DI\Extensions\InjectExtension->   updateDefinition (arguments)

...\vendor\nette\di\src\DI\Compiler.php:207     source  Nette\DI\Extensions\InjectExtension->   beforeCompile ()

...\vendor\nette\di\src\DI\Compiler.php:141     source  Nette\DI\Compiler->     generateCode (arguments)

...\vendor\nette\bootstrap\src\Bootstrap\Configurator.php:268   source  Nette\DI\Compiler->     compile ()

inner-code      Nette\Configurator->    generateContainer (arguments)

...\vendor\nette\di\src\DI\ContainerLoader.php:113      source  call_user_func_array (arguments)

...\vendor\nette\di\src\DI\ContainerLoader.php:78       source  Nette\DI\ContainerLoader->      generate (arguments)

...\vendor\nette\di\src\DI\ContainerLoader.php:43       source  Nette\DI\ContainerLoader->      loadFile (arguments)

...\vendor\nette\bootstrap\src\Bootstrap\Configurator.php:222   source  Nette\DI\ContainerLoader->      load (arguments)

...\app\bootstrap.php:19        source  Nette\Configurator->    createContainer ()

 9:
10:    $configurator->setTempDirectory(__DIR__ . '/../temp');
11:
12:    $configurator->createRobotLoader()
13:        ->addDirectory(__DIR__)
14:        ->register();
15:
16:    $configurator->addConfig(__DIR__ . '/config/config.neon');
17:    $configurator->addConfig(__DIR__ . '/config/config.local.neon');
18:
19:    $container = $configurator->createContainer();
20:
21:    return $container;
22:
...\www\index.php:3     source  require (arguments)

Exception

Environment

HTTP request

HTTP response

Report generated at 2015/11/11 18:55:21
http://localhost/…lator-nette/
PHP 5.6.3
Apache/2.4.10 (Win32) OpenSSL/1.0.1i PHP/5.6.3

 
Odpovědět 11.11.2015 19:00
Avatar
Odpovídá na Vít Cigánek
Martin Konečný (pavelco1998):11.11.2015 19:13

Jak vypadají tvé configy? Přesněji sekce services. Nette ti hlásí, že se nenašla žádná třída se jménem App\Forms\Sig­nFormFactory

Odpovědět 11.11.2015 19:13
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Vít Cigánek:11.11.2015 20:06

Po přenatavení services funguje....

services:
        - App\Model\CalculatorManager
          s timhle mi to řve chybu tak jsem to dal pryč ///- App\Model\UserManager
        - App\Forms\SignFormFactory
        router: App\RouterFactory::createRouter
 
Odpovědět 11.11.2015 20:06
Avatar
Vít Cigánek:16.11.2015 17:33

hm, tak jsem měl dneska školení v nete a nikde tady není psané že se v souboru app/config/con­gif.neon přidavají service pomocí tabulatoru.

services:
        - App\Model\CalculatorManager
        router: App\RouterFactory::createRouter
<style>
        html { font: normal 18px/1.3 Georgia, "New York CE", utopia, serif; color: #666; -webkit-text-stroke: 1px rgba(0,0,0,0); overflow-y: scroll; }
        body { background: #3484d2; color: #333; margin: 2em auto; padding: 0 .5em; max-width: 600px; min-width: 320px; }
        a { color: #006aeb; padding: 3px 1px; }
        a:hover, a:active, a:focus { background-color: #006aeb; text-decoration: none; color: white; }
        #banner { border-radius: 12px 12px 0 0; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAB5CAMAAADPursXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGBQTFRFD1CRDkqFDTlmDkF1D06NDT1tDTNZDk2KEFWaDTZgDkiCDTtpDT5wDkZ/DTBVEFacEFOWD1KUDTRcDTFWDkV9DkR7DkN4DkByDTVeDC9TDThjDTxrDkeADkuIDTRbDC9SbsUaggAAAEdJREFUeNqkwYURgAAQA7DH3d3335LSKyxAYpf9vWCpnYbf01qcOdFVXc14w4BznNTjkQfsscAdU3b4wIh9fDVYc4zV8xZgAAYaCMI6vPgLAAAAAElFTkSuQmCC); }
        h1 { font: inherit; color: white; font-size: 50px; line-height: 121px; margin: 0; padding-left: 4%; background: url(http://files.nette.org/images/[email protected]) no-repeat 95%; background-size: 130px auto; text-shadow: 1px 1px 0 rgba(0, 0, 0, .9); }
        @media (max-width: 600px) {
                h1 { background: none; font-size: 40px; }
        }
        #content { background: white; border: 1px solid #eff4f7; border-radius: 0 0 12px 12px; padding: 10px 4%; overflow: hidden; }
        h2 { font: inherit; padding: 1.2em 0; margin: 0; }
        img { border: none; float: right; margin: 0 0 1em 3em; }
</style>

Nějaka podobna blbost platí u stylu... musí tam být mezera html {/mezera/ font: normal 18px/1.3 Georgia,

když ne tak to hodí chybu.

 
Odpovědět  +1 16.11.2015 17:33
Avatar
Mazwor
Člen
Avatar
Mazwor:14.3.2016 15:26

Při práci s fomulářem jsem musel v presenteru (CalculatorPre­senter) kód upravit tak, že buď při každém odkazu na třídu form nestačilo napsat Form, ale bylo zapotřebí Nette\Applica­tion\UI\Form, případně jsem musel třídu na začátku načíst pomocí

use Nette\Application\UI\Form;

Stejně tomu je i při volání modelu z presenteru a podobně. Např. začátek presenteru pak vypadal následovně:

<?php

namespace App\Presenters;

use Nette;
use App\Model\CalculatorManager;
use Nette\Application\UI\Form;


class CalculatorPresenter ...

Chtěl bych se tedy zeptat, zda to tak dělám správně a autor to pouze neuvádí, protože to nejspíš považuje za samozřejmost, nebo zda mi něco uniká a celá kalkulačka by měla fungovat i bez této drobné úpravy?

Každopádně super seriál, který mě konečně donutil začít s Nette a nepsat zdlouhavě celé projekty čistě v PHP. Díky za něj! :)

Odpovědět 14.3.2016 15:26
Pořádek je pro blbce, inteligent ovládá chaos. :D
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Mazwor
Jindřich Máca:15.3.2016 12:04

Ahoj,

jsem moc rád, že se Ti seriál líbí. :-) Co se týče Tvého dotazu, tak máš naprostou pravdu a tyto "hlavičky" tam samozřejmě patří. Nějak jsem to opravdu považoval za samozřejmost, ale už jsem pro jistotu poslal ke schválení doplněnou verzi článku. Pokud by Tě zajímalo, jak jsem je konkrétně napsal já, tak samozřejmě v přiloženém archívu u článku jsou v příslušných zdrojový kódech uvedeny. ;-)

 
Odpovědět  +1 15.3.2016 12:04
Avatar
SolusLupusUmbra
Redaktor
Avatar
SolusLupusUmbra:9.7.2017 21:13

Zajímavé.
K otázce, zda kontrolovat data v presenteru nebo v manageru - nebylo by nejlepší kontrolovat to v obojím, nezávisle na sobě v duchu "programátor nemusí být paranoidní, ale hodně to pomáhá"?

 
Odpovědět 9.7.2017 21:13
Avatar
SolusLupusUmbra
Redaktor
Avatar
SolusLupusUmbra:9.7.2017 21:18

abych doplnil svou myšlenku - na straně klienta validovat formulář pomocí javascriptu, aby se kvůli každé chybě nemuselo vše odesílat

 
Odpovědět 9.7.2017 21:18
Avatar
Odpovídá na SolusLupusUmbra
Martin Konečný (pavelco1998):9.7.2017 21:56

Kontrola v JS je dobrá v tom, že je "ihned", je přívětivější pro uživatele a nezatěžuje server dalším požadavkem. Na serveru se to ale musí validovat vždy, takže JS je jen takový fajn doplněk.

Odpovědět  +2 9.7.2017 21:56
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
SolusLupusUmbra
Redaktor
Avatar
Odpovídá na Martin Konečný (pavelco1998)
SolusLupusUmbra:9.7.2017 23:03

To je samozřejmé, má myšlenka spíš byla, zda na serveru ta data kontrolovat jen jednou (a to buď v presenteru, jak to doporučuje autor, nebo v modelu) nebo jestli je lepší je kontrolovat dvakrát - v presenteru i v modelu. A jak říkáš, u klienta pro rychlost a jeho pohodlnost validovat JS
->taková malá diskuze, co je lepší, kdy a proč

 
Odpovědět 9.7.2017 23:03
Avatar
Odpovídá na SolusLupusUmbra
Martin Konečný (pavelco1998):9.7.2017 23:34

Já osobně nevidím moc důvod, proč by se verifikace na straně klienta dělat neměla, pokud by nešlo o typ kontroly, že si z DB vytáhneš všechna hesla, uložíš je v JS do pole a pak zadané heslo ve formuláři kontroluješ u klienta :D

Pokud jde o drobnosti typu správná délka vstupu, správné znaky, zaškrtnutý checkbox atp., tak mě nenapadá, proč by se validace u klienta měla nevyplatit, nebo být dokonce špatná. V Nette je fajn, že to tu JS validaci generuje samo a dokonce dovoluje přidat vlastní validační pravidla.

Odpovědět  +1 9.7.2017 23:34
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Bebbana
Člen
Avatar
Bebbana:14.9.2017 10:32

Ahoj, vše udělané podle návodu, ale hlásí mi to tuto výjimku:

"Nette\Invalid­StateException

Found section 'router' in configuration, but corresponding extension is missing."

Prosím, poraďte. :-? ;-(

 
Odpovědět 14.9.2017 10:32
Avatar
Bebbana
Člen
Avatar
Bebbana:14.9.2017 10:34

Můj config.neon :

parameters:


application:
        errorPresenter: Error
        mapping:
                *: App\*Module\Presenters\*Presenter


session:
        expiration: 14 days


database:
        dsn: 'mysql:host=127.0.0.1;dbname=test'
        user:
        password:
        options:
                lazy: yes


services:
    - App\Model\UserManager
    - App\Forms\FormFactory
    - App\Forms\SignInFormFactory
    - App\Forms\SignUpFormFactory
    - App\Model\CalculatorManager
router: App\RouterFactory::createRouter
 
Odpovědět 14.9.2017 10:34
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Bebbana
Jindřich Máca:14.9.2017 18:31

Ahoj, jedná se s největší pravděpodobností pouze o hloupou chybu v odsazení. Před označením router:... musí totiž být stejné odsazení jako před ostatními uvedenými službami. Bohužel formát Neon je na chyby podobného druhu velmi náchylný. :-)

 
Odpovědět  +1 14.9.2017 18:31
Avatar
Bebbana
Člen
Avatar
Odpovídá na Jindřich Máca
Bebbana:18.9.2017 9:38

Děkuji, už mi to funguje :)

 
Odpovědět 18.9.2017 9:38
Avatar
Tomáš Korduliak:22.9.2017 21:31

Ahoj, po stažení a zadání číselných hodnot do kalkulačky hlásí laděnka chybu (A non well formed numeric value encountered).

Čím to může být? Děkuji moc za odpověď.

Odpovědět 22.9.2017 21:31
Každý další den je výzvou poznávat nové věci
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Tomáš Korduliak
Jindřich Máca:23.9.2017 23:37

Ahoj, chyba je nejspíše způsobena kompatibilitou starší verze Nette, ve které je psaný seriál, a nového PHP 7. Nette totiž PHP 7 podporuje, ale až od novější verze. :)

 
Odpovědět  +1 23.9.2017 23:37
Avatar
Renko Frick
Člen
Avatar
Renko Frick:25.9.2017 13:05

kua :-/ani mne to vobec nejde. skopiroval som si kalkulacku, spustil a:

Nette\DI\Servi­ceCreationExcep­tion

Method App\RouterFac­tory::createRou­ter() used in service 'routing.router' is not callable

 
Odpovědět 25.9.2017 13:05
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Renko Frick
Jindřich Máca:25.9.2017 20:33

Ahoj, tohle bývá dost často kvůli cache, zkoušel jsi ji celou smazat? :)

 
Odpovědět 25.9.2017 20:33
Avatar
Renko Frick
Člen
Avatar
Odpovídá na Jindřich Máca
Renko Frick:2.10.2017 12:45

prisiel som na to, com to bolo :-D
v tutorialy bolo spomenuty (alebo je pridany) presenter CalculatorPre­senter. ja som vyrobil novy a nechal som v adresary ten povodny - HomePresenter. ten to robil ;)
ked som ho vymazal, je to OK ;)
diky za ochotu

Editováno 2.10.2017 12:46
 
Odpovědět  +1 2.10.2017 12:45
Avatar
Jiří Šír
Člen
Avatar
Jiří Šír:31.1.2018 10:57

Zdravím, rád bych se v PHP trochu pohnul směrem kupředu, proto jsem se začal zabývat Nette.

Jedu pěkně postupně, ale při stažení této kalkulačky ovšem dostávám chybové hlášení:

Fatal error: Cannot use 'Object' as class name as it is reserved in C:\xampp\htdoc­s\calculator-nette\vendor\net­te\utils\src\U­tils\Object.php on line 54

Poradí, prosím, někdo.
Předem díky ;-)

Editováno 31.1.2018 10:58
 
Odpovědět 31.1.2018 10:57
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Jiří Šír
Jindřich Máca:31.1.2018 11:52

Ahoj a jakou používáš verzi PHP? Tento projekt je psaný ještě ve starší verzi Nette 2.3, která není kompatibilní s PHP verzí 7 a vyšší.

Pokud je toto Tvůj problém, řešením je např. aktualizovat verzi Nette v projektu na 2.4, což lze poměrně snadno provést pomocí nástroje Composer.

 
Odpovědět 31.1.2018 11:52
Avatar
Jiří Šír
Člen
Avatar
Jiří Šír:31.1.2018 15:16

Díky za rychlou reakci.
Mám právě PHP ve verzi 7.2.0. Provedl jsem tedy update v Composeru, ale bohužel se shodným výsledkem.
Obsah composer.json přikládám v printscreenu.
Díky za prověření...

 
Odpovědět 31.1.2018 15:16
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Jiří Šír
Jindřich Máca:31.1.2018 21:59

Ta chyba se obecně vztahuje na celé Nette, všechny projekty i všechna rozšíření a spočívá v tom, že od verze PHP 7.2 se nemůže třída jmenovat Object, přičemž Nette tuto třídu do verze 2.3 mělo a docela hojně používalo. Je také použita i v tomto projektu a tudíž je nutné její použití všude předělat a to na trait SmartObject. Je to i oficiálně reportované viz. https://github.com/…s/issues/146

Takže buď tedy všude v projektu předěláš použití třídy Object na použití trait SmartObject nebo zkrátka musíš downgradovat verzi PHP. :-`

 
Odpovědět 31.1.2018 21:59
Avatar
Jiří Šír
Člen
Avatar
Odpovídá na Jindřich Máca
Jiří Šír:1.2.2018 11:41

Díky moc za odkaz.
Po použití SmartObject je projekt funkční.
Ještě jednou díky za rady a ať se daří ;-)

 
Odpovědět  +2 1.2.2018 11:41
Avatar
Libor
Člen
Avatar
Libor:19.2.2018 12:15

Ahoj
Potýkám se s podobným problémem kolem SmartObject.
Mám PHP 7.2.1. Přejmenoval jsem tedy v souboru CalculatorMana­ger.php "Object" na "SmartObject". Předpokládám že v tomto souboru by to mělo stačit. Nette mám nejnovější verzi 2.4.
Bohužel mi to stále hlásí chybu "Class App\Model\Cal­culatorManager cannot extend from trait Nette\SmartObject" na posledním řádku, u koncové závorky třídy }.
Už jsem zkoušel vše. poradíte co dělám špatně?
Díky Libor

 
Odpovědět 19.2.2018 12:15
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Libor
Jindřich Máca:19.2.2018 12:27

Ahoj, vždyť jsem to psal, SmartObject není na rozdíl od Object třída, nýbrž PHP trait, takže se k tomu nemůžeš chovat stejně! Např. z trait nelze vůbec dědit, což Ti přesně píše ta chyba. :D

Takže doporučuji nejdříve dostudovat samotný jazyk PHP než jen tak náhodně experimentovat. Viz. http://php.net/…5.traits.php

 
Odpovědět 19.2.2018 12:27
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Libor
Jindřich Máca:19.2.2018 12:29

Jo a použití SmartObject je poměrně detailně popsané i v oficiální dokumentaci Nette - https://doc.nette.org/…/smartobject

 
Odpovědět 19.2.2018 12:29
Avatar
Libor
Člen
Avatar
Odpovídá na Jindřich Máca
Libor:19.2.2018 17:23

Ahoj, omlouvám se, nepochopil jsem to hned napoprvé.
Díky za upřesnění. Teď už vše funguje :-)

 
Odpovědět 19.2.2018 17:23
Avatar
Lukáš Vaněk:24.7.2018 12:10

Class or interface 'App\Nette\Ap­plication\IRou­ter' not found. Is return type of App\RouterFac­tory::createRou­ter() used in service 'routing.router' correct? search►

postupoval jsem podle tutoriálu a vyhodilo mi to tuhle chybu několikrát jsem to kontroloval ale nevím proč mi to hlásí nenalezen soubor IRouter. Poradí někdo děkuji.

 
Odpovědět 24.7.2018 12:10
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Lukáš Vaněk
Jindřich Máca:24.7.2018 20:53

Ahoj, to vypadá, že máš někde problém v namespace, nebo v klauzulích use na začátku souboru. Můžeš sem prosím poslat celý formátováný kód Tvého RouterFactory­.php? Pak snad bude jasnější, kde je chyba. :-)

 
Odpovědět 24.7.2018 20:53
Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:6.8.2018 13:22

Aktualizováno pro Nette 2.4

Odpovědět 6.8.2018 13:22
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Odpovídá na Jindřich Máca
Vít Cigánek:23.8.2018 9:46

Mam uplne stejny problem s routerem jak vanek.

 
Odpovědět 23.8.2018 9:46
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Vít Cigánek
Jindřich Máca:23.8.2018 23:00

Taky už jsem na to jednou odpovídal. Chyba je v nesprávném zacházení s namespace a use. Jednoduše třída, kterou chceme a definuje se v rámci use je Nette\Application\IRouter a ta chyba říká, že se pokoušíte místo toho použít třídu App\Nette\Application\IRouter, což je nesmysl. Tohle ale spadá do základní práce v čistém PHP - http://php.net/…s.basics.php, Nette tohoto mechanismu pouze využívá. :-`

 
Odpovědět 23.8.2018 23:00
Avatar
Marty
Člen
Avatar
Marty:29.10.2018 17:30

Jen drobné nepodstatné povšimnutí. Proč složky templates a presenters jsou v množném čísle, a složka model jen v jednotném?

 
Odpovědět 29.10.2018 17:30
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Marty
Jindřich Máca:29.10.2018 17:44

To je úplně jedno. Ale asi by tam mělo být spíš to models. :-`

 
Odpovědět 29.10.2018 17:44
Avatar
Marty
Člen
Avatar
Odpovídá na Jindřich Máca
Marty:29.10.2018 22:20

Ono je to v jednotném čísle normálně při vytvoření jakéhokoli nette projektu. Přemýšlel jsem, jestli to je nějaký záměr, ale nevím. :)

 
Odpovědět 29.10.2018 22:20
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Marty
Jindřich Máca:29.10.2018 22:33

Právě a já jsem to tak prostě nechal.

A pokud tam nějaký hlubší záměr je, tak se přiznám, že o něm nevím... :-D

 
Odpovědět 29.10.2018 22:33
Avatar
Martin Konečný (pavelco1998):29.10.2018 22:53

Marty Jindřich Máca Řekl bych, že jednotné číslo označuje název vrstvy, ne ve smyslu "kolekce tříd pod touto složkou". Tedy když v MVP je vrstva Presenter, bude složka pojmenována Presenter. Kdy je tam vrstva Model, bude složka pojmenovaná Model. A v ní teprve v množném čísle oddělené složkami "kolekce" tříd, např. Managers, Repositories, Listeners, Exceptions atd.

Odpovědět 29.10.2018 22:53
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Martin Konečný (pavelco1998)
Jindřich Máca:29.10.2018 23:28

No to chápu, ale právě tady řešíme, že v šabloně to takhle není a to je na tom to divné. :-S Když se podíváš přímo na čistý sandbox, tak uvidíš, že je tam vrstva pro Presenter a složka se jmenuje presenters/ a potom je tam vrstva Model, přičemž složka se jmenuje model/. :D

 
Odpovědět 29.10.2018 23:28
Avatar
Odpovídá na Jindřich Máca
Martin Konečný (pavelco1998):29.10.2018 23:35

Těžko říct, no :D Je pravda, že také používám název "Presenters", ale pak mám název "Model". Mám to ale právě kvůli tomu, že složku "Model" označuji spíše jako celou vrstvu, ve které mám určité "typy" modelových tříd (Facades, Listeners atd.), ve složce Presenters mám jen seznam jednotlivých presenterů. Tedy abych nepsal Presenter/Pre­senters/UserPre­senter, tak to píšu rovnou pod jeden název. U modelových tříd pak mám dělení např. Model/Facades/Ar­ticleSearcher, Model/Entities/Ar­ticle :)

Editováno 29.10.2018 23:36
Odpovědět  +1 29.10.2018 23:35
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Jaroslav Patrný:22.11.2018 23:18

Postupoval jsem podle textu, nemám tudíž
- App\Forms\Sig­nInFormFactory
- App\Forms\Sig­nUpFormFactory
v configu a hlásilo mi to tedy výjimku:

Nette\DI\Servi­ceCreationExcep­tion
Service 'application.4' (type of App\Presenter­s\SignPresenter): Service of type App\Forms\Sig­nInFormFactory needed by $signInFactory in App\Presenter­s\SignPresenter::__con­struct() not found. Did you register it in configuration file?

Proto jsem ve třídě SignPresenter jsem zakomentoval konstruktor

public function __construct(Forms\SignInFormFactory $signInFactory, Forms\SignUpFormFactory $signUpFactory)

Kalkulačka nyní už chodí.

 
Odpovědět 22.11.2018 23:18
Avatar
Erik Dobeš
Člen
Avatar
Erik Dobeš:30. června 12:12

Zdravím, tutoriál se mně velmi líbí, ale výsledek má 1 chybu, která jej podle mě celý devalvuje. Když člověk po vypočtení výrazu obnoví stránku, tak se objeví známá hláška o tom, jestli chce doopravdu stránku obnovit, jelikož se znovu odešlou formuláře. Chybu lze spravit takhle:

public function renderDefault($flag = true, $result = 0)
{
    if($flag)
        $this->template->result = $this->result;
    else
        $this->template->result = $result;
}

public function calculatorFormSucceeded($form, $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);
    $this->redirect('default', false, $this->result);
}
 
Odpovědět  +1 30. června 12:12
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 56 zpráv z 56.