Lekce 7 - Jednoduchý redakční systém v Nette - Administrace

PHP Nette Framework Základy Jednoduchý redakční systém v Nette - Administrace

ONEbit hosting 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, Jednoduchý redakční systém v Nette - Výpis článku, jsme již vytvořili základní strukturu pro výpis článků kromě šablon, které dnes přidáme. Tím výpis zprovozníme a budeme pokračovat s tvorbou jejich administrace.

Šablony

Nyní je tedy čas podívat se na zoubek šablonám (templates). Opět zde můžeme smazat celou složku app/templates/Homepage/, protože stejně jako třídu HomepagePresenter ji už nebudeme dále potřebovat.

app/templates/@la­yout.latte

Samozřejmě nezačneme ničím jiným než úpravou celkového vzhledu naší aplikace, který v tomto případě zajišťuje Latte šablona @layout.latte, opět předpřipravená v sandboxu, ze kterého vycházíme.

{**
 * @param string   $basePath web base path
 * @param array    $flashes  flash messages
 *}
<!DOCTYPE html>
<html lang="cs">
        <head>
                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <meta name="description" content="{include description|striptags}">

                <title>{include title|striptags}</title>

                {block css}
                        <link rel="stylesheet" href="{$basePath}/css/style.css">
                {/block}

                {block head}{/block}
        </head>

        <body>
                <header>
                        <h1>Jednoduchý redakční systém v Nette</h1>
                </header>

                {* Výpis flash zpráv. *}
                <p n:foreach="$flashes as $flash" class="message">{$flash->message}</p>

                <nav>
                        <ul>
                                <li><a n:href=:Core:Article:>Úvod</a></li>
                                <li><a href="#">Seznam článků</a></li>
                                <li><a href="#">Kontakt</a></li>
                        </ul>
                </nav>
                <br clear="both">

                <article>
                        <header>
                                <h1>{include title}</h1>
                        </header>
                        <section>
                                {include content} {* Vložení obsahu do šablony. *}
                        </section>
                </article>

                <footer>
                        <p>
                                Ukázkový tutoriál pro jednoduchý redakční systém v Nette z programátorské sociální sítě
                                <a href="http://www.itnetwork.cz" target="_blank">itnetwork.cz</a>
                        </p>
                </footer>

                {block scripts}
                        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
                        <script src="{$basePath}/js/main.js"></script>
                {/block}
        </body>
</html>

app/CoreModule/tem­plates/Article/de­fault.latte

Nakonec vytvoříme složku Article/ pro šablony našeho ArticlePresenter v CoreModule a do ní přidáme soubor default.latte jako šablonou pro jeho výchozí akci renderDefault(). Samotná šablona je relativně jednoduchá:

{block title}{$article->title}{/block}
{block description}{$article->description}{/block}
{block content}
{$article->content|noescape}

Nyní si již můžete zkusit web spustit a měli byste vidět úvodní stránku. Je to jistě příjemný pocit po tom, co jsme změnili tolik kódu, že? :)

Úvodní stránka redakčního systému v Nette frameworku

Úplný základ máme zprovozněný, nyní se podíváme hlouběji do CoreModule a to konkrétně na administraci článků ;)

Presentery

Model máme již nachystaný z minula, takže začneme rovnou od presenterů.

app/CoreModule/pre­senters/Article­Presenter.php

Jelikož v Nette můžeme mít více akcí v jednom presenteru, budeme pokračovat v rozšiřování naší třídy ArticlePresenter a přidáme do ní následující metody dalších akcí:

<?php

namespace App\CoreModule\Presenters;

use App\CoreModule\Model\ArticleManager;
use App\Presenters\BasePresenter;
use Nette\Application\AbortException;
use Nette\Application\BadRequestException;
use Nette\Application\UI\Form;
use Nette\Database\UniqueConstraintViolationException;
use Nette\Utils\ArrayHash;

/**
 * Presenter pro akce s články.
 * @package App\CoreModule\Presenters
 */
