Začíname so Zend Frameworkom – tutoriál [2/3]

23.12.2007 13:05 | blackhole

V tejto časti budeme pokračovať v tutoriáli, v presne tom mieste, kde sme skončili. Dostaneme sa už k nejakému serióznejšiemu kódeniu a zobrazíme prvé výsledky práce MVC systému.

<prelozeny-text>
Zostavenie controlleru
Teraz sme pripravení zostaviť a napísať controller. V Zend Frameworku je controller triedou, takže sa musí volať podľa syntaxe {Názov Controlleru}Controller. Pamätajte, že {Názov Controlleru} musí začínať veľkým písmenom. Táto trieda je zapísaná v súbore {Názov Controlleru}Controller.php v špecifikovanom adresári s controllermi. Opäť {Názov Controlleru} musí začínať veľkým písmenom, každé ďalšie písmeno musí byť malé. Každá akcia je verejnou funkciou triedy controllera a musí mať názov {názov akcie}Action. V tomto prípade je {názov akcie} malými písmenami.

Náš IndexController je zapísaný v súbore
zf-tutorial/application/controllers/IndexController.php a jeho obsah je:

zf-tutorial/application/controllers/IndexController.php

<?php
  class IndexController extends Zend_Controller_Action
  {
    function indexAction()
    {
    }
    function addAction()
    {
    }
    function editAction()
    {
    }
    function deleteAction()
    {
    }
  }

Máme zostavené naše akcie. Zatiaľ nepracujú, pokým nezostavíme view. URL budú mať teda takýto vplyv:

            URL                                        Akcia
-------------------------------------------------------------------------------
<a href="http://localhost/zf-tutorial/" title="http://localhost/zf-tutorial/">http://localhost/zf-tutorial/</a>                IndexController::indexAction()
<a href="http://localhost/zf-tutorial/index/add" title="http://localhost/zf-tutorial/index/add">http://localhost/zf-tutorial/index/add</a>      IndexController::addAction()
<a href="http://localhost/zf-tutorial/index/edit" title="http://localhost/zf-tutorial/index/edit">http://localhost/zf-tutorial/index/edit</a>      IndexController::editAction()
<a href="http://localhost/zf-tutorial/index/delete" title="http://localhost/zf-tutorial/index/delete">http://localhost/zf-tutorial/index/delete</a>    IndexController::deleteAction()

Takto teda funguje routovanie pre každú stránku v našej aplikácii.

Zostavenie view
Komponent view sa Zend Frameworku nazýva Zend_View. Tento komponent nám umožňuje oddeliť kód, ktorý zobrazuje stránku od kódu ostatných funkcií.

Základné použitie Zend_View je:

$view = new Zend_View();
$view->setScriptPath('/path/to/view_files');
echo $view->render('view.php');

Veľmi ľahko môžete vidieť, že ak by sme takúto štruktúru vkladali priamo do našich akcií, skončili by sme s veľmi otravným, opakujúcim sa kódom, ktorý nemá žiaden súvis s našimi akciami. Namiesto toho je vhodnejšie view inicializovať na inom mieste a pristupovať k už nakonfigurovanému view objektu v rámci každej akcie (funkcie akcie).

Návrhári Zend Frameworku predvídali tento druh problému a riešenie je uložené v tzv. akčnom helperi. Zend_Controller_Action_Helper_ViewRenderer sa stará o inicializáciu view člena ($this->view) a tiež vykreslí view skript. Pre vykresľovanie sa Zend_View objekt nastaví tak, aby skripty hľadal v umiestnení views/scripts/{názov controlleru} a defaultne hľadal skript nazvaný podľa akcie s koncovkou phtml. Takže plná cesta k skriptu má formu views/scripts/{názov controlleru}/{názov_akcie}.phtml a vyrenderovaný obsah sa uloží do tela objektu Response. Objekt Response sa používa na to aby zhromažďoval všetky http hlavičky, telo dokumentu a generované výnimky ako výsledok práce MVC systému. Front controller potom automaticky pošle hlavičky nasledované telom dokumentu na konci tzv. dispatchu (posledný príkaz v bootstrapperi).

Pre integrovanie view do našej aplikácie je potrebné vytvoriť nejaké view súbory s vykresľovacím kódom a pridať nejaké príkazy do akcií controllera.

