Zend Framework - Hello World projekt (a MVC pattern)
11. March 2007
V minulé článku jsem popsal jednotlivé části Zend Frameworku. V dnešním článku bych chtěl ukázat vytvoření Hello World projektu, ukázat si samotný základ frameworku, kterým je MVC.
Co znamená Model View Controller
Z praxe vím, že spousta phpkářů neví co magická zkratka MVC vlastně znamená. Model View Controller je jeden z nejzákladnějších (a hojně používaných) návrhových vzorů, které se používají pro webové aplikace (najdou se programátoři, který jej používají i pro desktopové aplikace).
MVC je návrhový vzor, pomocí kterého lze řešit obsluhu webových požadavků (requestů) a vygenerování odpovědi (response). Každé slovo z tříslovného názvu představuje jednu část systému. Pojďme si je přestavit.
Model
Model jsou data, která se vypisují na stránce. Dá se to říci i takto sekernicky. Podrobnější popis říká, že to objekt (kolekce objektů), které jsou volány ve View. Modelem může obsahovat de facto libovolnou kolekci objektů (čísla, řetězce, a jednoduše jakéhokoliv potomka třídy Object). Takže například na této stránce tvoří model následující objekty (zjednodušeno):
- objekt článek (má nadpis, text)
- seznam rubrik
- seznam odkazů
- položky hlavního menu
- komentáře pod článkem
Z jiného úhlu pohledu jsou modelem všechna data, pro jejich zjištění bylo nutné zavolat nějakou část kódu, která přistupuje k datům naší webové aplikace. To může být například:
- přístup do databáze
- XML soubory
- filesystém
- paměť počítače
View
Úkolem View části je prezentovat model ve vhodné formě clientovi. Můžeme tedy mít jeden Model a několik různých View. Jedno View budeme například mít pro mobilní telefony a jiné pro clienty, kteří používají moderní webové prohlížeč (IE, Mozilla, Firefox). View je tedy nejčastěji (X)HTML stránka, PDF, XLS. Můžete totiž chtít ta samá data prezentovat v nejrůznějších formátech. Jediným úkolem View je tedy prezentace dat (modelu). Je tedy přísně zakázáno ve View přistupovat k datové vrsvě naší aplikace a provádět nad ní jakékoliv operace.
Controller
Controller je část návrhového vzoru MVC, která se stará o sestavení Modelu a výběr View pro požadavek klienta. Typickými operacemi, které patří do popisu Controlleru jsou:
- na základě URL (a někdy i hlaviček požadavku) určí která část kódu (obvykle třída) bude obsluhovat daný požadavek
- přístup k datové vrstvě modelu a provádění CRUD operací což vyústí k vytvoření Modelu
- předání Modelu příslušnému View a jeho vygenerování
Výhoda tohoto návrhového vzoru je fakt, že jednotlivé se jednotlivé části chovají jako zapouzdření komponenty. Co tím mám na mysli:
- View nemá ani tušení o tom odkud a jakým způsobem byl vygenerován Model
- Controller se nestará o to jakým způsobem budou data prezentována ve View. Pouze se postará o jeho výběr
- Model ani View nemusí nic tušit o tom jaký požadavek klient odeslal (například jaké bylo URL, jaké parametry byly s požadavkem odeslány)
A nyní se podívejme jako má MVC naimplementován Zend Framework a pojďme vytvořit Hello World projekt.
Implementace MVC v Zend Frameworku
Podívejme se jak je návrhový vzor MVC implementován v Zend Frameworku – Zend_Controller.
Zend_Controller
V této části nejprve vyjmenuji všechny komponenty controlleru.
- Zend_Controller_Front
- Tato třída zpracovává veškeré požadavky, které chceme přes ZF
zpracovávat a je zodpovědná za jejich delegování příslušným
actioncontrollerům (
Zend_Controller_Action
). Jedná se o implementaci návhového vzoru Front Controller. - Zend_Controller_Request_Abstract
- Abstraktní třída, která představuje požadavek a poskytuje metody pro
přístup k jeho parametrům a k actioncontrolleru a příslušné
action (metodě actioncontrolleru). Defaultní implementace je
Zend_Controller_Request_Http
. - Zend_Controller_Router_Interface
- Interface, jehož implementace mají za úkol podle požadavku (instance
Zend_Controller_Request_Http
) zvolit actioncontroller a action (metodu), která jej bude obsluhovat. Základní implementací je Zend_Controller_Router_Rewrite. - Zend_Controller_Dispatcher_Interface
- Dispatcher je zodpovědný za zavolání actioncontrolleru a action, kterou
mu předal
Zend_Controller_Router_Interface
. K názvu controlleru je přidáno Controller a k názvu akce action. Toto se může měnit dle nastavení nebo implementace. - Zend_Controller_Action
- Abstraktní třída kterou musíme (jako jedinou z vyjmenovaných
objektů) implementovat v našich projektech. Jedná se
o actioncontroller (neplést s front controllerem –
Zend_Controller_Front
). V této třídě se bude nacházet kód, který je na konci tohoto řetězce a který bude nastavovat model a volat příslušné view.
Jak funguje Zend_Controller_Router_Rewrite?
Jistě jste se již někdy setkali s tímto kódem:
if($loc2 == 'authors'){
echo '<h2> ';
echo "{$g_page_info['name']}";
if($author_id > 0){
echo " - ".ltext('Detaily autora', 'Author details');
echo '</h2>';
}else{
echo " - ".ltext('Seznam autorů', 'Authors review');
echo '</h2>';
include(ltext('./clanky/authors_text.php', './clanky/authors_text_eng.php'));
}
echo show_authors($author_id);
}elseif($loc2 == 'fotka_dne'){
include('fotka_dne.php');
}elseif($loc2 == 'fotka_dne_edit'){
echo '<h2> ';
echo "{$g_page_info['name']}";
echo " - Top foto editace";
echo '</h2>';
include('fotka_dne_edit.php');
}
Takto lze psát PHP aplikace také (samozřejmě né za použití ZF). Ovšem routování je zadrátováno do view. Udržovat také stránky je noční můra. Kdo tímto způsobem někdy dělal větší projekt ví o čem mluvím. Krom toho – toto řešení nemá nic společného s objektovým přístupem. Čímž samozřejmě neříkám, že vše neobjektové je špatné. Jenom chci říci, že jakákoliv změna routovací politiky (změna parametrů, celé URL) bude znamenat zásahy do zdrojového kódu stránek (PHP stránky vlastně ani jiný než zdrojový kód de facto nemají :)).
Zend_Controller_Router_Rewrite
na to jde podobně, ale
mnohem inteligentněji. Controller a akci určuje podle URL požadavku a
případně podle jeho parametrů. K tomu používá
následující mapování:
http://framework.zend.com/controller/action/key1/value1/
Proberme si jednotlivé části tohoto URL:
- http://framework.zend.com
- host nebo ROOT aplikace.
- controller
- tento string bude převeden na název controlleru. Jeho defaultní hodnota je index.
- action
- tento string bude převeden na název akce. Jeho defaultní hodnota je index.
- key1
- třetí část URL je považován a název proměnné
- value1
- a čtvrtý za jeho hodnotu
Pár příkladů
- Pro http://www.vavru.cz/ bude zvolen controller s názvem IndexController (a akce s názvem indexAction).
- Pro http://www.vavru.cz/kontakt/ bude zvolen controller s názvem KontaktController (a akce s názvem indexAction).
- Pro http://www.vavru.cz/php/uvod-do-zend-frameworku/ bude zvolen controller s názvem PhpController (a akce s názvem uvodDoZendFrameworkuAction).
Definování vlastních cest
Takto se chová v případě, že nedefinujete žádné svoje routy
(cesty) – objekty typu Zend_Controller_Router_Route
.
Jistě už je chtěli namítnout, že toto chování ne vždy zcela
vyhovuje.
Vezměme si znovu URL http://www.vavru.cz/php/uvod-do-zend-frameworku/. Vidíme, že php je název kategorie a uvod-do-zend-frameworku je nice URL unikátní pro článek. Bylo by vhodné, kdyby všechny požadavky na zobrazení článku obsluhoval jeden controlleru (bez ohledu na kategorii).
$router->addRoute('clanek', new Zend_Controller_Router_Route(':categoryStr/:acticleStr', , array('controller' => 'article', 'action' => 'detail'));
);
Toho samého výsledku lze dosáhnout i definicí routy v properties souboru:
routes.clanek.route = "categoryStr/:acticleStr"
routes.clanek.defaults.controller = "article"
routes.clanek.defaults.action = "detail"
Routám (cestám) lze nadefinovat i požadavky (jako regulární výrazy) na jednotlivé parametry. Uveďme si opět příklad:
routes.archive.route = "archive/:year/*"
routes.archive.defaults.controller = archive
routes.archive.defaults.action = show
routes.archive.defaults.year = 2000
routes.archive.reqs.year = "\d+"
V rychlost si vysvětleme předchozí kód. Jedná se o definici cesty s názvem archive, kterou bude obsluhovat ArchiveController a akce showAction. Defautlní hodnota pro year je 2000 a year musí být číslo.
Více informací
o Zend_Controller_Router_Rewrite
se dozvíte na
stránce Zend_Controller_Router_Rewrite:http://framework.zend.com/…classes.html#….
Vytvoření Hello World projektu v Zend Frameworku
Budu předpokládat, že čtenáři tohoto článku nemají se ZF žádnou zkušenost. Proto nejprve proberu jeho stažení a „instalaci“.
Stažení a instalace Zend Frameworku
Od minulého článku uplynulo hodně vody, a tak už není pravdivá, že aktuální verze má označení 0.7. Máme tu už verzi 0.8 (a soudě podle čirého ruchu na newslistu na sebe nenechá verze 0.9 dlouho čekat). Nejdříve si tedy stáhneme Zend Framework. Stable verze má velikost asi 14,5 MB (po rozbalení 63 MB) a obsahuje kromě také dokumentaci a inkubátor (komponenty, které zatím nebyly zahrnuty do aktuální verze).
Projekt vytvářím v Eclipse IDE s nainstalovaným PHPECLIPSE pluginem.
Na svém počítači mám nainstalované PHP 5.1.2, Apache 2.0.58 , ve
windows/system32/drivers/etc/hosts mam nastaveno 127.0.0.1
zf-hello-world
. V konfiguračním souboru Apache mám nadefinován
virtualhost:
<VirtualHost *:80>
ServerName zf-hello-world
DocumentRoot d:/Programming/Php/Projects/zend-hello-world/www
</VirtualHost>
V root aplikace mám soubor .htaccess
, který obsahuje:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !\.(js|ico|gif|jpg|png|css|pdf)$ index.php
V našem příkladu jsem vytvořil jednoduchý web, který se skládá z následujících stránek:
- úvodní stránka s přehledem kategorií
- stránka s vybranou kategorií a přehledem článků v kategorii
- stránka s detailem článku
Struktura projektu:
- app
- v tomto adresáři se nacházejí soubory specifické pro každý
projekt (controllery –
app/web/controllers/
, view –app/view/php
, servisní třídy –app/business/service/
a nakonec například doménové objekty –app/business/pers/
). - config
- jak napovídá sám název nacházejí se zde konfigurační soubory. ZF obsahuje zpravidla jeden s názvem zend.ini.
- library
- tento adresář obsahuje kód Zend Frameworku a kód dalších knihoven
- www
- obsahem tohoto adresáře jsou soubory, které chceme, aby byly přímo
přístupné uživateli. Podle best practise by to měl být v zásadě
pouze bootstrap soubor, obrázky, css styly. V tomto projektu jsem to pro
jednoduchost neudělal, ale v produktčním prostředí by měl být
zamezen přistup uživatelům do ostatních adresářů. Například pomocí
nastavení práv v souboru
.htaccess
.
Bootstrap soubor index.php
:
<?php
/*
* created on 11.3.2007
* project: zend-hello-world
* author: vlasta
* version: $
*/
session_start();
set_include_path(get_include_path() . PATH_SEPARATOR . $root . '../library');
set_include_path(get_include_path() . PATH_SEPARATOR . $root . '../app');
require_once 'Zend.php';
// naloadovani konfigurace ze souboru
Zend :: loadClass('Zend_Config_Ini');
$config = new Zend_Config_Ini('../config/zend.ini', 'all');
Zend :: register('config', $config);
// inicializace a nastaveni front controlleru
Zend :: loadClass('Zend_Controller_Front');
$controller = Zend_Controller_Front :: getInstance();
$controller->setControllerDirectory("../app/web/controllers/");
$controller->throwExceptions(true);
// naloadovani cest z konfigurace
Zend :: loadClass('Zend_Controller_Router_Rewrite');
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, 'routes');
$controller->setRouter($router);
// inicializace a konfigurace view
Zend :: loadClass('Zend_View');
$view = new Zend_View();
$view->setScriptPath('../app/view/php/');
Zend :: register('view', $view);
// inicializace testovaciho servisniho objektu
require_once('business/service/ArticleService.php');
$articleService = new ArticleService();
Zend :: register('articleService', $articleService);
// dispatchnuti requestu
$controller->dispatch();
?>
Soubor app/web/controllers/IndexController.php
:
<?php
/*
* created on 11.3.2007
* project: zend-hello-world
* author: vlasta
* version: $
*/
Zend::loadClass('Zend_Controller_Action');
class IndexController extends Zend_Controller_Action {
public function __contruct() {
parent::__construct();
}
public function indexAction() {
$view = Zend::registry('view');
$articleService = Zend::registry('articleService');
$categories = $articleService->findAllCategories();
$view->assign('categories', $categories);
$out = $view->render('index.php');
$this->getResponse()->appendBody($out);
}
public function noRouteAction() {
$view = Zend::registry('view');
$this->getResponse()->setRawHeader("HTTP/1.0 404 Not Found");
$out = $view->render('error404.php');
$this->getResponse()->appendBody($out);
}
}
?>
A poslední ukázka kódu je soubor
app/business/service/ArticleService.php
:
<?php
/*
* created on 11.3.2007
* project: zend-hello-world
* author: vlasta
* version: $
*/
require_once('business/pers/Category.php');
require_once('business/pers/Article.php');
class ArticleService {
private $categories;
public function __construct() {
$this->categories = array ();
$article1 = new Article();
$article1->name = "Úvod do Zend Frameworku";
$article1->str = "uvod-do-zend-frameworku";
$article1->text = "Toto je text článku 'Úvod do Zend Frameworku'";
$article2 = new Article();
$article2->name = "Zend Framework - Hello World projekt";
$article2->str = "zend-framework-hello-world-projekt";
$article2->text = "Toto je text článku 'Zend Framework - Hello World projekt'";
$article3 = new Article();
$article3->name = "Kódování JSP stránek a fmt tag library";
$article3->str = "kodovani-jsp-stranek-a-fmt-tag-library";
$article3->text = "Toto je text článku 'Kódování JSP stránek a fmt tag library'";
$cat1 = new Category();
$cat1->name = "PHP";
$cat1->str = "php";
$cat1->articles = array (
$article1,
$article2
);
$cat2 = new Category();
$cat2->name = "Java";
$cat2->str = "java";
$cat2->articles = array (
$article3
);
$article1->category = $cat1;
$article2->category = $cat1;
$article3->category = $cat2;
$this->categories = array($cat1, $cat2);
}
public function getCategoryByStr($categoryStr) {
foreach ($this->categories as $category) {
if ($category->str == $categoryStr) {
return $category;
}
}
return null;
}
public function getArticleByStr($articleStr) {
foreach ($this->categories as $category) {
foreach ($category->articles as $article) {
if ($article->str == $articleStr) {
return $article;
}
}
}
return null;
}
public function findAllCategories() {
return $this->categories;
}
}
?>
Nemá význam uvádět zde kód všech souborů z našeho příkladu. Raději si stahněte celý Zend Framework Hello World projekt (ZIP, 1,7 MB).
Závěr
Celý Zend_Controller
je velmi rozáhlá část ZF. V tomto
článku jsem uvedl pouze základní schopnosti. Nebylo totiž účelem jej
představit vyčerpávajícím způsobem. ZF a jeho controller jsme ve firmě použili již na spoustě
projektů a je s ním báječná práce.
Odkazy
- Zend Framework User Space
- Tutoriály o Zend Frameworku
- Zend Framework Programmer's Reference Guide
- Zend Framework Hello World projekt (ZIP, 1,7 MB)
Článek patří do kategorie: PHP, Zend Framework
14 Komentářů Přidat komentář
1. error414 | 18. March 2007 v 19.52
Hezke ale z prikladu se ned nic pochopit, silene moc balastu co nema nic spolecneho s MVC. Chybi ukazka ini souboru.
Priklady pro Hallo word maji byt male a rychle pochipitelne coz tohle neni.
2. Vlasta | 18. March 2007 v 21.51
[1] Souhlasím s tím, že příklad není úplně jednoduchý. Tím to ovšem končí. Ukázka ini souboru v příkladu je. Stačí si jej stáhnout a rozbalit. A „balast“ jsem do příkladu zařadil kvůli tomu, aby i naprostý začátečník na příkaldu viděl a pochopil co je to model a jakým způsobem se získává.
Popis vlastního MVC jsem uvedl v článku včetně popisu jeho implementace v ZF. Stačí si projít Hello World projekt a jeho komentáře a příslušné asociace vyplavou na povrch.
3. error414 | 19. March 2007 v 13.21
Ja se omlouvam ale uz jsem byl vytocen z originalni dokumentace. No a vyradil jsem se zde. Jsem bourliva povaha.
Diky za takovy uceleny pohled.
4. error414 | 20. March 2007 v 13.02
Doufam ze po prvnim komentari me hned neodpolkujete ale uz se docela chytam, jen jsem narazil jsem na maly problem.
setControllerDirectory(‚./‘);
$controller->dispatch();
?>
v tomto pripade to nic nevypise.
$_SERVER[‚REQUEST_URI‘] = '/index/neexistuje;
Timto supluju adresu, protoze me nejede rewrite.
Proste kdyz neexistuje controler ani akce tak se na obrazovku nic nevypise.
5. Vlasta | 20. March 2007 v 13.11
[4] Nevím co přesně Vám nefunguje. ZF lze samozřejmě provozovat i bez modu rewrite a nebo také v podadresáři.
Pokud máte projekt v podadresáři je nutné kontroleru nastavit subdirectory. Pokud Vám nefunguje rewrite nelze používat nice URLs…a musíte si informace předávat v parametrech.
Ale ZF bez rewritu ztrácí své kouzlo…
Ještě maličkost – pokud se Vám nevypisuje nic, ani chybová hláška, pak nastavte kontroleru throwExceptions na true.
6. error414 | 20. March 2007 v 15.16
[5] to nastavni vyjimky to vyresilo, diky
7. Ronnie | 21. March 2007 v 1.55
Hezké, jen bych session_start() na začátku nevolal pokud nepoužíváte Zend_Auth. A navíc v novější verzi je už Zend_Session plně použitelné.
JInak light tutoriál do MVC & ZF je i zde
http://weblog.ronnieweb.net/?p=57 (také Hello World)
8. Eggze | 24. April 2007 v 16.52
Dobrý den,
měl bych dotaz ohledně licenčních podmínek při použití Zend Frameworku. Lze to bez problémů používat na komerční weby apod.? Možná hloupý dotaz a odkážete mně na stránky Zendu, ale jestli Vám to nebude vadit, byl bych rád, kdybyste odpověděl.
A ještě mně zajímá, jestli mohou vzniknout nějaké problémy na hostingu při použití Zend Frameworku, nevyžaduje nějaké speciální funkce z PHP, které na běžných hostinzích nejsou dostupné?
Děkuji moc za odpověď.
9. Vlasta | 24. April 2007 v 17.05
[8]Licencování ZF:
Máte pravdu :) Skutečně Vás odkážána stránky ZF. Na stránce http://framework.zend.com/license/new-bsd se praví, že ZF je distribuován pomocí BSD Licence, která umožňuje volné používání za dodržení podmínek viz uvedený odkaz.
A ještě mně zajímá, jestli mohou vzniknout nějaké problémy na hostingu při použití Zend Frameworku.
V dnešní době jsem se s problémy už nesetkal. Dnes je všude PHP 5.1.x. Problém na některých (zejména free hostinzích) může být s Apache rewritem.
10. Eggze | 24. April 2007 v 18.12
Tak ohledně práv už jsem našel informaci na interval.cz:
Jak je to s autorskými právy?
Za používání Zend Frameworku nemusíte platit žádný poplatek, a to ani při použití pro komerční účely. Framework můžete dále rozšiřovat, vaše třídy však nesmí začínat předponou „Zend“.
11. Lukáš | 3. January 2008 v 1.05
Stáhl jsem si 1.0.3 verzi a zjistil, že příklad nefunguje, jelikož knihovna neobsahuje žádný soubor Zned.php. To znamená že další verze asi nebudou zpětně kompatibilní. :(
12. Vlasta | 3. January 2008 v 1.51
[11]Lukáši neházev flintu do žita. Na samém úvodu článku uvádím odkaz na článek o PHP semináři 2007 v rámci které jsem jsem presenoval ZF i s přkladem.
13. Mixxy | 25. September 2008 v 20.24
Díky alespoň za tento kousek informací. Chystám se napsat takový MVC jako semestrální projekt, takže každá rada dobrá ;)
Mixxy
14. Steve&hellip | 10. January 2012 v 1.17
Hi…
http://www.webcamgirls4.com/…
Přidat komentář
Povolené HTML značky:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Odkazovat na tento článek | Přihlásit se k odběru těchto komentářů přes RSS Feed