IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

3. diel - Cms v Nette a Doctrine 2 - Modely a Layout

V minulej lekcii, Cms v Nette a Doctrine 2 - Kostra aplikácie , sme si založili projekt so všetkými potrebnými knižnicami a úpravami. V dnešnom Nette tutoriálu začneme tvoriť modelovou vrstvu a pridáme layout s úvodnou stránkou.

Východisková stránka v Nette Doctrine CMS - Cms v Nette a Doctrine 2

Model

Kľúčom k práci s Doctrine sú anotácie. Pomocou nich si uloží do cache informácie o entitách, ako sú názvy stĺpcov, ich dátové typy, väzby medzi entitami atď. Ak teda napríklad pomocou anotácie určíme, že premenná bude typu boolean, pri výbere dát z databázy sa hodnota automaticky přetypuje na TRUE alebo FALSE (v databáze hodnota bude uložená ako 1/0). Informácie je možné zapisovať aj pomocou formátu XML alebo Yamli, ale anotácie sú najpoužívanejšie.

Entity

App / model / entities / User.php

Vytvoríme si našu prvú jednoduchú entitu, ktorá bude reprezentovať užívateľa. Nezabúdajte na to, že Nette obsahuje triedu Nette\Security\User, ktorej objekt je vo všetkých Presenter a šablónach v premennej $user. Nemali by sme túto premennú prepísať, preto sa premenná našej entity bude v šablónach menovať $userEntity.

Pre začiatok bude naša entita veľmi jednoduchá. Obsahuje definíciu atribútov, ktoré zodpovedajú stĺpcom v databázovej tabuľke user. Názov je odvodený od názvu entity, preto ak by sme mali iný názov tabuľky, pridáme anotácii @ORM\Table(name="nazev_tabulky").

Rovnako to platí aj u názvov stĺpcov, tam pridáme do zátvorky parameter name (@ORM\Column(name="nazev_sloupce")).

<?php

namespace App\Model\Entities;

use Doctrine\ORM\Mapping as ORM;
use Kdyby\Doctrine\Entities\BaseEntity;

/**
 * Doctrine entita pro tabulku user.
 * @package App\Model\Entities
 * @ORM\Entity
 */
class User extends BaseEntity
{
    // Pomocné konstanty pro náš model.

    /** Konstanty pro uživatelské role. */
    const ROLE_USER = 1,
        ROLE_ADMIN = 2;

    /** Konstanty pro uživatelské jméno. */
    const MAX_NAME_LENGTH = 15,
        NAME_FORMAT = "^[a-zA-Z0-9]*$";

    // Proměné reprezentující jednotlivé sloupce tabulky.

    /**
     * Sloupec pro ID uživatele.
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    protected $id;

    /**
     * Sloupec pro jméno.
     * @ORM\Column(type="string")
     */
    protected $name;

    /**
     * Sloupec pro heslo.
     * @ORM\Column(type="string")
     */
    protected $password;

    /**
     * Sloupec pro email.
     * @ORM\Column(type="string")
     */
    protected $email;

    /**
     * Sloupec pro IP adresu.
     * @ORM\Column(type="string")
     */
    protected $ip;

    /**
     * Sloupec pro datum registrace.
     * @ORM\Column(name="`registration_date`", type="datetime")
     */
    protected $registrationDate;

    /**
     * Sloupec role uživatele. Význam hodnot viz. konstanty pro uživatelské role.
     * @ORM\Column(type="integer")
     */
    protected $role;

    /**
     * Ověřuje, zda je uživatel v roli administrátora.
     * @return bool vrací true, pokud je uživatel administrátor; jinak vrací false
     */
    public function isAdmin()
    {
        return $this->role === self::ROLE_ADMIN;
    }
}

Pre ID je potreba zapísať anotácií viac, Doctrine ho potom automaticky použije ako primárny kľúč.

Ďalej si všimnite metódy isAdmin() - entita nie je len prepravkou na dáta, ale môže vedieť aj jednoduché operácie.

Všetky naše entity budú dediť od základnej entity, vytvorené rozšírením Keby. Získa vďaka tomu niektoré príjemné funkcie, napr. Nebudeme musieť písať setter a Getter a namiesto toho budeme môcť používať priamo $user->name.

Poznámka: Ak atribútu určíme typ údajov date alebo datetime, Doctrine automaticky pri ťahaní dáta z databázy vytvorí objekt triedy DateTime.

