8. díl - Jednoduchý redakční systém v Nette - Dokončení administrace

PHP Nette Framework Základy Jednoduchý redakční systém v Nette - Dokončení 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ém tutoriálu o tvorbě jednoduchého redakčního systému v Nette frameworku jsme rozpracovali administraci článků. Dnes ji spolu s kontaktním formulářem dokončíme.

app/CoreModule/presenters/ContactPresenter.php

Nejprve přidáme ještě presenter pro kontaktní formulář:

<?php

namespace App\CoreModule\Presenters;

use App\Presenters\BasePresenter;
use Nette\Application\UI\Form;
use Nette\InvalidStateException;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;
use Nette\Utils\ArrayHash;

/**
 * Zpracovává kontaktní formulář.
 * @package App\CoreModule\Presenters
 */
class ContactPresenter extends BasePresenter
{
        /** Email administrátora, na který se budou posílat emaily z kontaktního formuláře. */
        const EMAIL = 'admin@localhost.cz';

        /**
         * Vytváří a vrací komponentu kontaktního formuláře.
         * @return Form kontaktní formulář
         */
        protected function createComponentContactForm()
        {
                $form = new Form;
                $form->addText('email', 'Vaše emailová adresa')->setType('email')->setRequired();
                $form->addText('y', 'Zadejte aktuální rok')->setRequired()
                        ->addRule(Form::EQUAL, 'Chybně vyplněný antispam.', date("Y"));
                $form->addTextArea('message', 'Zpráva')->setRequired()
                        ->addRule(Form::MIN_LENGTH, 'Zpráva musí být minimálně %d znaků dlouhá.', 10);
                $form->addSubmit('submit', 'Odeslat');
                $form->onSuccess[] = [$this, 'contactFormSucceeded'];
                return $form;
        }

        /**
         * Funkce se vykonaná při úspěsném odeslání kontaktního formuláře a odešle email.
         * @param Form $form kontaktní formulář
         * @param ArrayHash $values odeslané hodnoty formuláře
         */
        public function contactFormSucceeded($form, $values)
        {
                try {
                        $mail = new Message;
                        $mail->setFrom($values->email)
                                ->addTo(self::EMAIL)
                                ->setSubject('Email z webu')
                                ->setBody($values->message);
                        $mailer = new SendmailMailer;
                        $mailer->send($mail);
                        $this->flashMessage('Email byl úspěšně odeslán.');
                        $this->redirect('this');
                } catch (InvalidStateException $ex) {
                        $this->flashMessage('Email se nepodařilo odeslat.');
                }
        }
}

Zde si povšimněte především použití Nette rozhraní pro odesílání e-mailů z formuláře.

app/config/config.neon

Pokud jste pozorně četli kód, všimli jste si, že u tvorby formulářů v metodě ->setRequired() není vyplněna chybová hláška. To je z toho důvodu, že nám bude stačit všude stejná a tu si globálně nastavíme v konfiguračním souboru připsáním následujících řádků:

# Nastavení výchozích chybových hlášek pro formuláře.
forms:
        messages:
                REQUIRED: 'Povinné pole.'

app/router/RouterFactory.php

Nakonec musíme ještě opět upravit routování, aby naše aplikace brala v potaz nové presentery a jejich akce. Také si zde ukáže příklad překladu českých URL adres na anglické názvy akcí.

<?php

namespace App;

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

/**
 * Routovací továrnička.
 * Řídí routování v celé aplikaci.
 * @package App
 */
class RouterFactory
{
        /**
         * Vytváří router pro aplikaci.
         * @return RouteList výsledný router pro aplikaci
         */
        public static function createRouter()
        {
                $router = new RouteList();
                $router[] = new Route('kontakt/', 'Core:Contact:default');
                $router[] = new Route('administrace/', 'Core:Administration:default');
                $router[] = new Route('[<action>/][<url>]', array(
                        'presenter' => 'Core:Article',
                        'action' => array(
                                Route::VALUE => 'default',
                                Route::FILTER_TABLE => array(
                                        // řetězec v URL => akce presenteru
                                        'seznam-clanku' => 'list',
                                        'editor' => 'editor',
                                        'odstranit' => 'remove'
                                ),
                                Route::FILTER_STRICT => true
                        ),
                        'url' => null,
                ));
                $router[] = new Route('[<url>]', 'Core:Article:default');
                return $router;
        }
}

