Lekce 6 - Jednoduchý redakční systém v Nette - Výpis článku
V minulé lekci, Jednoduchý redakční systém v Nette - Struktura projektu, jsme si připravili projektovou strukturu pro jednoduchý redakční systém.
Dnes se rovnou bez zbytečných řečí vrhneme do programování
CoreModule
. Takže jdeme na to!
Databáze
Prvně se podíváme na nastavení a obsah databáze. Vytvoříme si novou
databázi pro náš projekt (např. nette-rs
) a v ní spustíme
následující 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 vytvoří a naplní tabulku s články, kterou budeme dále potřebovat.
SQL skript naleznete i v archivu ve složce sql/
pod názvem create_script.sql
.
app/config/common.neon
Nakonec je ještě potřeba nastavit v Nette připojení do naší databáze. To uděláme v hlavním konfiguračním souboru tak, že upravíme již přednastavené parametry, tj. název naší databáze i uživatelské jméno a heslo. Výsledek by měl vypadat přibližně 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: kokos # heslo options: lazy: yes ...
Pokud máte jinou konfiguraci databáze v různých
prostředích (localhost vs. hosting), používá se ještě lokální
konfigurační soubor app/config/local.neon
, jehož nastavení
přepíše to globální specificky pro dané prostředí.
Model
Stejně jako v minulé aplikaci začneme hezky od modelu.
app/Model/DatabaseManager.php
Protože jsme všichni znalí OOP a nechceme mít zbytečné duplicity v kódu, připravíme si základní abstraktní modelovou třídu pro práci s databází, která bude pomocí Dependency Injection (zkráceně DI) získávat přístup k Nette rozhraní pro práci s databází a všechny další modelové třídy z ní budou následně dědit, aby automaticky tento přístup získaly. Třída je poměrně jednoduchá a vypadá takto:
<?php declare(strict_types=1); namespace App\Model; use Nette\Database\Explorer; 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 */ class DatabaseManager { use SmartObject; /** @var Explorer Služba pro práci s databází. */ protected Explorer $database; /** * Konstruktor s injektovanou službou pro práci s databází. * @param Explorer $database Automaticky injektovaná Nette služba pro práci s databází */ public function __construct(Explorer $database) { $this->database = $database; } }
app/CoreModule/Model/ArticleManager.php
Další na řadě je model pro správu článků, který tedy podědí ze
třídy DatabaseManager
. Bude již součástí našeho CoreModulu a
jeho cílem je definovat metody, které pomocí Nette rozhraní pro práci s
databází umožní manipulaci s tabulkou článků, kterou jsme si vytvořili
výše.
<?php declare(strict_types=1); 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(ArrayHash $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(string $url) { $this->database->table(self::TABLE_NAME)->where(self::COLUMN_URL, $url)->delete(); } }
Všimněte si, že Nette rozhraní pro práci s databází kopíruje
pokládání klasických SQL dotazů, ovšem v rámci PHP kódu v trochu
zjednodušeném formátu. A také obsahuje např. ochranu proti SQL
injection.
Presentery
Dále budeme pokračovat s presentery. Začneme tím, že smažeme základní
presenter app/presenters/HomepagePresenter.php
, protože už ho
nebudeme potřebovat. Na konci této lekce už totiž budeme mít základ zcela
nové aplikace.
app/Presenters/BasePresenter.php
Stejně jako ve většině projektů v Nette a podobně jako v našem modelu,
začneme od základní abstraktní třídy, ze které dědí všechny ostatní
presentery a tou je BasePresenter
. V sandboxu, ze kterého
vycházíme, už je vytvořen, takže ho jen trošku upravíme:
<?php declare(strict_types=1); 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/ArticlePresenter.php
Nyní se dostáváme k presenteru, který nám pomocí
ArticleManager
bude předávat data článků do šablony. Tento
presenter tvoří hlavní část našeho CoreModule a vypadá následovně:
<?php declare(strict_types=1); 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(string $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(string $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/common.neon
Aby fungovalo automatické předávání závislostí pomocí DI, musíme
ještě zaregistrovat ArticleManager
jako službu v naší
aplikaci. Konkrétně to provedeme v konfiguračním souboru našeho modulu, kde
i předáme konfiguraci URL výchozího článku do našeho nového
presenteru:
# # 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.
Routování
app/router/RouterFactory.php
Abychom se opravdu dostali k daným článkům podle jejich URL, je nutné
ještě upravit routování naší aplikace. To zařídíme úpravou již
existující třídy ze sandboxu RouterFactory
, kde nastavíme
směrování na náš nový presenter:
<?php declare(strict_types=1); namespace App\Router; use Nette; use Nette\Application\Routers\RouteList; final class RouterFactory { use Nette\StaticClass; public static function createRouter(): RouteList { $router = new RouteList; $router->addRoute('[<url>]', 'Core:Article:default'); return $router; } }
To je zatím vše.
V příští lekci, Jednoduchý redakční systém v Nette - Administrace, se budeme věnovat šablonám a projekt zprovozníme.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 665x (3.65 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP