Začínáme se Zend Framework 1.0
Tento dokument se v anglickém originále nachází na stránkách Akra's DevNotes – Getting Started with the Zend Framework. Sepsal ho Rob Allen. Překlad byl sepsán krátce po vyjití Zend Framework verze 1.0 (25.8.2008). V současné době je na světě Zend Framework 1.5 a Rob Allen aktualizoval jeho článek Getting Started with the Zend Framework 1.5.
Verze | Zend Framework | Poslední aktualizace |
---|---|---|
1.4.4 | 1.0 a novější | 6.4.2008 |
Tento tutoriál byl vytvořen proto, aby vám poskytl základní představu o použití Zend Framework. Jeho využití si budeme demonstrovat na základní aplikaci využívající databázi.
Tento tutoriál byl vytvořen a otestován na Zend Frameworku ve verzi 1.0.0. Je pravděpodobné, že bude fungovat i na novějších verzích. Je ale nepravděpodobné, že bude fungovat na verzi nižší.
MVC (Model-View-Controller) architektura aplikace
Tradiční způsob tvorby PHP aplikací je asi takový:<?php
include "common-libs.php";
include "config.php";
mysql_connect($hostname, $username, $password);
mysql_select_db($database);
?>
<?php include "header.php"; ?>
<h1>Home Page</h1>
<?php
$sql = "SELECT * FROM news";
$result = mysql_query($sql);
?>
<table>
<?php
while ($row = mysql_fetch_assoc($result)) {
?>
<tr>
<td><?php echo $row['date_created']; ?></td>
<td><?php echo $row['title']; ?></td>
</tr>
<?php
}
?>
</table>
<?php include "footer.php"; ?>
Během vývoje aplikace ale dojdeme k závěru, že tento postup je neefektivní, pokud vytváříme nějaké změny. Tyto změny navíc musí být provedeny v různých dokumentech.
Jednou z možností, jak si vývoj aplikace usnadnit je rozdělit kód na stránce do 3 různých částí (a většinou i samostatných souborů):
Model | Model je část aplikace, která je zodpovědná za výběr dat, která budou zobrazena. Na příkladu uvedeném o pář řádku výše by se jednalo o výběr novinek z databáze a jejich uložení do pole. |
---|---|
View | View se stará o způsob, jakým jsou data prezentována uživateli. Nejčastěji se tedy jedná o HTML. |
Controller | Controller se stará o vytvoření modelu a zároveň volá správný view. |
Zend Framework využívá MVC (Model-View-Controller) architekturu. Ta se používá pro oddělení rozdílných částí aplikace a tím také snažší vývoj a údržbu.
Požadavky
Zend Framework požaduje:- PHP 5.1.4 (a novější)
- Webový server podporující mod_rewrite. V tomto tutoriálu je použit Apache.
Začínáme se Zend Framework
Zend Framework si můžete stáhnout ze stránek http://framework.zend.com/download ve formátu .zip nebo .tar.gz.Adresářová struktura
Zend Framework vám nenařizuje striktně používat jednotnou strukturu aplikace, ale je doporučené držet se navrhované struktury v manuálu. Ta počítá s tím, že máte plnou kontrolu nad konfigurací webového serveru (Apache). Nicméně pro náš tutoriál si strukturu trochu zjednodušíme.Začneme vytvořením adresáře zf-tutorial
v kořenovém
adresáři webového serveru (Apache). To znamená, že URL námi vytvořené
aplikace bude dostupné na adrese http://localhost/zf-tutorial
.
Vytvořte následující podadresáře:
zf-tutorial/
/application
/controllers
/models
/views
/filters
/helpers
/scripts
/library
/public
/images
/scripts
/styles
Jak vidíte, vytvořili jsme rozdílné adresáře pro model, view a
controller soubory naší aplikace. Podporované obrázky, skripty a CSS soubory
jsou uloženy ve zvláštních adresářích ve složce public
.
Soubory Zend Framework budou uloženy v adresáři library
. Pokud
budeme potřebovat nějaké další knihovny, je vhodné umístit je zde.
Rozbalte soubory z archivu ZendFramework-1.0.0.zip do
dočasné složky. Všechny soubory jsou umístěny v podadresáři
ZendFramework-1.0.0
. Zkopírujte obsah adresáře
library/Zend
do zf-tutorial/library/
. V adresáři
zf-tutorial/library/
tedy budete mít podadresář
Zend
.
Bootstrap
Zend Framework a jeho controller Zend_Controller byl vytvořen tak, aby podporoval hezká URL. Pokud tohoto chcete dosáhnout, musí jít veškeré požadavky na aplikaci přes souborindex.php
, kterému se říká
bootstrap soubor. Veškeré požadavky tedy budou přesměrovány na tento
soubor pomocí souboru .htaccess
v adresáři
zf-tutorial
:
zf-tutorial/.htaccess
RewriteEngine on
RewriteRule .* index.php
php_flag magic_quotes_gpc off
php_flag register_globals off
Pravidlo RewriteRule je velmi jednoduché a slovně ho lze vyjádřit
„vezmi jakoukoliv URL a přesměruj na index.php
“.
Také definujeme několik PHP ini nastavení kvůli bezpečnosti. Tyto
parametry jsou pravděpodobně již nastaveny, ale my se potřebujeme ujistit,
že tomu skutečně tak je. Poznámka: php_flag nastavení
v souboru .htaccess
funguje pouze pokud používáte mod_php.
Pokud používáte CGI/FastCGI, budete muset nastavení provést
v konfiguračním souboru php.ini
.
Nesmíme opomenout, že obrázky, skripty a CSS soubory nemohou být
přesměrovány na bootstrap soubor. Proto všechny tyto soubory musíme
umístit do příslušných podadresářů ve složce public
.
Webovému serveru (Apache) jednoduše nařídíme v .htaccess
souboru:
zf-tutorial/public/.htaccess
RewriteEngine off
Není to nezbytné, ale z bezpečnostních důvodů můžeme přidat ještě
několik .htaccess
souborů, aby jsme se ujistili, že naše
aplikace a její knihovny jsou chráněny:
zf-tutorial/application/.htaccess
deny from all
zf-tutorial/library/.htaccess
deny from all
Nezapomínejte, že soubory .htaccess
používá webový server
Apache a v nastavení serveru (soubor httpd.conf
) musí být
direktivita AllowOverride nastavena na all. Původní nápad s použítím
více .htaccess
souborů pochází z článku od Jaysona
Minarda – Blueprint for
PHP Applications: Bootstrapping (Part 2). Doporučuji Vám přečíst si
i první část.
Bootstrap soubor: index.php
zf-tutorial/index.php
je náš bootstrap soubor a my nyní
začneme s následujícím kódem:
zf-tutorial/index.php
<?php
error_reporting(E_ALL|E_STRICT);
date_default_timezone_set('Europe/London');
set_include_path('.' . PATH_SEPARATOR . './library'
. PATH_SEPARATOR . './application/models/'
. PATH_SEPARATOR . get_include_path());
include "Zend/Loader.php";
Zend_Loader::loadClass('Zend_Controller_Front');
// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory('./application/controllers');
// run!
$frontController->dispatch();
Určitě jste si všimli, že na konci souboru chybí ?>
.
Uzavření není potřeba a dokážeme jejím vynecháním předejít mnoha
složitě odhalitelných chyb pokud používáme přesměrování přes
header()
a na konci souboru se vyskytuje „prázdné
místo“.
Pojďme si projít soubor index.php
error_reporting(E_ALL|E_STRICT);
date_default_timezone_set('Europe/London');
Tyto řádky slouží k tomu, že budeme informováni o všech chybách,
které ať už úmyslně nebo neúmyslně vytvoříme (direktivita
display_errors
musí být nastavena na on v souboru
php.ini
). Také zvolíme naše časové pásmo, jak požaduje
specifikace PHP 5.1+.
set_include_path('.' . PATH_SEPARATOR . './library' . PATH_SEPARATOR .
'./application/models/' . PATH_SEPARATOR . get_include_path());
include "Zend/Loader.php";
Zend Framework je koncipován tak, že cesty k jeho souborům musí být
definovány v include_path. Pro usnadnění si do include_path přidáme
i cestu k model, takže budou naše třídy později snadno
dostupné. Jako první připojíme soubor Zend/Loader.php
, který
nám spřístupní třídu Zend_Loader
. Ta obsahuje nezbytné
statické funkce pro nahrání dalších Zend Framework tříd.
Zend_Loader::loadClass('Zend_Controller_Front');
Zend_Loader::loadClass
nahraje zadanou třídu. Toho je
dosaženo tím, že název třídy obsahuje cestu k dané třídě.
Podtržítko symbolizuje podadresář a na konec je přidána přípona .php.
Z názvu třídy Zend_Controller_Front
dostaneme následující
cestu k třídě Zend/Controller/Front.php
. Pokud budete
dodržovat doporučený postup pojmenování, můžete vaše třídy také
nahrávat přes Zend_Loader::loadClass
. První třída, kterou
potřebujeme, je front controller.
Front controller používá třídu router pro analýzu URL a vytvoření
cesty k funkci, která zobrazí správnou stránku. Aby mohla třída router
správně analyzovat URL, musí vědět, která část URL vede
k index.php
a od tohoto místa začít. Za normálních okolností
by měl Request object zjistit umístění souboru index.php
sám.
V některých případech je ale potřeba tuto cestu manuálně nastavit
$frontController->setBaseUrl()
.
Nyní musíme nakonfigurovat front controller tak, aby věděl, kde máme umístěny naše controllers.
$frontController = Zend_Controller_Front::getInstance();
$frontController->setControllerDirectory('./application/controllers');
$frontController->throwExceptions(true);
Protože se jedná pouze o ukázkovou aplikaci, nastavíme front controller tak, aby se zobrazovaly veškeré vyjímky. Defaultně front controller vyjímky zachytí a uloží v objektu „Response“ a proměnné _exceptions. Objekt dále uloží informace o okolnostech, které k vyjímce vedly jako je HTTP hlavička, obsah stránky a vyjímky. Front controller pošle hlavičku a obsah stránky až nakonec, takže se veškeré vyjímky zobrazí hned nahoře stránky. Nicméně na produkčním serveru by uživatel nikdy neměl žádnou vyjímku spatřit!
Nakonec se dostaneme k tomu hlavnímu – aplikaci spustíme:
// run!
$frontController->dispatch();
Pokud nyní načtete stránku http://localhost/zf-tutorial pravděpodobně se vám zobrazí podobná chyba: Fatal error: Uncaught exception ‚Zend_Controller_Dispatcher_Exception‘ with message ‚Invalid controller specified (index)‘ in…
Tato chyba se nám zobrazuje z důvodu toho, že jsme ještě neprovedli veškeré potřebné nastavení aplikace. Nejdříve si ale popíšeme to, co bude naše aplikace vlastně dělat.
Webová stránka
Naše aplikace bude fungovat jako organizér naší sbírky CD. Na hlavní stránce bude zobrazen obsah naší sbírky, a také možnost přidání, editace nebo smazání jednotlivých položek. Pro uložení dat o naší sbírce využijeme databázi, která bude obsahovat tabulku album s následující strukturou:Sloupec | Typ | Nulový | Poznámka |
---|---|---|---|
id | Integer | Ne | Primary key, Autoincrement |
artist | Varchar(100) | Ne | |
title | Varchar (100) | Ne |
Jednotlivé stránky
Home page | Na hlavní stránce se zobrazí obsah naší sbírky spolu s odkazy na přidání, editaci nebo smazání jednotlivých položek. |
---|---|
Add New Album | Tato stránka bude obsahovat formulář pro přidání nového alba. |
Edit Album | Tato stránka bude obsahovat formulář pro editaci alba. |
Delete Album | Na této stránce potvrdíte, jestli opravdu chcete album smazat. |
Umístění jednotlivých stránek
Ještě předtím než začneme vytvářet jednotlivé soubory, je důležité, aby jste pochopili, kde framework bude hledat vaše soubory a proč. Každá stránka se nazývá „action“ a actions jsou sdruženy do „controllers“. Když tedy budeme chtít zobrazit stránku http://localhost/…al/news/view tak pro nás controller bude news a action view. To nám umožňuje sdružovat podobné actions. Například controller news může mít action current, archived a view. Zend Framework a jeho MVC struktura také podporuje moduly, které nám zase umožňují sdružovat controllers dohromady. Tato aplikace ale pro toto použítí není dostatečně rozsáhlá.Zend Framework a jeho controller obsahuje speciální action nazvanou index která je použita jako výchozí. To znamená, že pro URL http://localhost/…torial/news/ je controller news a action index. Jak už možná tušíte, Zend Framework obsahuje i výchozí controller, který je opět nazvaný index. Pokud tedy zadáte URL http://localhost/zf-tutorial/ jako controller se použije index a jako action také index.
Protože se jedná o základní tutoriál, nebudeme u aplikace zacházet do takových detailů, jako je přihlašování a odhlašování uživatelů.
Máme tedy čtyři stránky, které pracují s naší aplikací pro organizování CD. Bude tedy vhodné sdružit je do jediného controlleru a čtyř action. Použijeme výchozí controller index a následující actions:
Stránka | Controller | Action |
---|---|---|
Home page | Index | Index |
Add New Album | Index | Add |
Edit Album | Index | Edit |
Delete Album | Index | Delete |
Nastavujeme Controller
V Zend Frameworku je controller třída a musí být nazvána {název Controlleru}Controller. {název Controlleru} musí začínat velkým písmenem. Tento controller musí být uložen v souboru nazvaném {název Controlleru}Controller.php uvnitř adresáře určeného pro controllery. Nezapomeňte na to, že první písmeno musí být velké a ostatní malá! Každá action je public function, kterou obsahuje třída controller. Akce musí být pojmenována {název action}Action. Pro název action používáme hned od začátku malá písmena.Nyní již máme vytvořenou třídu IndexController, která je uložena
v souboru
zf-tutorial/application/controllers/IndexController.php
:
zf-tutorial/application/controllers/IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
function indexAction()
{
}
function addAction()
{
}
function editAction()
{
}
function deleteAction()
{
}
}
Nyní jsme vytvořili čtyři akce, které zatím nebudou fungovat. Musíme jim definovat views.
URL pro jednotlivé akce jsou:
URL | * Action |
---|---|
http://localhost/zf-tutorial/ | IndexController::indexAction() |
http://localhost/…al/index/add | IndexController::addAction() |
http://localhost/…l/index/edit | IndexController::editAction() |
http://localhost/…index/delete | IndexController::deleteAction() |
Nyní je ten správný čas, pro vytvoření views.
Nastavujeme Views
Komponenta view Zend Frameworku je nazvána vcelku logicky Zend_View. Ta nám umožňuje oddělit kód, který se stará o zobrazení stránky (nejčastěji HTML) od kódu v příslušné action a tím aplikaci spřehlednit.Základní použití Zend_View je následující:
$view = new Zend_View();
$view->setScriptPath('/path/to/view_files');
echo $view->render('view.php');
Tento kód bychom jednoduše mohli umístit do každé action, bylo by to ale
zbytečné. Mnohem lepší je inicializovat třídu view někde jinde a
v action jen přistupovat na objekt třídy view. Vývojáři Zend Framework to
vyřešili tím, že vytvořili tzv. „action helper“.
Zend_Controller_Action_Helper_ViewRenderer se nám tedy postárá
o inicializaci objektu view($this->view) a zároveň vygeneruje view skript.
Dále nastaví objektu Zend_View cestu k view skriptu
views/scripts/{controller name}
. Tento se ve výchozím nastavení
nachází views/scripts/{controller name}/{action_name}.phtml
.
Výsledný obsah stránky je přidán do objektu Response. Objekt Response
sdružuje všechny HTTP hlavičky, obsah body a vyjímky vzniklé použítím
MVC. Front Controller poté automaticky pošle hlavičku a obsah body na konci
dispatchingu.
Nejprve si naplníme actions:
zf-tutorial/application/controllers/IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
function indexAction()
{
$this->view->title = "My Albums";
}
function addAction()
{
$this->view->title = "Add New Album";
}
function editAction()
{
$this->view->title = "Edit Album";
}
function deleteAction()
{
$this->view->title = "Delete Album";
}
}
V každé funkci jsme přiřadili proměnnou title objektu view. Nezapomínejte na to, že tento text se zobrazí teprve až projde přes Front Controller na konci procesu nazvaném dispatching, který se volá až na úplném konci běhu aplikace.
Nyní potřebujeme vytvořit view soubory pro naší aplikaci. Tyto soubory můžeme také nazvat šablony. Metoda render() očekává, že šablony jsou pojmenovány podle přidružených akcí s koncovkou .phtml. Soubory musí být umístěny v podadresáři, který je pojmenován podle controlleru.
zf-tutorial/application/views/scripts/index/index.phtml
<html>
<head>
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<h1><?php echo $this->escape($this->title); ?></h1>
</body>
</html>
zf-tutorial/application/views/scripts/index/add.phtml
<html>
<head>
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<h1><?php echo $this->escape($this->title); ?></h1>
</body>
</html>
zf-tutorial/application/views/scripts/index/edit.phtml
<html>
<head>
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<h1><?php echo $this->escape($this->title); ?></h1>
</body>
</html>
zf-tutorial/application/views/scripts/index/delete.phtml
<html>
<head>
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<h1><?php echo $this->escape($this->title); ?></h1>
</body>
</html>
Pokud nyní otestujete jednotlivé controller/action (jednotlivá URL v tabulce o fous výše), měli by zobrazit title definované v controlleru a přidružené action.
Obyčejný HTML kód
Jednotlivé views obsahují především HTML kód. Protože ale nechceme psát některé jeho části stále dokola v každém souboru zvlášt, vytvoříme si dva soubory ve složcescripts
nazvané
header.phtml
a footer.phtml
. Tyto soubory potom
připojíme v jednotlivých šablonách.
zf-tutorial/application/views/scripts/header.phtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
<div id="content">
zf-tutorial/application/views/scripts/footer.phtml
</div>
</body>
</html>
Naše view soubory potřebují drobnou úpravu:
zf-tutorial/application/views/scripts/index/index.phtml
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('footer.phtml'); ?>
zf-tutorial/application/views/scripts/index/add.phtml
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('footer.phtml'); ?>
zf-tutorial/application/views/scripts/index/edit.phtml
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('footer.phtml'); ?>
zf-tutorial/application/views/scripts/index/delete.phtml
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('footer.phtml'); ?>
Stylování
Sice se jedná pouze o tutoriál, ale neodpoustil jsem si malé grafické vylepšení aplikace pomocí css. Na tomto příkladu si můžeme zároveň ukázat, jak odkazovat na externí soubory. Využijeme funkce getBaseUrl().Použijeme funkci IndexController::init(). Tato funkce je zvláštní tím, že je volána přímo construktorem, a tak je jsou její objekty přístupné ve všech actions.
zf-tutorial/application/controllers/IndexController.php
...
class IndexController extends Zend_Controller_Action
{
function init()
{
$this->view->baseUrl = $this->_request->getBaseUrl();
}
function indexAction()
{
...
CSS soubor musíme připojit v