class ArticlePresenter extends BasePresenter
{
        /** @var string URL výchozího článku. */
        private $defaultArticleUrl;

        /** @var ArticleManager Model pro správu s článků. */
        private $articleManager;

        /**
         * Konstruktor s nastavením URL výchozího článku a injektovaným modelem pro správu článků.
         * @param string         $defaultArticleUrl URL výchozího článku
         * @param ArticleManager $articleManager    automaticky injektovaný model pro správu článků
         */
        public function __construct($defaultArticleUrl, ArticleManager $articleManager)
        {
                parent::__construct();
                $this->defaultArticleUrl = $defaultArticleUrl;
                $this->articleManager = $articleManager;
        }

        /**
         * Načte a předá článek do šablony podle jeho URL.
         * @param string|null $url URL článku
         * @throws BadRequestException Jestliže článek s danou URL nebyl nalezen.
         */
        public function renderDefault($url = null)
        {
                if (!$url) $url = $this->defaultArticleUrl; // Pokud není zadaná URL, vezme se URL výchozího článku.

                // Pokusí se načíst článek s danou URL a pokud nebude nalezen vyhodí chybu 404.
                if (!($article = $this->articleManager->getArticle($url)))
                        $this->error(); // Vyhazuje výjimku BadRequestException.

                $this->template->article = $article; // Předá článek do šablony.
        }

        /** Načte a předá seznam článků do šablony. */
        public function renderList()
        {
                $this->template->articles = $this->articleManager->getArticles();
        }

        /**
         * Odstraní článek.
         * @param string|null $url URL článku
         * @throws AbortException
         */
        public function actionRemove($url = null)
        {
                $this->articleManager->removeArticle($url);
                $this->flashMessage('Článek byl úspěšně odstraněn.');
                $this->redirect('Article:list');
        }

        /**
         * Vykresluje formulář pro editaci článku podle zadané URL.
         * Pokud URL není zadána, nebo článek s danou URL neexistuje, vytvoří se nový.
         * @param string|null $url URL adresa článku
         */
        public function actionEditor($url = null)
        {
                if ($url) {
                        if (!($article = $this->articleManager->getArticle($url)))
                                $this->flashMessage('Článek nebyl nalezen.'); // Výpis chybové hlášky.
                        else $this['editorForm']->setDefaults($article); // Předání hodnot článku do editačního formuláře.
                }
        }

        /**
         * Vytváří a vrací formulář pro editaci článků.
         * @return Form formulář pro editaci článků
         */
        protected function createComponentEditorForm()
        {
                // Vytvoření formuláře a definice jeho polí.
                $form = new Form;
                $form->addHidden('article_id');
                $form->addText('title', 'Titulek')->setRequired();
                $form->addText('url', 'URL')->setRequired();
                $form->addText('description', 'Popisek')->setRequired();
                $form->addTextArea('content', 'Obsah');
                $form->addSubmit('save', 'Uložit článek');

                // Funkce se vykonaná při úspěšném odeslání formuláře a zpracuje zadané hodnoty.
                $form->onSuccess[] = function (Form $form, ArrayHash $values) {
                        try {
                                $this->articleManager->saveArticle($values);
                                $this->flashMessage('Článek byl úspěšně uložen.');
                                $this->redirect('Article:', $values->url);
                        } catch (UniqueConstraintViolationException $e) {
                                $this->flashMessage('Článek s touto URL adresou již existuje.');
                        }
                };

                return $form;
        }
}

Zde bych upozornil především na způsob předávání výchozích hodnot do editačního formuláře a hlavně na to, že se úkon provádí v metodě actionEditor(), nikoliv v renderEditor(). Vyplývá to z životního cyklu akce Nette presenteru.

Dále si všimněte použití Nette knihovny pro vytvoření editačního formuláře, což nám, mimo např. bezpečnosti, v šabloně následně ušetří spoustu kódu při jeho vykreslování ;)

app/CoreModule/pre­senters/Adminis­trationPresen­ter.php