Zmeny v IdexController (zmeny sú označené komentátom // nové):

zf-tutorial/application/controllers/IndexController.php

<?php
  class IndexController extends Zend_Controller_Action
  {
    function indexAction()
    {
      $this->view->title = "My Albums";  // nové
    }
    function addAction()
    {
      $this->view->title = "Add New Album";  // nové
    }
    function editAction()
    {
      $this->view->title = "Edit Album";  // nové
    }
    function deleteAction()
    {
      $this->view->title = "Delete Album";  // nové
    }
  }

V každej funkcii priradíme premennú, ktorá ponesie titulok do člena view. Zobrazovanie sa však nevykonáva na tomto mieste – to je vykonávané front controllerom na konci dispatch procesu.

Teraz potrebujeme pridať do našej aplikácie 4 view súbory. Tieto súbory sa označujú ako šablony (template) a metóda render() očakáva, že sa súbory nazývajú rovnako ako akcia s príponou .phtml. Súbor musí byť umiestnený s adresári s rovnakým názvom ako controller. Tu je obsah týchto súborov:

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>

Môžete otestovať každú akciu z index controllera pomocou hore uvedených URL. Výsledkom by mala byť stránka so zobrazeným tučným titulkom.

Spoločný HTML kód
Veľmi rýchlo zistíte, že veľká časť HTML kódu sa v našich view opakuje. Tento spoločný kód si umiestnime do dvoch všeobecných súborov: header.phtml a footer.phtml v rámci skript adresára. Využijeme ich aby obsahovali spoločný HTML kód a z view šablóny sa na ne iba odkážeme. Tu je obsah týchto súborov:

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>

Okrem iného budú naše stránky teraz v súlade s XHTML normou! Najskôr však musíme vykonať zmeny v našich view šablónach. Zapíšte do nich tento nový kód (pôvodný prepíšte).

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'); ?>

Úprava CSS
Aj keď je toto iba jednoduchý tutoriál, vytvoríme si CSS štýlopis aby naša aplikácia vypadala viac k svetu. To prináša nový problém, lebo nevieme ako sa odkázať na CCS súbor, kvôli tomu že URL neukazuje na správny root adresár. Na vyriešenie tohto problému použijeme funkciu getBaseUrl(), ktorá je súčasťou requestu a posunieme ju do view. To nám poskytne časť URL, ktorú nepoznáme.

Použijeme funkciu IndexController::init() pre tento kód, nakoľko funkcia init() je volaná konštruktorom a tak je tento člen dostupný vo všetkých akciách:

zf-tutorial/application/controllers/IndexController.php

...
class IndexController extends Zend_Controller_Action
{
  function init()
  {
    $this->view->baseUrl = $this->_request->getBaseUrl();
  }
  function indexAction()
  {
...

Okrem toho je potrebné pridať cestu k CSS súboru do hlavičky v header.phtml:

zf-tutorial/application/views/scripts/header.phtml

...
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <title><?php echo $this->escape($this->title); ?></title>
  <link rel="stylesheet" type="text/css" media="screen"
  href="<?php echo $this->baseUrl;?>/public/styles/site.css" />
</head>
...

Nakoniec si vytvoríme nejaké CSS štýly

zf-tutorial/public/styles/site.css

body,html {
  font-size:100%;
  margin: 0;
  font-family: Verdana,Arial,Helvetica,sans-serif;
  color: #000;
  background-color: #fff;
}
h1 {
  font-size:1.4em;
  color: #800000;
  background-color: transparent;
}
#content {
  width: 770px;
  margin: 0 auto;
}
label {
  width: 100px;
  display: block;
  float: left;
}
#formbutton {
  margin-left: 100px;
}
a {
  color: #800000;
}

Stránka by mala teraz vypadať o niečo krajšie.

Databáza
Teraz keď už máme oddelenú logiku aplikácie od vykresľovania, je čas sa pozrieť na komponent model v našej aplikácii. Pamätáte – model je tá časť, ktorá má dočinenia s jadrom aplikácie (nazývaným tiež biznis logika) a konkrétne v našom prípade sa jedná o prácu s databázou. Použijeme triedu Zend_Db_Table zo Zend Frameworku, ktorá sa používa na vyhľadávanie, vkladanie, mazanie a aktualizovanie riadkov v databázovej tabuľke.

Konfigurácia

Ak chceme používať triedu Zend_Db_Table, musíme jej povedať, ktorú databázu má používať spolu s používateľským menom a heslom. Nakoľko nepreferujeme tieto údaje natvrdo zapísať do aplikácie, použijeme konfiguračný súbor, ktorom budú tieto údaje zapísané.

Zend Framework poskytuje triedu Zend_Config, ktorá poskytuje flexibilný, objektovo orientovaný prístup ku konfiguračným súborom. Konfigurácia môže byť uložená vo formáte XML alebo INI. My použijeme formát INI:

zf-tutorial/application/config.ini

[general]
db.adapter        = PDO_MYSQL
db.config.host    = localhost
db.config.username = php
db.config.password = 123456
db.config.dbname  = zftest

Samozrejme konfiguráciu si prispôsobte podľa vašich aktuálnych podmienok.

Použitie triedy Zend_Config je veľmi jednoduché:
$config = new Zend_Config_Ini('config.ini', 'section');

Všimnite si, že v tomto prípade Zend_Config načíta jednu sekciu z INI súboru, nie všetky sekcie (ale všetky sekcie je možné načítať na želanie). Podporuje notáciu v názve sekcie aby umožňovala načítavanie dodatočných sekcií. Trieda Zend_Config_Ini okrem toho považuje bodku v názvoch parametrov za hierarchický oddeľovač, aby ste tak mohli zoskupovať podobné konfiguračné parametre. V našom súbore config.ini budú parametre host, usermane, password a dbname parametre zoskupené do $config->db->config.

Konfiguračný súbor načítame v bootstraperi (index.php):

Odpovedjúca časť zf-tutorial/index.php

...
Zend_Loader::loadClass('Zend_Controller_Front');
Zend_Loader::loadClass('Zend_Config_Ini');
Zend_Loader::loadClass('Zend_Registry');
// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
// setup controller
...

Načítame si triedy, ktoré budeme potrebovať (Zend_Config_Ini a Zend_Registry) a potom načítame sekciu general zo súboru application/config.ini do našeho objektu $config. Nakoniec objekt $config priradíme do registra, takže ku nemu môžeme pristupovať z ľubovoľného miesta aplikácie.

Pozn.: V tomto tutoriáli nie je potrebné odkladať $config do registra, ale je to dobrý zvyk v skutočných aplikáciách, pretože do registra nebudete odkladať iba konfiguráciu databázy, ale aj iné potrebné veci. Tiež si dajte pozor pri používaní registra, pretože ten vystupuje podobne ako globálne pole a spôsobuje závislosti medzi objektmi, ktoré by nemali na sebe závisieť, ak nie ste opatrní.

Konfigurácia Zend_Db_Table
Pre využívanie triedy Zend_Db_Table jej musíme najskôr poskytnúť databázovú konfiguráciu, ktorú sme práve načítali. Aby sme to vykonali, musíme vytvoriť inštanciu Zend_Db a tú potom zaregistrovať pomocou statickej funkcie Zend_Db_Table::setDefaultAdapter(). Toto opäť vykonáme v bootstraperi:

Odpovedjúca časť zf-tutorial/index.php

...
Zend_Loader::loadClass('Zend_Controller_Front');
Zend_Loader::loadClass('Zend_Config_Ini');
Zend_Loader::loadClass('Zend_Registry');
Zend_Loader::loadClass('Zend_Db');
Zend_Loader::loadClass('Zend_Db_Table');
// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
// setup database
$db = Zend_Db::factory($config->db->adapter, $config->db->config->toArray());
Zend_Db_Table::setDefaultAdapter($db);
// setup controller
...

Vytvoríme tabuľku
Používame MySQL a SQL príkaz na vytvorenie tabuľky je:

CREATE TABLE album (
  id      int(11)        NOT NULL auto_increment,
  artist  varchar(100NOT NULL,
  title    varchar(100NOT NULL,
  PRIMARY KEY (id)
);

Vykonajte tento príkaz pomocou niektorého MySQL klienta, ako je napr. phpMyAdmin alebo klient pre príkazový riadok.

Vložíme testovacie albumy
Pridáme aj niekoľko riadkov záznamov do novej tabuľky, aby sme hneď mohli otestovať výberové funkcie z databázy na domovskej stránke. Ja som sa rozhodol pridať prvé dva albumy z rebríčka HOT 100 na Amazon.co.uk:

INSERT INTO album (artist, title)
VALUES
  ('James Morrison', 'Undiscovered'),
  ('Snow Patrol', 'Eyes Open');

</prelozeny-text>

V tomto mieste tutoriál opäť prerušíme (z rovnakých dôvodov) a dokončíme ho v poslednom, treťom diely.