Fasády

Fasády budú služby (tzn. Že v celej aplikácii bude len jedna inštancia danej triedy), ktoré budú sprostredkovávať všetky operácie.

App / model / Facades / UserFacade.php

Pre začiatok bude opäť naša trieda veľmi jednoduchá. Konštruktor automaticky dostane objekt triedy Kdyby\Doctrine\EntityManager (opäť malé vylepšenia EntityManager od Doctrine) a pridáme metódu na vyhľadanie užívateľa podľa ID.

<?php

namespace App\Model\Facades;

use App\Model\Entities\User;
use Kdyby\Doctrine\EntityManager;
use Nette\Object;

/**
 * Fasáda pro manipulaci s uživateli.
 * @package App\Model\Facades
 */
class UserFacade extends Object
{
    /** @var EntityManager Manager pro práci s entitami. */
    private $em;

    /**
     * Konstruktor s injektovanou třídou pro práci s entitami.
     * @param EntityManager $em automaticky injektovaná třída pro práci s entitami
     */
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    /**
     * Najde a vrátí uživatele podle jeho ID.
     * @param int|NULL $id ID uživatele
     * @return User|NULL vrátí entitu uživatele nebo NULL pokud uživatel nebyl nalezen
     */
    public function getUser($id)
    {
        return isset($id) ? $this->em->find(User::class, $id) : NULL;
    }
}

Metóda EntityManager::find() vyhľadá entitu User podľa daného ID. Normálne by sme ako prvý parameter mali napísať celý názov triedy App\Model\Entities\User, ale magická konštanta class (zabudovaná priamo v PHP) tento názov vrátane menného priestoru obsahuje, je preto náš zápis o niečo kratšia.

V našom prípade sa buď vrátia načítané entita User (ako načítané chápte objekt naplnený dátami z databázy) alebo NULL (čo vracia metóda EntityManager::find(), ak takú entitu nenájde).

App / config / config.neon

Keďže je UserFacade automaticky injectovaná služba, musíme ju zaregistrovať v našom konfiguračnom súbore:

...
services:
    - App\Model\Facades\UserFacade
    router: App\RouterFactory::createRouter
...

Presenter

App / presenters / BasePresenter.php

Entitu User aj UserFacade budeme využívať v podstate všetkých Presenter, preto ich uložíme do nášho BasePresenteru:

<?php

namespace App\Presenters;

use App\Model\Entities\User as UserEntity;
use App\Model\Facades\UserFacade;
use Kdyby\Translation\Translator;
use Nette\Application\UI\Presenter;
use Nette\Bridges\ApplicationLatte\Template;

/**
 * Základní presenter pro všechny ostatní presentery aplikace.
 * @package App\Presenters
 */
abstract class BasePresenter extends Presenter
{
    /** @persistent null|string Určuje jazykovou verzi webu. */
    public $locale;

    /**
     * @var Translator Obstarává jazykový překlad na úrovni presenteru.
     * @inject
     */
    public $translator;

    /**
     * @var UserFacade Fasáda pro manipulaci s uživateli.
     * @inject
     */
    public $userFacade;

    /** @var UserEntity Entita pro aktuálního uživatele. */
    protected $userEntity;
…

Ďalej si tu preťažíme metódu startup(), kde si vytvoríme entitu User. Ak je používateľ prihlásený, nájdeme informácie z databázy a uložíme ich do entity. Ak nie je, potom vytvoríme entitu s jedinou informácií, a to že používateľ nie je v úlohe administrátora (aby sme nemuseli všade pripisovať podmienku $user->isLoggedIn(), ale stačilo len $userEntity->isAdmin()).

/**
 * Volá se na začátku každé akce, každého presenteru a zajišťuje inicializaci entity uživatele.
 */
public function startup()
{
    parent::startup();
    if ($this->getUser()->isLoggedIn()) {
        $this->userEntity = $this->userFacade->getUser($this->getUser()->getId());
    } else {
        // Abychom mohli použít "$userEntity->isAdmin()", když uživatel není přihlášen.
        $entity = new UserEntity();
        $entity->role = UserEntity::ROLE_USER;
        $this->userEntity = $entity;
    }
}

Nezabudnite do use uviesť

use App\Model\Entities\User as UserEntity

Ďalšie metóda beforeRender() odovzdáva premenné šablóne ešte pred jej vykreslením - je jedno, o ktorú šablónu sa jedná - odovzdá je teda každej šablóne, ktorá sa bude vykresľovať. Keďže sa naša entita hodí vo všetkých šablónach, odovzdáme ju opäť v BasePresenter.

/**
 * Volá se před vykreslením každého presenteru a předává společné proměnné do celkového layoutu webu.
 */
public function beforeRender()
{
    parent::beforeRender();
    $this->template->userEntity = $this->userEntity;
}

Šablóny

App/presenter­s/templates/@la­yout.latte

Layout je hlavná šablóna, ktorá sa vykresľuje vždy. Šablóny pre jednotlivé stránky sa potom vloží makrom {include} do obsahu.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">