Ještě než dnes skončíme s presentery, vytvoříme si jeden prázdný pro administrační sekci. Prázdný bude proto, že v tuto chvíli v podstatě nepotřebujeme do jeho šablony nic předávat:

<?php

namespace App\CoreModule\Presenters;

use App\Presenters\BasePresenter;

/**
 * Presenter pro vykreslování administrační sekce.
 * @package App\CoreModule\Presenters
 */
class AdministrationPresenter extends BasePresenter
{
}

Příště, v lekci Jednoduchý redakční systém v Nette - Dokončení administrace, si přidáme ještě presenter pro kontaktní formulář a poté také všechny chybějící šablony. Těmi administraci článků v našem redakčním systému dokončíme :)


 

 

Článek pro vás napsal Jindřich Máca
Avatar
Jak se ti líbí článek?
9 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. :-)
Aktivity (5)

 

 

Komentáře
Zobrazit starší komentáře (18)

Avatar
Jan Bezdíček
Redaktor
Avatar
Jan Bezdíček:6.11.2016 1:58

Vice zkusenosti bys asi ziskal, kdybys ty teoreticke znalosti uplatnil v praxi na nejakem projektu bez pouziti nette, at to PHP dostanes poradne do krve ... protoze ocividne z predchoziho komentare ani nevis, jak poradne funguje syntaxe jazyka

 
Odpovědět 6.11.2016 1:58
Avatar
Vakos
Redaktor
Avatar
Vakos:8.2.2017 23:09

Chtěl bych se zeptat. Zkoušel jsem využít ArticleManager a ArticlePresenter a vytvořil jsem jejich kopie s názvy DateManager a DatePresenter a celý obsah překopíroval a názvy tříd, proměnné, konstanty atd. jsem upravil a pořád mi to hází Error 500.

V Router by problém být neměl, zkopíroval jsem si zde také tu část z Article a dal jsem nakonec i 'url' => null, takže v tomto by problém být neměl.

Nevíte, v čem by mohla být tedy chyba?

Odpovědět 8.2.2017 23:09
"Jediný způsob, jak dělat skvělou práci, je milovat to, co děláte. Pokud jste to ještě nenašli, hledejte dál. Ne...
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Vakos
Jindřich Máca:8.2.2017 23:29

Jestliže to hází pouze čistou chybu 500, tak konkrétní chybová zpráva bude uvedená v logu (složka log/). ;)

P.S.: Pokud si ani s ní nebudeš vědět rady, tak ji sem pošli. :)

 
Odpovědět 8.2.2017 23:29
Avatar
Vakos
Redaktor
Avatar
Odpovídá na Jindřich Máca
Vakos:8.2.2017 23:38

V log mi to hází toto:

Nette\InvalidStateException: Resource 'Core:Date' does not exist. in C:\xampp\htdocs\vendor\nette\security\src\Security\Permission.php:300  @  http://localhost/dates/  @@  exception-2017-02-08-22-01-55-ab963012bf52ae2b84dd39a63a6b0823.html

Nevím ale kde to upravit. V konfig.neon mám přidáno date: Core:Date

Odpovědět 8.2.2017 23:38
"Jediný způsob, jak dělat skvělou práci, je milovat to, co děláte. Pokud jste to ještě nenašli, hledejte dál. Ne...
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Vakos
Jindřich Máca:8.2.2017 23:49

To vypadá na to, že používáš finální verzi projektu a zapomněl jsi přidat zdroj pro oprávnění. Podívej se do 11. dílu, kde se řeší právě konfigurace oprávnění u statického ACL - http://www.itnetwork.cz/…ka-opravneni

Co potřebuješ přidat je toto:

# Nastavení vlastních služeb dále přístupných pomocí DI v rámci CoreModule.
services:
        security.authorizator: # Nastavení zdrojů a pravidel přístupu k nim v rámci CoreModule pomocí statického ACL.
                setup:
                        ...
                        - addResource(%date%)
                        ...

