Modules in Zend Framework
Momenteel ben ik bezig met het maken van een website met daarbij een op maat gemaakt CMS. Dit alles wordt gemaakt met behulp van Zend Framework (ZF). De website, een nieuwsportaal, heeft een frontend (de website zelf) en een backend. Allemaal geen probleem natuurlijk, maar het mooiste zou zijn om de mappenstructuur ook daadwerkelijk zo te maken, dat de frontend van de backend gescheiden is. Dit is heel goed te doen in Zend Framework. In mijn geval wil ik dat alle artikelen op de website als volgt worden weergeven: “www.website.ext/naam-van-het-artikel”.
Daarnaast wil ik de backend als “www.website.ext/admin/controller/action”. Bijvoorbeeld: “www.website.ext/admin/nieuws/overzicht”.
In dit artikel ga ik uitleggen hoe je dit met behulp van routes kunt bewerkstelligen.
Let op: dit is geen leer-zend-framework-in-5-minuten cursus, maar meer een artikel voor iemand die al met ZF gewerkt heeft. Voor een beginnerscursus raad ik Rob Allen’s “Getting started with Zend Framework” aan, of het boek: php|architects Guide to Programming with Zend Framework
Directory structuur

Directory Structuur
Om dit alles voor elkaar te krijgen gaan we werken met zogenaamde routes en een andere directory structuur dan je gewent bent. De uiteindelijke directory structuur zal op de screenshot (links) lijken.
De volgende mappen zijn te zien:
Application – al mijn custom made serverside scripts
config – ini-config bestand(en)
controllers – controllers voor de frontend
layouts – templates die ik voor de front- en backend gebruik
models – models voor zowel front en backend
modules/admin/controllers – in modules is één map te zien: admin. In admin staan backend controllers en views.
views - views voor de frontend
html – al mijn afbeeldingen, javascript en stylesheets
library – bevat het zend framework
De backend
Allereerst gaan we ervoor zorgen dat de frontend van de backend is gescheiden. Daarvoor moet eerst de route class worden geladen in je bootstrap:
Zend_Loader::loadClass('Zend_Controller_Router_Route');
De Zend_Controller_Router_Route class zorgt er onder andere voor dat je “/controller/action” aankomt bij de bijbehorende controller en action. Om “/admin/nieuws/overzicht” te laten werken moet de standaard route worden aangepast en moeten de controller directories handmatig worden aangegeven. Standaard wordt de map “/application/controllers” gebruikt voor de controllers. Wij willen die map echter alleen voor onze frontend gebruiken. De backend willen we in een aparte map opslaan en ook aanroepen in een aparte map, namelijk “www.website.ext/admin”.
Dit doen we door de backend als module aan te maken. Daarvoor hebben we in de map “application” een map “modules” aangemaakt. Daarin maken we één module aan, te weten “admin”. Daarin maak je weer “controllers” en “views” directories aan. Deze modules worden niet automatisch opgepikt, maar moeten worden toegevoegd aan je front controller. Dat doe je door de volgende code in je bootstrap te plaatsen, net voordat de dispatch() method aanroept op je front controller.
// setup controller $front = Zend_Controller_Front::getInstance(); $front->setControllerDirectory(array( 'default' => './application/controllers', 'admin' => './application/modules/admin/controllers' ));
Allereerst wordt front controller aangemaakt en in de variabele $front geplaatst. Vervolgens worden er twee controller directories doorgegeven aan de front controller: “controllers” (voor de frontend) en “modules/admin/controllers” voor de backend.
De frontend
Om er voor te zorgen dat “www.website.ext/naam-van-het-artikel” ook daadwerkelijk bij een artikel uitkomt moeten pagina’s zonder controller-naam in de url worden doorgestuurd naar één en dezelfde controller (ik gebruik de index-controller).
Dit doen we door de _call method aan te passen. In je /application/controllers/IndexController.php bestand voeg je een nieuwe functie toe, namelijk:
function __call($method, $args) {
$this->_forward('index', 'index', 'default');
}
In de code hierboven worden alle calls naar de IndexController geforward naar de indexAction van de controller index van de default-module (er wordt dus maar 1 action gebruikt van de indexController).
Artikelen en/of pagina’s aanroepen
Nu alle calls, behalve calls met daarin ‘/admin/’, naar de indexAction van de IndexController worden gestuurd, kunnen we daar iets mee gaan doen. Laten we er gemakshalve van uitgaan dat alle calls een alias bevatten van een pagina in een database. De database zou er zo uit kunnen zien:
tabel: articles
id int auto_increment
title varchar
alias varchar
content varchar
Stel dat er in deze tabel een record staat met als titel ‘Zend Framework tutorial. De alias zou vervolgens ‘zend-framework-tutorial’ kunnen zijn. Dit artikel zou vervolgens als volgt worden aangeroepen op de site: ‘www.website.ext/zend-framework-tutorial’.
We moeten dus de call gaan vergelijken met de alias van een artikel uit de database. Als die twee matchen, kan er vervolgens een artikel uit de database worden gehaald en weergegeven. Dat wordt gedaan met de volgende code (commentaar is toegevoegd):
public function indexAction() {
// url van de huidige pagina ophalen
// de inhoud achter de laatste slash in $alias plaatsen
$url = $this->_request->getRequestUri();
$url = split('/', $url);
$alias = end($url);
// deze switch kun je in principe weglaten, maar is wel handig
//
// Admin
// controlleren of 'admin' wordt aangevraagd
// zo ja doorsturen naar admin module
//
// homepage controller
// is er geen alias? dan wordt de homeAction() aangeroepen
switch($alias):
case 'admin':
$this->_redirect('admin/account/login');
break;
case '':
$this->_forward('home', 'index', 'default');
break;
endswitch;
// als de $alias niet leeg is kan er op een alias worden gecontrolleerd
// dit gebeurt door de article-class aan te roepen (=articles tabel)
if($alias !== '') {
Zend_Loader::loadClass('Zend_Filter_StripTags');
$filter = new Zend_Filter_StripTags();
$alias = $filter->filter($alias);
Zend_Loader::loadClass('Article');
$article = new Article();
$article = $article->fetchRow("alias = '" . $alias . "'");
// als deze query een artikel oplevert kan deze worden weergegeven
if(count($article) == 1) {
$this->view->article = $article;
} else {
// geen artikel gevonden? 404 pagina weergeven (in dit geval error404)
$this->_forward('error404', 'index', 'default');
}
}
}
And that’s it. Als er geen artikel in de database gevonden wordt, komt de gebruiker uit op een 404 pagina. Wordt er wel een pagina gevonden, dan wordt de data van dat artikel naar de view gestuurd, waar je deze weer kunt weergeven. Veel succes ermee en vragen kun je natuurlijk altijd hieronder kwijt.
Heel heldere uitleg, bedankt! Hoe kan ik daarnaast in de frontend nog een andere controller dan IndexController gebruiken, bijvoorbeeld blogController.php of forumController.php?
Hallo Joost,
Ik heb het nogal druk momenteel, anders had ik dit allemaal voor je uitgezocht ;)
Maar misschien heb je hier wat aan: http://www.spotsec.com/blogs/archive/2007/9/27/creating-custom-routes-with-zend-framework/
geweldig, net wat ik zocht
Super! Eindelijk eens een heldere duidelijke uitleg waarin verteld wordt hoe je modules maakt! Na lang zoeken is deze verhelderende uitleg echt een verademing! Bedankt!