    <title>Nette Doctrine 2 blog</title>

    <link rel="stylesheet" href="{$basePath}/css/style.css">
    <link rel="shortcut icon" href="{$basePath}/favicon.ico">
    <meta name="viewport" content="width=device-width">
    {block head}{/block}
</head>
<body>
    <div id="container">

        {if count($flashes) > 0}
            <div id="flashes">
                <div class="text">
                    <div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
                        {$flash->message}
                    </div>
                </div>
            </div>
        {/if}

        <div id="header">
            <div id="logo">
                <h1>Blog system</h1>
            </div>
            <div id="userInfo">
                {if $user->isLoggedIn()}
                    {_common.loggedAs}: {$userEntity->name}
                {/if}
            </div>
            <div id="menu">
                <a n:href="Homepage:default">{_menu.homepage}</a>
            </div>
        </div>
        <div id="content">
            {include content}
        </div>
        <div id="footer">
            <div class="text">
                &copy; Konesoft Corporation
            </div>
        </div>
    </div>

    {block scripts}
    <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
    <script src="//nette.github.io/resources/js/netteForms.min.js"></script>
    <script src="{$basePath}/js/main.js"></script>
    {/block}
</body>
</html>

Nette okrem premennej $user odovzdáva šablónam automaticky aj premenné $flashes, ktorá obsahuje polia flash správ (tie sa vytvoria metódou Nette\Application\UI\Control::flashMessage($message, $type)), a $basePath - tá obsahuje názov koreňovej zložky projektu.

Všimnite si, že texty v našej šablóne sú pripravené pre preklad. K tomuto článku priložím všetky súbory pre preklad, nech to nemusíme v každom článku dopisovať. Je tu podpora pre český a anglický jazyk.

Tiež bude priložený CSS súbor. Príde mi bezdôvodné ho tu ukazovať a vysvetľovať (je k stiahnutiu v projekte), dizajn nie je cieľom tejto série. Rovnako si ho upravíte podľa vlastného vkusu.

Poznámka: Pri preklade znamená prvé slovo názov súboru, ostatné sú názvy kľúčov. Preto makro {_menu.homepage} použije kľúč homepage zo súboru menu (presný súbor sa ešte určí podľa jazyka). Súbory sú vo formáte NEON.

App / presenters / templates / Homepage / default.latte

Na úvodnej stránke bude zobrazené niekoľko najnovších článkov. To si vytvoríme až neskôr, teraz pridáme len stránku s nadpisom, aby Nette nevyhadzovali chybu 404.

{block content}
<h2>{_homepage.header}</h2>

To je z dnešnej lekcie všetko. Nabudúce, v lekcii Cms v Nette a Doctrine 2 - Registrácia užívateľa , pridáme možnosť registrácie užívateľa.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 274x (3.43 MB)
Aplikácia je vrátane zdrojových kódov v jazyku PHP

 

Predchádzajúci článok
Cms v Nette a Doctrine 2 - Kostra aplikácie
Všetky články v sekcii
Cms v Nette a Doctrine 2
Preskočiť článok
(neodporúčame)
Cms v Nette a Doctrine 2 - Registrácia užívateľa
Článok pre vás napísal Martin Konečný (pavelco1998)
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se o IT moc nezajímá, raději by se věnoval speciálním jednotkám jako jsou SEALs nebo SAS. Když už to ale musí být něco z IT, tak tvorba web. aplikací v PHP. Také vyvýjí novou českou prohlížečovou RPG hru a provozuje osobní web http://www.mkonecny.cz
Aktivity