Lekce 3 - CMS v Nette a Doctrine 2 - Modely a Layout

Vydávání, hosting a aktualizace umožňují jeho sponzoři.
V minulé lekci, CMS v Nette a Doctrine 2 - Kostra aplikace, jsme si založili projekt se všemi potřebnými knihovnami a úpravami. V dnešním Nette tutoriálu začneme tvořit modelovou vrstvu a přidáme layout s úvodní stránkou.

Model
Klíčem k práci s Doctrine jsou anotace. Pomocí nich si uloží do cache informace o entitách, jako jsou názvy sloupců, jejich datové typy, vazby mezi entitami atd. Pokud tedy například pomocí anotace určíme, že proměnná bude typu boolean, při výběru dat z databáze se hodnota automaticky přetypuje na TRUE nebo FALSE (v databázi hodnota bude uložena jako 1 / 0). Informace lze zapisovat i pomocí formátu XML nebo YAML, ale anotace jsou nejpoužívanější.
Entity
app/model/entities/User.php
Vytvoříme si naši první jednoduchou entitu, která bude reprezentovat
uživatele. Nezapomínejte na to, že Nette obsahuje třídu
Nette\Security\User
, jejíž objekt je ve všech presenterech a
šablonách v proměnné $user
. Neměli bychom tuto proměnnou
přepsat, proto se proměnná naší entity bude v šablonách jmenovat
$userEntity
.
Pro začátek bude naše entita velmi jednoduchá. Obsahuje definici
atributů, které odpovídají sloupcům v databázové tabulce
user
. Název je odvozen od názvu entity, proto pokud bychom měli
jiný název tabulky, přidáme anotaci
@ORM\Table(name="nazev_tabulky")
.
Stejně to platí i u názvů sloupců, tam přidáme do závorky parametr
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;
}
}
Pro ID je potřeba zapsat anotací více, Doctrine ho poté automaticky použije jako primární klíč.
Dále si všimněte metody isAdmin()
- entita není jen
přepravkou na data, ale může umět i jednoduché operace.
Všechny naše entity budou dědit od základní entity, vytvořené
rozšířením Kdyby. Získá díky tomu některé příjemné funkce, např.
nebudeme muset psát settery a gettery a místo toho budeme moci používat
přímo $user->name
.
Poznámka: Pokud atributu určíme datový typ date
nebo
datetime
, Doctrine automaticky při tahání data z databáze
vytvoří objekt třídy DateTime
.
Fasády
Fasády budou služby (tzn. že v celé aplikaci bude jen jedna instance dané třídy), které budou zprostředkovávat veškeré operace.
app/model/facades/UserFacade.php
Pro začátek bude opět naše třída velmi jednoduchá. Konstruktorem
automaticky dostane objekt třídy Kdyby\Doctrine\EntityManager
(opět malé vylepšení EntityManager
od Doctrine) a přidáme
metodu na vyhledání uživatele podle 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;
}
}
Metoda EntityManager::find()
vyhledá entitu User podle daného
ID. Normálně bychom jako první parametr měli napsat celý název třídy
App\Model\Entities\User
, ale magická konstanta class
(zabudovaná přímo v PHP) tento název včetně jmenného prostoru obsahuje,
je proto náš zápis o něco kratší.
V našem případě se buď vrátí načtená entita User (jako načtená
chápejte objekt naplněný daty z databáze) nebo NULL (což vrací metoda
EntityManager::find()
, pokud takovou entitu nenajde).
app/config/config.neon
Jelikož je UserFacade
automaticky injectovaná služba, musíme
ji zaregistrovat v našem konfiguračním souboru:
...
services:
- App\Model\Facades\UserFacade
router: App\RouterFactory::createRouter
...
Presentery
app/presenters/BasePresenter.php
Entitu User
i UserFacade
budeme využívat v
podstatě všech presenterech, proto je uložíme do našeho 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;
…
Dále si zde přetížíme metodu startup()
, kde si vytvoříme
entitu User
. Pokud je uživatel přihlášen, najdeme informace z
databáze a uložíme je do entity. Pokud není, pak vytvoříme entitu s
jedinou informací, a to že uživatel není v roli administrátora (abychom
nemuseli všude připisovat podmínku $user->isLoggedIn()
, ale
stačilo jen $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;
}
}
Nezapomeňte do use
uvést
use App\Model\Entities\User as UserEntity
Další metoda beforeRender()
předává proměnné šabloně
ještě před jejím vykreslením - je jedno, o kterou šablonu se jedná -
předá je tedy každé šabloně, která se bude vykreslovat. Jelikož se naše
entita hodí ve všech šablonách, předáme ji opět 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;
}
Šablony
app/presenters/templates/@layout.latte
Layout je hlavní šablona, která se vykresluje vždy. Šablony pro
jednotlivé stránky se poté vloží makrem {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">
© 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 kromě proměnné $user
předává šablonám automaticky
i proměnné $flashes
, která obsahuje pole flash zpráv (ty se
vytvoří metodou
Nette\Application\UI\Control::flashMessage($message, $type)
), a
$basePath
- ta obsahuje název kořenové složky projektu.
Všimněte si, že texty v naší šabloně jsou připravené pro překlad. K tomuto článku přiložím všechny soubory pro překlad, ať to nemusíme v každém článku dopisovat. Je zde podpora pro český a anglický jazyk.
Také bude přiložen CSS soubor. Přijde mi bezdůvodné ho tu ukazovat a vysvětlovat (je ke stažení v projektu), design není cílem této série. Stejně si ho upravíte dle vlastního vkusu.
Poznámka: U překladu znamená první slovo název souboru, ostatní jsou názvy klíčů. Proto makro {_menu.homepage} použije klíč homepage ze souboru menu (přesný soubor se ještě určí podle jazyka). Soubory jsou ve formátu NEON.
app/presenters/templates/Homepage/default.latte
Na úvodní stránce bude zobrazeno několik nejnovějších článků. To si vytvoříme až později, nyní přidáme jen stránku s nadpisem, aby Nette nevyhazovalo chybu 404.
{block content}
<h2>{_homepage.header}</h2>
To je z dnešní lekce vše. Příště, v lekci CMS v Nette a Doctrine 2 - Registrace uživatele, přidáme možnost registrace uživatele.
Stáhnout
Staženo 226x (3.43 MB)
Aplikace je včetně zdrojových kódů v jazyce PHP
Komentáře


Zobrazeno 10 zpráv z 18. Zobrazit vše