Zend Framework i podświetlanie wybranego elementu menu

W życiu każdego progra­mi­sty nadcho­dzi chwila, od której staje się leniwy (czytaj: mądrzej­szy) i zaczyna korzy­stać z goto­wych, wydaj­niej­szych rozwią­zań w postaci frame­wor­ków. Do tej pory progra­mi­sta tworzył w pocie czoła swoje „klocki”, z których mógł sobie skła­dać apli­ka­cje. W lite­ra­tu­rze zwie się takie coś „reinven­ting the wheel”, czyli po naszemu trace­nie czasu na klepa­nie kodu, który ktoś napi­sał za nas. Śpie­chu niniej­szym prze­cho­dzi na następny level i zaczyna pozna­wać Zend Frame­work.

Nie ma niestety dobrej lite­ra­tury w języku polskim do Zenda. Przy okazji czeka­nia na pociąg zaha­czy­łem o Empik i kupi­łem sobie PHP5. Progra­mo­wa­nie z wyko­rzy­sta­niem Symfony, Cake­PHP, Zend Frame­work Tomasz Skara­czyń­skiego i Andrzeja Zoła. 2/3 książki zajmuje niestety opis Symfony. Myślę jednak, że te kilka­dzie­siąt stron na start z Zendem wystar­czy. Potem wgry­zamy się w doku­men­ta­cję na stro­nie producenta.

W skró­cie: Zend Frame­work stosuje wzorzec projek­towy Model-Widok-Kontroler (MVC). Na świat wysta­wiony mamy jedy­nie plik index.php. Router rozpra­co­wuje żąda­nie wyświe­tle­nia strony i kieruje do odpo­wied­niego kontro­lera i jego akcji. Jeden kontro­ler może mieć wiele akcji, z których każda może robić co innego. Dla każdego kontro­lera przy­pi­sany jest osobny kata­log z wido­kami, z których gene­ro­wana jest osta­teczna strona. Kontro­ler w razie potrzeby odpala również jakieś zapy­ta­nia do bazy danych (model) i prze­ka­zuje wyniki do widoku.

Chcemy uzyskać taki efekt, że po klik­nię­ciu konkret­nego elementu menu ten nam się podświe­tli. I to tak żeby cała opera­cja odby­wała się bez inge­ren­cji progra­mi­sty piszą­cego kolejne akcje. Samego startu z Zendem nie będę omawiał. Zapy­taj­cie wujka Google o Zend_Layout i o rozsze­rza­nie Zend_Controller_Plugin_Abstract.

Pierw­sze czego potrze­bu­jemy to plik layout.phtmlapplication/layouts/scripts. Jest super prosty i zawiera 2 place­hol­dery — 1 na menu i 1 na treść z akcji.

<html><head></head>
<body>
   <div id="menu">
      <?php echo $this->layout()->menu; ?>
   </div>
   <div id="content">
      <?php echo $this->layout()->content; ?>
   </div>
</body>
</html>

Następ­nie potrze­bu­jemy kontro­lera, który będzie zajmo­wał się tylko wyświe­tla­niem menu. Tworzymy plik MenuController.phpapplication/controllers.

class MenuController extends Zend_Controller_Action {
   public function init() {
      $this->_helper->viewRenderer->setResponseSegment('menu');
   }
   public function showmenuAction() {
      $menuItems = array(
         array('tytul' => 'pierwszy link',
            'kontroler' => 'index',
            'akcja' => 'pierwszy'),
         array('tytul' => 'drugi link',
            'kontroler' => 'index',
            'akcja' => 'drugi'),
         array('tytul' => 'trzeci link',
            'kontroler' => 'index',
            'akcja' => 'trzeci')
      );
 
      $this->view->assign('menu', $menuItems);
      $front = Zend_Controller_Front::getInstance();
      $this->view->assign('highlight_action', $front->getPlugin('Spiechu_Menu_Plugin_Menu')->getActionName());
      $this->view->assign('highlight_controller', $front->getPlugin('Spiechu_Menu_Plugin_Menu')->getControllerName());
   }
}

Powyż­szy kod usta­wia place­hol­der na menu (content jest domyślny), następ­nie prze­ka­zuje zmienne potrzebne do wyświe­tle­nia widoku.

Teraz trzeba by jakoś wyświe­tlić te elementy menu. Tworzymy plik widoku odpo­wia­da­jący nazwie akcji. W naszym przy­padku jest to showmenu.phtmlviews/scripts/menu.

<ul>
 <?php foreach ($this->menu as $menuItem): ?>
  <?php if ($this->highlight_action == $menuItem['akcja'] && $this->highlight_controller == $menuItem['kontroler']): ?>
   <li><a style="background-color:green;" "href="<?php echo $this->url(array('controller' => $menuItem['kontroler'] , 'action' => $menuItem['akcja'])); ?>"><?php echo $menuItem['tytul'] ?></a></li>
  <?php else: ?>
   <li><a href="<?php echo $this->url(array('controller' => $menuItem['kontroler'] , 'action' => $menuItem['akcja'])); ?>"><?php echo $menuItem['tytul'] ?></a></li>
  <?php endif; ?>
 <?php endforeach; ?>
