Letní akce! Lákají tě IT školení C#, Javy a PHP v Brně? Přihlas se a napiš nám do zpráv kód "BRNO 500" pro slevu 500 Kč na libovolný brněnský kurz. Lze kombinovat se slevami uvedenými u školení i použít pro více kurzů. Akce končí 28.7.

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

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

Unicorn College ONEbit hosting 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 již můžeme smazat složku app/templates/Homepage/, protože stejně jako HomepagePresenter ji už nebudeme dále potřebovat.

app/templates/@layout.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 cesta k webovému obsahu např. CSS souborům
 * @param array    $flashes  pole zpráv
 *}

<!DOCTYPE html>
<html lang="cs-cz">
        <head>
                <meta charset="UTF-8" />
                <title>{include title|striptags}</title>
                <meta name="description" content="{include description|striptags}" />

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

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

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

                {* Výpis 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}{/block}
        </body>
</html>

app/CoreModule/templates/Article/default.latte

Nakonec přidáme šablonou pro výchozí akci (renderDefault) našeho ArticlePresenter, která je relativně jednoduchá:

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

Nyní si již můžete zkusit web spustit a 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

Jelikož model máme již nachystaný z minula, začneme rovnou od presenterů.

app/CoreModule/presenters/ArticlePresenter.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\BadRequestException;
use Nette\Application\UI\Form;
use Nette\Database\UniqueConstraintViolationException;
use Nette\Utils\ArrayHash;

/**
 * Zpracovává práci s články.
 * @package App\CoreModule\Presenters
 */
class ArticlePresenter extends BasePresenter
{
        /** Konstanta s hodnotou URL výchozího článku. */
        const DEFAULT_ARTICLE_URL = 'uvod';

        /** @var ArticleManager Instance třídy modelu pro práci s články. */
        protected $articleManager;

        /**
         * Konstruktor s injektovaným modelem pro práci s články.
         * @param ArticleManager $articleManager automaticky injektovaná třída modelu pro práci s články
         */
        public function __construct(ArticleManager $articleManager)
        {
                parent::__construct();
                $this->articleManager = $articleManager;
        }

        /** Načte a vykreslí článek článek do šablony podle jeho URL.
         * @param string $url URL článku
         * @throws BadRequestException Jestliže článek s danou URL nebyl nalezen.
         */
        public function renderDefault($url)
        {
                if (!$url) $url = self::DEFAULT_ARTICLE_URL; // Pokud není zadaná URL, vykreslí se výchozí článek.
                // Pokusí se načíst článek s danou URL a pokud nebude nalezen, vyhodí chybu 404.
                if (!($article = $this->articleManager->getArticle($url))) throw new BadRequestException();
                $this->template->article = $article; // Předá článek do šablony.
        }

        /** Vykreslí seznam článků do šablony. */
        public function renderList()
        {
                $this->template->articles = $this->articleManager->getArticles();
        }

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

        /**
         * Vykresluje editaci článku podle jeho URL.
         * @param string $url URL adresa článku, který editujeme, pokud není zadána, vytvoří se nový
         */
        public function actionEditor($url)
        {
                // Pokud byla zadána URL, pokusí se článek načíst a předat jeho hodnoty do editačního formuláře, jinak vypíše chybovou hlášku.
                if ($url) ($article = $this->articleManager->getArticle($url)) ? $this['editorForm']->setDefaults($article) : $this->flashMessage('Článek nebyl nalezen.');
        }

        /**
         * Vrátí formulář pro editor článků.
         * @return Form formulář pro editor článků
         */
        protected function createComponentEditorForm()
        {
                $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('submit', 'Uložit článek');
                $form->onSuccess[] = [$this, 'editorFormSucceeded'];
                return $form;
        }

        /**
         * Funkce se vykonaná při úspěsném odeslání formuláře; zpracuje hodnoty formuláře.
         * @param Form $form formulář editoru
         * @param ArrayHash $values odeslané hodnoty formuláře
         */
        public function editorFormSucceeded($form, $values)
        {
                try {
                        $this->articleManager->saveArticle($values);
                        $this->flashMessage('Článek byl úspěšně uložen.');
                        $this->redirect(':Core:Article:', $values->url);
                } catch (UniqueConstraintViolationException $ex) {
                        $this->flashMessage('Článek s touto URL adresou již existuje.');
                }
        }
}

Zde bych upozornil především na způsob předávání výchozích hodnost do editačního formuláře a to, že se to provádí v actionEditor, nikoliv v renderEditor metodě a také si všimněte použití Nette forms pro vytvoření editačního formuláře, což nám v šabloně ušetří spoustu kódu.

app/CoreModule/presenters/AdministrationPresenter.php

Vytvoříme si prázdný presenter pro administrační rozhraní, protože v tuto chvíli mu v podstatě nepotřebujeme do šablony nic předávat:

<?php

namespace App\CoreModule\Presenters;

use App\Presenters\BasePresenter;

/**
 * Zpracovává 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ě ContactPresenter a poté šablony. Těmi administraci článků v našem redakčním systému dokončíme :)


 

Stáhnout

Staženo 344x (651.49 kB)
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?
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 (2)

 

 

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