Slevový týden - Březen C# týden
Využij náš slevový týden a získej až 30 % bodů navíc zdarma! Zároveň také probíhá C# týden se slevou na e-learning až 80 %
Hledáme fulltime programátora do ITnetwork týmu -100% homeoffice, 100% časově flexibilní #bezdeadlinu Mám zájem!

6. diel - Jednoduchý redakčný systém v Nette - Výpis článku

V minulej lekcii, Jednoduchý redakčný systém v Nette - Štruktúra projektu , sme si pripravili projektovú štruktúru pre jednoduchý redakčný systém. A ako som sľúbil, dnes sa rovno bez zbytočných rečí vrhneme na implementáciu. Takže ideme na to! :)

Databázy

Najprv sa pozrieme na nastavenie a obsah databázy. Vytvoríme si novú databázu pre náš projekt (napr. nette-rs) a v nej spustíme nasledujúci SQL:

-- ----------------------------
-- Table structure for `article`
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
    `article_id`  int(11) NOT NULL                   AUTO_INCREMENT,
    `title`       varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
    `content`     text COLLATE utf8_czech_ci,
    `url`         varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
    `description` varchar(255) COLLATE utf8_czech_ci DEFAULT NULL,
    PRIMARY KEY (`article_id`),
    UNIQUE KEY `url` (`url`)
) ENGINE = InnoDB AUTO_INCREMENT = 3 DEFAULT CHARSET = utf8 COLLATE = utf8_czech_ci;

-- ----------------------------
-- Records of article
-- ----------------------------
INSERT INTO `article` VALUES ('1', 'Úvod', '<p>Vítejte na našem webu!</p><p>Tento web je postaven na <strong>jednoduchém redakčním systému v Nette frameworku</strong>. Toto je úvodní článek, načtený z databáze.</p>', 'uvod', 'Úvodní článek na webu v Nette v PHP');
INSERT INTO `article` VALUES ('2', 'Stránka nebyla nalezena', '<p>Litujeme, ale požadovaná stránka nebyla nalezena. Zkontrolujte prosím URL adresu.</p>', 'chyba', 'Stránka nebyla nalezena.');

To nám vytvorí a naplní tabuľku s článkami, ktorú budeme ďalej potrebovať.

SQL skript nájdete aj v archíve v priečinku sql/ pod názvom create_script.sql.

App / config / config.neon

Nakoniec je ešte potrebné nastaviť v Nette pripojenie do našej databázy. To urobíme v hlavnom konfiguračnom súbore tak, že upravíme už prednastavené parametre, tj. Názov našej databázy aj užívateľské meno a heslo. Výsledok by mal vyzerať približne takto:

...
# Konfigurace databázového připojení v rámci celé aplikace.
database:
    dsn: 'mysql:host=127.0.0.1;dbname=nette-rs' # Typ, adresa a název databáze
    user: root     # Uživatelské jméno
    password: **** # Heslo
    options:
        lazy: yes
...

Pokiaľ máte inú konfiguráciu databázy v rôznych prostrediach (localhost verzus hosting), používa sa ešte lokálne konfiguračný súbor config.local.neon, ktorého nastavenie prepíše to globálne špecificky pre dané prostredie.

Model

Tento výukový obsah pomáhajú rozvíjať nasledujúce firmy, ktoré možno hľadajú práve teba!

Rovnako ako v minulej aplikácii začneme pekne od modelu.

App / model / DatabaseManager.php

Pretože sme všetci znalí OOP a nechceme mať zbytočné duplicity v kóde, pripravíme si základné abstraktné modelovú triedu pre prácu s databázou, ktorá bude pomocou Dependency Injection (skrátene DI) získavať prístup k Nette rozhranie pre prácu s databázou a všetky ďalšie modelové triedy z nej budú následne dediť, aby automaticky tento prístup získali. Trieda je pomerne jednoduchá a vyzerá takto:

<?php

namespace App\Model;

use Nette\Database\Context;
use Nette\SmartObject;

/**
 * Základní model pro všechny ostatní databázové modely aplikace.
 * Poskytuje přístup k práci s databází.
 * @package App\Model
 */
abstract class DatabaseManager
{
    use SmartObject;

    /** @var Context Služba pro práci s databází. */
    protected $database;

    /**
     * Konstruktor s injektovanou službou pro práci s databází.
     * @param Context $database automaticky injektovaná Nette služba pro práci s databází
     */
    public function __construct(Context $database)
    {
        $this->database = $database;
    }
}

App / CoreModule / model / ArticleManager.php

Ďalšia na rade je model pre správu článkov, ktorý teda zdedí z triedy DatabaseManager. Bude už súčasťou nášho CoreModulu a jeho cieľom je definovať metódy, ktoré pomocou Nette rozhranie pre prácu s databázou umožní manipuláciu s tabuľkou článkov, ktorú sme si vytvorili vyššie.

<?php

namespace App\CoreModule\Model;