Pokud se divíte, proč musí mít každý presenter svojí routu, tak je to z toho důvodu, že potřebujeme rozlišit mezi URL článku a názvem presenteru, protože obě mají stejný tvar - /presenter vs. /url. V tomto případě pak vždy dostane přednost presenter před vykreslením článku.

Šablony

Nyní se můžeme podívat zase na šablony (templates).

app/templates/@layout.latte

Zde pouze upravíme odkazy na jednotlivé presentery a jejich akce pomocí Latte maker a přidáme odkaz na administrační rozhraní. Nebudu sem vypisovat znovu celou šablonu, pouze provedené změny:

...
<ul>
        <li><a n:href=":Core:Article:">Úvod</a></li>
        <li><a n:href=":Core:Article:list">Seznam článků</a></li>
        <li><a n:href=":Core:Contact:">Kontakt</a></li>
</ul>
...
<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>. <a n:href=":Core:Administration:">Administrace</a></p>
</footer>
...

app/CoreModule/templates/Article/list.latte

Přidáme šablonu pro výpis článků:

{define title}Výpis článků{/define}
{define description}Výpis všech článků.{/define}
{block content}
<table>
        <tr n:foreach="$articles as $article">
                <td>
                        <h2><a n:href=":Core:Article: $article->url">{$article->title}</a></h2>
                        {$article->description}
                        <br />
                        <a n:href=":Core:Article:editor $article->url">Editovat</a>
                        <a n:href=":Core:Article:remove $article->url">Odstranit</a>
                </td>
        </tr>
</table>

app/CoreModule/templates/Article/editor.latte

Nyní šablonu pro editor článků. Zde si povšimněte jak jednoduše jsme zařídili renderování formuláře a dále způsobu přidávání dalších JavaScript knihoven:

{define title}Editor{/define}
{define description}Editor článků.{/define}
{block content}
{* Formulář pro editaci. *}
{control editorForm}
{/block}

{block scripts}
{include parent}
<script type="text/javascript" src="//tinymce.cachefly.net/4.0/tinymce.min.js"></script>
<script type="text/javascript">
    tinymce.init({
        selector: "textarea[name=content]",
        plugins: [
            "advlist autolink lists link image charmap print preview anchor",
            "searchreplace visualblocks code fullscreen",
            "insertdatetime media table contextmenu paste"
        ],
        toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
        entities: "160,nbsp",
        entity_encoding: "named",
        entity_encoding: "raw"
});
</script>
{/block}

app/CoreModule/templates/Administration/default.latte

Dále následuje šablona pro administrační rozhraní:

{define title}Administrace webu{/define}
{define description}Administrace webu.{/define}
{block content}
<p>Vítejte v administraci!</p>
<h2><a n:href=":Core:Article:editor">Editor článků</a></h2>
<h2><a n:href=":Core:Article:list">Seznam článků</a></h2>

app/CoreModule/templates/Contact/default.latte

A na závěr šablona pro stránku s kontaktním formulářem:

{define title}Kontaktní formulář{/define}
{define description}Kontaktní formulář.{/define}
{block content}
<p>Kontaktujte nás odesláním formuláře níže.</p>
{* Formulář pro kontakt. *}
{control contactForm}
Stránka s kontaktním formulářem

Gratuluji, právě vám běží jednoduché administrační rozhraní pro články v Nette s kontaktním formulářem jako bonus. ;)

Nyní, v rámci seriálu, budeme dále pokračovat v rozšiřování administrace a začneme se věnovat zabezpečení našeho webu, což by mělo vyústit v plně funkční přihlašování a registraci uživatelů s definicí jejich práv, takže se určitě máte na co těšit. :) Konkrétně v příštím díle začneme rozšířením databáze a přidáním modelu uživatelů. ;)


 

