Zend Framework - Hello World projekt (a MVC pattern)

11. Březen 2007

Pozor – obsah tohoto článku pojednává o Zend Frameworku verze 0.7, která jehož API se od finální verze 1.0 velmi liší. V článku PHP seminář podzim 2007 (a moje první přednáška) je za námi najdete odkaz na příklad, který je již vytvořen v této nové verzi.

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_Ac­tion). Jedná se o implementaci návhového vzoru Front Controller.
Zend_Controller_Re­quest_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_Re­quest_Http.
Zend_Controller_Rou­ter_Interface
Interface, jehož implementace mají za úkol podle požadavku (instance Zend_Controller_Re­quest_Http) zvolit actioncontroller a action (metodu), která jej bude obsluhovat. Základní implementací je Zend_Controller_Rou­ter_Rewrite.
Zend_Controller_Dis­patcher_Inter­face
Dispatcher je zodpovědný za zavolání actioncontrolleru a action, kterou mu předal Zend_Controller_Rou­ter_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_Ac­tion
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_Fron­t). 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_Rou­ter_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_Rou­ter_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://framewor­k.zend.com/con­troller/action/key­1/value1/

Proberme si jednotlivé části tohoto URL:

http://framewor­k.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.va­vru.cz/ bude zvolen controller s názvem IndexController (a akce s názvem indexAction).
  • Pro http://www.va­vru.cz/kontak­t/ bude zvolen controller s názvem KontaktController (a akce s názvem indexAction).
  • Pro http://www.va­vru.cz/php/uvod-do-zend-frameworku/ bude zvolen controller s názvem PhpController (a akce s názvem uvodDoZendFra­meworkuAction).

Definování vlastních cest

Takto se chová v případě, že nedefinujete žádné svoje routy (cesty) – objekty typu Zend_Controller_Rou­ter_Route. Jistě už je chtěli namítnout, že toto chování ne vždy zcela vyhovuje.

Vezměme si znovu URL http://www.va­vru.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_Contro­ller_Router_Rew­rite se dozvíte na stránce Zend_Controller_Rou­ter_Rewrite:http://fra­mework.zend.com/…clas­ses.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/dri­vers/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/contro­llers/, view – app/view/php, servisní třídy – app/business/ser­vice/ a nakonec například doménové objekty – app/business/per­s/).
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/contro­llers/IndexCon­troller.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/ser­vice/ArticleSer­vice.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

Článek patří do kategorie: PHP, Zend Framework

14 Komentářů Přidat komentář

  • 1. error414  |  18. Březen 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. Březen 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. Březen 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. Březen 2007 v 13.02

    Doufam ze po prvnim komentari me hned neodpolkujete ale uz se docela chytam, jen jsem narazil jsem na maly problem.

    setController­Directory(‚./‘);
    $controller->dispatch();
    ?>

    v tomto pripade to nic nevypise.

    $_SERVER[‚REQU­EST_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. Březen 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. Březen 2007 v 15.16

    [5] to nastavni vyjimky to vyresilo, diky

  • 7. Ronnie  |  21. Březen 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. Duben 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. Duben 2007 v 17.05

    [8]Licencování ZF:
    Máte pravdu :) Skutečně Vás odkážána stránky ZF. Na stránce http://framewor­k.zend.com/li­cense/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. Duben 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. Leden 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. Leden 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. Září 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. Leden 2012 v 1.17

    Hi…

    http://www.web­camgirls4.com/

Přidat komentář

Povinné

Povinné, skryté

Security Image Povinné
Opište text z obrázku

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


Kalendář

Říjen 2014
P Ú S Č P S N
« Led    
 12345
6789101112
13141516171819
20212223242526
2728293031  

Poslední články

Locations of visitors to this page