</ul>

Powyż­sza hybryda php i html iteruje nam po wszyst­kich elemen­tach tablicy $this->menu. Jeżeli aktu­al­nie itero­wany element odpo­wiada temu do podświe­tle­nia to wkle­jamy mu styl background-color podświe­tla­jący na zielono. Warty uwagi jest również helper widoku $this->url(), który montuje nam prawi­dłowy adres do danego elementu menu.

Zapewne zwró­ci­li­ście uwagę na takie dziwne coś jak Spiechu_Menu_Plugin_Menu. Jest to mój plugin podcze­pia­jący się do kontro­lera fron­to­wego i pobie­ra­ją­cego z niego dane na temat żąda­nego kontro­lera i akcji. A czemu taka dziwna nazwa? Zend rozpra­co­wuje sobie gdzie ma szukać danej klasy w struk­tu­rze kata­lo­gów za pomocą nazw poprze­dzie­la­nych podkreślnikiem.

Tworzymy kod pluginu w library/Spiechu/Menu/Plugin/Menu.php

class Spiechu_Menu_Plugin_Menu extends Zend_Controller_Plugin_Abstract {
 protected $_actionName;
 protected $_controllerName;
 
 public function routeShutdown(Zend_Controller_Request_Abstract $request) {
  $this->_actionName = $request->getActionName();
  $this->_controllerName = $request->getControllerName();
  $menu = clone $request;
  $menu->setControllerName('menu')->setActionName('showmenu');
  $frontController = Zend_Controller_Front::getInstance();
  if (!$frontController->hasPlugin('Zend_Controller_Plugin_ActionStack')) $frontController->registerPlugin(new Zend_Controller_Plugin_ActionStack());
  $actionStack = $frontController->getPlugin('Zend_Controller_Plugin_ActionStack');
  $actionStack->pushStack($menu);
 }
 
 public function getActionName() {
  return $this->_actionName;
 }
 
 public function getControllerName() {
  return $this->_controllerName;
 }
}

Macie tutaj sporo niezro­zu­mia­łego kodu. Dla każdego pluginu po zakoń­cze­niu procesu routingu, a przed rozpo­czę­ciem pętli wyko­ny­wa­nia akcji odpa­lana jest m.in. metoda route­Shut­down(). Używamy jej do zapisu danych nt. żąda­nego kontro­lera i akcji, a następ­nie wrzu­camy na stos (Zend_Controller_Plugin_ActionStack) dodat­kową akcję do wyko­na­nia, która wyświe­tli nam menu. Jest tutaj użyty numer z klono­wa­niem, który gdzieś znala­złem na forum. Potrze­bu­jemy jakie­goś żąda­nia, które można prze­flan­co­wać na wyświe­tle­nie menu. Ponadto nie możemy popsuć tego właści­wego, więc używamy klonowania.

Ostat­nie czego potrze­bu­jemy to zare­je­stro­wać plugin, gdyż Zend nie wie czy chcemy go używać czy nie. Najle­piej to zrobić w pliku application/bootstrap.php. U mnie wygląda to tak:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 public function __construct($application) {
  parent::__construct($application);
 
  Zend_Loader_Autoloader::getInstance()->registerNamespace('Spiechu_');
  $frontController = Zend_Controller_Front::getInstance();
  $frontController->registerPlugin(new Spiechu_Menu_Plugin_Menu());
 }
}

Najpierw reje­stru­jemy nową ścieżkę, a następ­nie reje­stru­jemy plugin. Jak zapewne zauwa­ży­li­ście, Zend_Loader_Autoloader jak i Zend_Controller_Front to single­tony. Mamy przez to pewność, że gdy w kontro­le­rze chcemy wycią­gnąć dane z naszego pluginu, to jest to ta sama instancja.

Na razie wszystko to jest dla mnie dosyć nowe. Sam nie do końca czuję czemu to nie wyrzuca jakie­goś błędu. :-)

Myślę, że ten wpis to dopiero począ­tek pisa­nia o Zendzie.

PS.: Nie zapo­mnij­cie sobie stwo­rzyć akcji pierw­szy­Ac­tion(), drugiAc­tion(), trze­ciAc­tion() w kontro­le­rze Inde­xCon­trol­ler (może wyświe­tlać tylko 1, 2, 3 — ważne, żeby można było poznać czy rzeczy­wi­ście podświe­tla nam ten element, który wybraliśmy).

Podobne wpisy:

  1. Łącze­nie zapy­tań Zend_Db_Select w Zend Frame­work [cz. 2/2]
  2. Własny wali­da­tor w Zend Framework
  3. Zend_Date i Zend_Config w Zend Framework
  4. Łącze­nie zapy­tań Zend_Db_Select w Zend Frame­work [cz. 1/2]

One comment

  • 15 lipca 2010 - 03:04 | Permalink

    Dzięki wiel­kie, bardzo się przydało ! :)

  • Dodaj komentarz

    Twój adres e-mail nie zostanie opublikowany.

    Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <p> <pre lang="" line="" escaped=""> <q cite=""> <strike> <strong>