IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 654x (3.65 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Jednoduchý redakční systém v Nette - Struktura projektu
Všechny články v sekci
Základy Nette frameworku
Přeskočit článek
(nedoporučujeme)
Jednoduchý redakční systém v Nette - Administrace
Článek pro vás napsal Jindřich Máca
Avatar
Uživatelské hodnocení:
60 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