use App\Model\DatabaseManager;
use Nette\Database\Table\ActiveRow;
use Nette\Database\Table\Selection;
use Nette\Utils\ArrayHash;

/**
 * Model pro správu článků v redakčním systému.
 * @package App\CoreModule\Model
 */
class ArticleManager extends DatabaseManager
{
    /** Konstanty pro práci s databází. */
    const
        TABLE_NAME = 'article',
        COLUMN_ID = 'article_id',
        COLUMN_URL = 'url';

    /**
     * Vrátí seznam všech článků v databázi seřazený sestupně od naposledy přidaného.
     * @return Selection seznam všech článků
     */
    public function getArticles()
    {
        return $this->database->table(self::TABLE_NAME)->order(self::COLUMN_ID . ' DESC');
    }

    /**
     * Vrátí článek z databáze podle jeho URL.
     * @param string $url URl článku
     * @return false|ActiveRow první článek, který odpovídá URL nebo false pokud článek s danou URL neexistuje
     */
    public function getArticle($url)
    {
        return $this->database->table(self::TABLE_NAME)->where(self::COLUMN_URL, $url)->fetch();
    }

    /**
     * Uloží článek do systému.
     * Pokud není nastaveno ID vloží nový článek, jinak provede editaci článku s daným ID.
     * @param array|ArrayHash $article článek
     */
    public function saveArticle($article)
    {
        if (empty($article[self::COLUMN_ID])) {
            unset($article[self::COLUMN_ID]);
            $this->database->table(self::TABLE_NAME)->insert($article);
        } else
            $this->database->table(self::TABLE_NAME)->where(self::COLUMN_ID, $article[self::COLUMN_ID])->update($article);
    }

    /**
     * Odstraní článek s danou URL.
     * @param string $url URL článku
     */
    public function removeArticle($url)
    {
        $this->database->table(self::TABLE_NAME)->where(self::COLUMN_URL, $url)->delete();
    }
}

Všimnite si, že Nette rozhranie pre prácu s databázou kopíruje pokladanie klasických SQL dotazov, avšak v rámci PHP kódu v trochu zjednodušenom formáte. A tiež obsahuje napr. Ochranu proti SQL injection. ;)

Presenter

Ďalej budeme pokračovať s Presenter. Začneme tým, že zmažeme základné presenter app/presenters/HomepagePresenter.php, pretože už ho nebudeme potrebovať. Na konci tejto lekcie už totiž budeme mať základ úplne nové aplikácie.

App / presenters / BasePresenter.php

Rovnako ako vo väčšine projektov v Nette a podobne ako v našom modeli, začneme od základnej abstraktné triedy, z ktorej dedia všetky ostatné Presenter a tou je BasePresenter. V sandboxe, z ktorého vychádzame, už je vytvorený, takže ho len trošku upravíme:

<?php

namespace App\Presenters;

use Nette\Application\UI\Presenter;

/**
 * Základní presenter pro všechny ostatní presentery aplikace.
 * @package App\Presenters
 */
abstract class BasePresenter extends Presenter
{
}

App / CoreModule / presenters / ArticlePresen­ter.php

Teraz sa dostávame k Presenter, ktorá nám pomocou ArticleManager bude odovzdávať dáta článkov do šablóny. Tento presenter tvorí hlavnú časť nášho CoreModule a vyzerá nasledovne:

<?php

namespace App\CoreModule\Presenters;

use App\CoreModule\Model\ArticleManager;
use App\Presenters\BasePresenter;
use Nette\Application\BadRequestException;

/**
 * Presenter pro vykreslování článků.
 * @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.
    }
}

App / CoreModule / config / config.neon

Aby fungovalo automatické odovzdávanie závislostí pomocou DI, musíme ešte zaregistrovať ArticleManager ako službu v našej aplikácii. Konkrétne to vykonáme v konfiguračnom súbore nášho modulu, kde aj odovzdáme konfiguráciu URL predvoleného článku do nášho nového prezentačného:

#
# Konfigurační soubor pro CoreModule.
#
parameters:
    defaultArticleUrl: 'uvod' # URL výhozího článku


# Nastavení služeb pro CoreModule.
services:
    - App\CoreModule\Model\ArticleManager # Vlastní služba dále přístupná pomocí DI.
    - App\CoreModule\Presenters\ArticlePresenter(%defaultArticleUrl%) # Předání nastavení při vytváření služby presenteru.

Routovanie

App / router / RouterFactory.php

Aby sme sa naozaj dostali uvedených článkov podľa ich URL, je nutné ešte upraviť routovanie našej aplikácie. To zariadime úpravou už existujúce triedy zo sandboxe RouterFactory, kde nastavíme smerovanie na náš nový presenter:

<?php

namespace App;

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

/**
 * Továrna na routovací pravidla.
 * Řídí směrování a generovaní URL adres v celé aplikaci.
 * @package App
 */
class RouterFactory
{
    use StaticClass;

    /**
     * Vytváří a vrací seznam routovacích pravidel pro aplikaci.
     * @return IRouter výsledný router pro aplikaci
     */
    public static function createRouter()
    {
        $router = new RouteList;
        $router[] = new Route('[<url>]', 'Core:Article:default');
        return $router;
    }
}

To je zatiaľ všetko. V budúcej lekcii, Jednoduchý redakčný systém v Nette - Administrácia , sa budeme venovať šablónam a projekt sprevádzkujeme.


 

 

Článok pre vás napísal Jindřich Máca
Avatar
Ako sa ti páči článok?
Ešte nikto nehodnotil, buď prvý!
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. :-)
Predchádzajúci článok
Jednoduchý redakčný systém v Nette - Štruktúra projektu
Všetky články v sekcii
Základy Nette frameworku
Miniatúra
Nasledujúci článok
Jednoduchý redakčný systém v Nette - Administrácia
Aktivity (1)

 

 

Komentáre

Avatar
Michal Dvořáček:15.2.2018 10:16

Zdravím,
chtěl bych se zeptat na BaseManager, konkrétně na Object.
Nějak jsem pochopil, že už je Nette\Object zastaralé a místo ní se udělalo Nette\SmartObject.
Co ale neumím určit, je jak se třeba konkrétně v tomto příkladu využije, respektive o co mám pak BaseManager rozšířit. Zkoušel jsem právě o SmartObject, ale nefunguje.

use Nette\SmartObject;
abstract class BaseManager extends SmartObject
{...}

Děkuji za každou radu jak tuto komplikaci vyřešit.

Editované 15.2.2018 10:18
 
Odpovedať
15.2.2018 10:16
Avatar
dez1nd
Člen
Avatar
Odpovedá na Michal Dvořáček
dez1nd:15.2.2018 10:21

Dědit nic nemusíš, máš to v use

potom stačí jen

$objekt = new SmartObject;

kdyby jsi to neměl v use tak bys musel

$objekt = new Nette\SmartObject;
Editované 15.2.2018 10:23
 
Odpovedať
15.2.2018 10:21
Avatar
Jindřich Máca
Tým ITnetwork
Avatar
Odpovedá na Michal Dvořáček
Jindřich Máca:15.2.2018 14:04

Ahoj, problém je v tom, že SmartObject není třída, ale PHP trait, což už mluví za vše. Příklady jejího použití pak najdeš přímo v oficiální dokumentaci - https://doc.nette.org/…/smartobject.

 
Odpovedať
15.2.2018 14:04
Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:6.8.2018 13:24

Aktualizováno pro Nette 2.4.

Odpovedať
6.8.2018 13:24
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
Fero Mikulic
Člen
Avatar
Fero Mikulic:14. marca 17:20

Ahoj. Udelal sem vsechno podle postupu a haze mi to chybu

"Nette\DI\Ser­viceCreationEx­ception

Service 'routing.router': Method App\Router\Rou­terFactory::cre­ateRouter() is not callable."

 
Odpovedať
14. marca 17:20
Tento výukový obsah pomáhajú rozvíjať nasledujúce firmy, ktoré možno hľadajú práve teba!
Avatar
Milan Turyna
Redaktor
Avatar
Milan Turyna:14. marca 18:25

Nekoukal jsem na serial, ale myslim si že to muze byt zpusobeno ze vyuzivas novou verzi a serial je pro starsi ale nevim. Ja kdyz jsem postupoval pri tvorbe cmska pres nette jen podle dokumentace tak mi to vzdy slo. Takze mozna ta verze.

 
Odpovedať
14. marca 18:25
Avatar
Milan Turyna
Redaktor
Avatar
Milan Turyna:14. marca 18:26

Jeste sem muzes poslat config

 
Odpovedať
14. marca 18:26
Avatar
Fero Mikulic
Člen
Avatar
Fero Mikulic:23. marca 13:53

Skousel sem to i na 2.4 a stejnej error.
Tady je GitHub repo
https://github.com/…ob/nette-cms

 
Odpovedať
23. marca 13:53
Avatar
Daniel Vítek
Tým ITnetwork
Avatar
Odpovedá na Fero Mikulic
Daniel Vítek:23. marca 16:02

https://github.com/…rFactory.php

Máš zde špatný namespace - jen App. Uprav jej na App\Router.

Odpovedať
23. marca 16:02
Na síti působím už pěknou řádku let. Pokud budeš něco potřebovat, písni mi, pokusím se ti poradit :)
Avatar
Fero Mikulic
Člen
Avatar
Odpovedá na Daniel Vítek
Fero Mikulic:24. marca 15:54

Dekuji. Pomohlo to

 
Odpovedať
24. marca 15:54
Robíme čo je v našich silách, aby bola tunajšia diskusia čo najkvalitnejšia. Preto do nej tiež môžu prispievať len registrovaní členovia. Pre zapojenie sa do diskusie sa zaloguj. Ak ešte nemáš účet, zaregistruj sa, je to zadarmo.

Zatiaľ nikto nevložil komentár - buď prvý!