Stáhnout

Staženo 291x (657.03 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

  Aktivity (1)

Článek pro vás napsal Jindřich Máca
Avatar
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. :-)

Jak se ti líbí článek?
Celkem (6 hlasů) :
55555


 



 

 

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

Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Jindřich Máca:

Ahoj,

tahle chyba spíš souvisí s SQL než s Nette jako takovým. Tvůj SQL kód:

INSERT INTO article (article_id, title, url, description, content)
VALUES ('', 'aaa', 'aaa', 'aaa', '<p>aaaaaa</p>');

Proč posíláš do hodnoty article_id, která je očividně celé číslo, prázdný textový řetězec? :D A pak ještě hned pod tím napíšeš toto:

Chápu, kde je chyba. Nevím, jak ji odstranit.

Tak to prostě nedělám a to SQL bude vypadat takto:

INSERT INTO article (title, url, description, content)
VALUES ('aaa', 'aaa', 'aaa', '<p>aaaaaa</p>');

Chyba odstraněna. ;)

Každopádně, pokud Ti tenhle kód nebude fungovat, tak máš špatně navrženou tu samotnou databázovou tabulku. Standardní postup při tvorbě SQL tabulek s indexem je, že se tento index nechává přiřazovat automaticky, tudíž se mu nastaví AUTO_INCREMENT a v INSERT dotazech se pak může právě úplně vynechat. :)

 
Odpovědět 2. února 20:50
Avatar
Jan Říha
Člen
Avatar
Jan Říha:

Ahoj,
postupuji podle návodu Jednoduchý redakční systém v Nette. Dodělal jsem 8. díl - Dokončení administrace.
Na úvodní stránce vyberu Administrace, Editor článků, vyplním pole Titulek, URL, Popisek, Obsah a dám Uložit článek. Vyskočí mnou víše popisovaná chyba.

Předpokládám, že problém souvisí s metodou createComponen­tEditorForm(), skrytým polem article_id, které se metoda editorFormSucceeded snaží uložit do tabulky article jako prázdný text.
Doplnil jsem:
$form->addHidden('ar­ticle_id')->addRule(Form::IN­TEGER);
Opět nejde uložit. Nad formulářem vyskočí: Please enter a valid integer.
Tabulku article jsem zakládal podle create_article.sql.
Hostuji na wedos.
Honza

 
Odpovědět 3. února 18:12
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Jan Říha
Jindřich Máca:

Chápu, takže to je chyba v kontextu toho formuláře. Každopádně to přidání pravidla k tomu skrytému poli není ve směs špatná myšlenka, ale v tomhle kontextu je to dost nepoužitelné, protože ten prázdný formulář tam to article_id nikdy nemá nastavené, tudíž to nemůže být číslo. :)

Takže, pokud se vrátíme k té původní chybě, tak já jsem teď stáhl místní archiv, rozchodil znovu lokálně, zopakoval Tebou uvedený postup a vše prošlo bez chyby. :D Takže jakou máš verzi PHP, popřípadě verzi databáze? A opravdu jsi v tom projektu vůbec nic neměnil?

 
Odpovědět 3. února 21:15
Avatar
Jan Říha
Člen
Avatar
Jan Říha:

PHP Version 5.6.29-0+deb8u1,
innodb_version 5.7.17,
Snažil jsem se nic neměnit.
Google pro výše uvedenou chybu doporučuje: sql_mode nemá být strict.
Nevím, zda-li to s tím souvisí, ale phpinfo() říká: session.use_stric­t_mode Off Off.
Když v ArticlePresen­ter.php odstraním řádek
$form->addHidden('ar­ticle_id')->addRule(Form::IN­TEGER);
chyba: Undefined property: Nette\Utils\A­rrayHash::$ar­ticle_id, která ukazuje také do ArticleManager.php metody public function saveArticle($ar­ticle) na řádek if (!$article[sel­f::COLUMN_ID])
Když do metody saveArticle($ar­ticle) vložím pouze:
$this->database->table(self::TAB­LE_NAME)->insert($article);
pak se článek uloží.

 
Odpovědět 3. února 22:33
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Jan Říha
Jindřich Máca:

To, co popisuješ, se všechno chová tak, jak by mělo. Pro tu chybu je asi tady opravdu nutné vypnout striktní mód databáze tak, jak napovídá Google. Ovšem session.use_stric­t_mode s tím nemá nic společného, to je nastavení módu pro PHP Sessions. ;)

 
Odpovědět 4. února 18:16
Avatar
Jan Říha
Člen
Avatar
Jan Říha:
  1. odstranil jsem: ->addRule(Form::IN­TEGER),
  2. na serveru jsem vytvořil soubor: /etc/mysql/con­f.d/disable_stric­t_mode.cnf
  3. do souboru jsem zapsal:

[mysqld]
sql_mode=IGNO­RE_SPACE,NO_ZE­RO_IN_DATE,NO_ZE­RO_DATE,ERROR_FOR_DI­VISION_BY_ZERO,NO_A­UTO_CREATE_USER,NO_EN­GINE_SUBSTITU­TION

  1. restartoval jsem mysql: service mysql restart

a funguje to.
Dík za odpovědi.

 
Odpovědět  +1 4. února 21:02
Avatar
Karl
Člen
Avatar
Karl:

Jindro a jak Ty máš nastavené my.ini ?

mám WAMP s PHP 7.0.10 a MySQL 5.7.14
když nastavím v my.ini sql-mode="" tak to funguje
když to nechám default, tedy zakomentuji v my.ini ;sql-mode="" (zakomentován) tak to nefunguje

jinak toto mi nepomohlo sql-mode=„NO_AUTO_VA­LUE_ON_ZERO“ – chybu to házelo dále

Kárl

 
Odpovědět 15. února 10:55
Avatar
Karl
Člen
Avatar
Odpovídá na Karl
Karl:

a pokud to udělám jako p.Říha, (tedy sql_mode=IGNO­RE_SPACE,NO_ZE­RO_IN_DATE,NO_ZE­RO_DATE,ERROR_FOR_DI­VISION_BY_ZERO,NO_A­UTO_CREATE_USER,NO_EN­GINE_SUBSTITU­TION)

tak mi to vyhodí chybu:

Integrity constraint violation: 1048 Column 'product_id' cannot be null

 
Odpovědět 15. února 11:13
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovídá na Karl
Jindřich Máca:

Já to mám obecně trochu jinak, protože používám Linux. Každopádně mám klasické php.ini a zde jednoduše nastavené:

sql.safe_mode = Off

Více informací - http://php.net/…ini.core.php#… ;)

Nicméně, když jsi použil řešení od Jan Říha, tak to pravděpodobně fungovalo, ale máš tam potom ještě jinou chybu a to nejspíše v chybějícím ID. :)

 
Odpovědět 15. února 19:39
Avatar
Karl
Člen
Avatar
Karl:

Nicméně mě tuto chybu háže každý insert v celém projektu Nette Eshop - stažen zip z posledního dílu s nahrazením Nette za 2.4

háže mi to jak localhost tak i na hostingu (Savana)
a jelikož to druhým funguje, musí to být někde v nastavení, aby MySQL nevadilo, že zadávám ´´ a místo toho zafungoval autoincrement

snad kvůli tomu nebudu přepisovat všechny inserty, navíc nevím jestli to pak neudělá paseku při editaci záznamu, tam se přeci id zase musí předávat

respektive nastavení v my.ini (překvapuje mě že ho nemáš) mě to s tímto nastavením
sql-mode="" funguje, jenom nevím, co jsem tím ještě ovlivnil

ještě podotknu, že to nastavení, kdy mi to vyhazuje chybu je default nastavení nejnovějšího WAMPu

 
Odpovědět 15. února 20:49
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 37. Zobrazit vše