A dále samozřejmě nastavení konkrétních přístupových práv k danému presenteru. ;)

 
Odpovědět  +1 8.2.2017 23:49
Avatar
Vakos
Redaktor
Avatar
Odpovídá na Jindřich Máca
Vakos:9.2.2017 9:55

Jj, to je ono, děkuji.

Odpovědět 9.2.2017 9:55
"Jediný způsob, jak dělat skvělou práci, je milovat to, co děláte. Pokud jste to ještě nenašli, hledejte dál. Ne...
Avatar
Miroslav Mucha:11. dubna 15:46

Ahoj, už několik dní si lámu hlavu s jedním problémem. Při vkládání nového řádku do DB se data uloží a následně se přesměruje se na šablonu show, kde se nově uložený záznam zobrazí. Vše je v pořádku jak ve vykreslené šabloně, tak v databázi.
Pokud ale edituji již dříve uložený záznam, změny se správně uloží do databáze, ale přesměrování se nepodaří. Laděnka vypisuje chybu: Trying to get property of non-object.
Pro nový záznam i editaci je stejný formulář, načítání a zobrazování záznamů je v pořádku.

Presenter (část):

public function projektFormSucceeded($form, $values)
        {
                $projektId = $this->getParameter('projektId');
                $values['datum_odevzdani'] = DateTime::from($values['datum_odevzdani']);
                $projekt = $this->projektManager->saveProjekt($values, $projektId);
                $this->flashMessage("Projekt byl úspěšně uložen.", 'success');
                $this->redirect('show', $projekt->projekt_id);
        }

        public function actionEdit($projektId)
        {
                $projekt = $this->projektManager->getProjekt($projektId);
                if (!$projekt) {
                        $this->error('Projekt nebyl nalezen');
                }else{
                        $this['projektForm']->setDefaults($projekt);
                }
        }

Model:

public function saveProjekt($values, $projektId)
        {
                if(!$projektId) {
                        $projekt = $this->data->insert($values);
                }else{
                        $projekt = $this->data->where('projekt_id', $projektId)->update($values);
                }
                return $projekt;
        }
 
Odpovědět 11. dubna 15:46
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Miroslav Mucha
Jindřich Máca:11. dubna 18:28

Ahoj, pokud se nepletu, tak problém vzniká tady:

public function saveProjekt($values, $projektId) {
        if (!$projektId) {
                // Metoda insert vrací nový vložený řádek.
                $projekt = $this->data->insert($values);
        } else {
                // Metoda update nevrací upravený řádek, ale číslo, reprezentující počet upravených řádek.
                $projekt = $this->data->where('projekt_id', $projektId)->update($values);
        }
        return $projekt;
}

A následně to vede k chybě, neboť při editaci dostaneš z metody saveProjekt() pouze číslo a ne objekt jako u vkládání. Následně na tomto číslu voláš přístup k atributu $projekt->projekt_id v rámci toho přesměrovaní, což samozřejmě vede k oné chybě, kterou Ti to píše... :)

Pokud bych do toho mohl mluvit, celkově bych předělal tu práci s ID. Už to, že ho při odeslání formuláře bereš z parametru, zavání potenciálním průšvihem. :-`

 
Odpovědět 11. dubna 18:28
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Jindřich Máca:11. dubna 18:31

Jinak informace o tom, jak se chovají ty metody insert(), update() apod. lze najít v oficiální programátorské dokumentaci Nette (API) - https://api.nette.org/…lection.html ;)

 
Odpovědět 11. dubna 18:31
Avatar
Odpovídá na Jindřich Máca
Miroslav Mucha:11. dubna 19:11

Vřelé díky za odpověď, bude to ono, protože když jsem si v Laděnce ukázal na proměnnou $projekt, ukázalo mi to jedničku a nedošlo mi proč. Dokumentaci jsem procházel, ale asi jsem se nechal zmást příklady, kde je rovněž použita metoda update, ale přeci jen trochu jinak.
Ještě jednou díky :-)

 
Odpovědět  +1 11. dubna 19:11
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 10 zpráv z 28. Zobrazit vše