Zend Framework Tutorial Teil 5: MVC Design Pattern und Zend_View
Im vierten Teil vom Zend Framework Tutorial haben wir die Zend_Db Komponente und deren Teilkomponenten kennen gelernt, um auf einen Datenbank zugreifen zu können. Die Ausgabe haben wir aber nur unformatiert auf dem Bildschirm ausgegeben, da wir bisher noch keine Templates eingesetzt hatten. Wenn du die ersten vier Teile noch nicht gelesen hast, hole dies bitte schnell nach.
In diesem fünften Teil des Tutorials werden wir uns um die View Komponente unseres Projektes kümmern. Nach einer kurzen Einführung in Zend_View werden wir die Templates für die im letzten Teil bereits erstellten Aktionsmethoden für die Ausgabe und das Ändern der Daten erstellen. Dabei werden wir auch die grundlegende Interaktion zwischen Controller und View sowie einige Hilfsmethoden für die Ausgabe kennen lernen.
Wenn du über neue Tutorial Teile informiert werden möchtest, abonniere am besten den Feed dieses Blogs. Dann verpasst du garantiert keinen Teil des Tutorials. Es gibt auch Übersicht mit den bisher erschienen Teilen des Tutorials.
Wichtig: Dieses Tutorial setzt die Zend Framework Preview Version 0.6.0 voraus und kann bei der Verwendung einer aktuelleren Version aus dem SVN unter Umständen nicht zu 100% funktionieren.
Inhaltsverzeichnis
- Zend_View
- Template für Artikelliste
- Aktionsmethode für Artikelliste anpassen
- Zend_Controller_RewriteRouter und Zend::register()
- Artikelliste erweitern
- Einen Artikel anzeigen
- Aufräumarbeiten
- Haupttemplate einrichten
- Ein klein wenig Design
- Übung: die weiteren Views
- Download
- Zusammenfassung
- Change log
- Navigation
- Kommentare
Zend_View
Als Einführung in die Zend_View Komponente empfehle ich dir, erst einmal einen kurzen Blick in das Zend_View Kapitel im Manual zu werfen. Dort erfährst du schon ein wenig über die Funktionsweise von Zend_View.
Die Zend_View Komponente ist keine klassische Template Engine und verwendet keine eigene Template Syntax wie dies z.B. bei Smarty, patTemplate oder PEAR::HTML_Template_IT der Fall ist. Stattdessen verwendet Zend_View einfach PHP als Templatesprache, d.h. in unseren Templates verwenden wir eine Mischung aus HTML und PHP Code.
Zend_View ist jedoch so konzeptioniert, dass du auch jede beliebige Template Engine für die Ausgabe verwenden kannst. Im weiteren Verlauf des Tutorials werde ich einige Beispiele aufzeigen, wie du auch andere Template Engines in das Zend Framework integrieren kannst. Zudem wird das Thema auch immer mal wieder in der Zend Framework Mailingliste diskutiert und es wurde bereits angedacht, eine Komponente zu entwickeln, welche Templates mit Smarty ähnlicher Syntax in PHP Skripte übersetzt, die dann einfach von Zend_View verwendet werden können.
Template für Artikelliste
Wir beginnen mit der Anzeige unserer Artikelliste und benötigen dafür natürlich ein erstes Template. Dazu erstelle bitte im Verzeichnis "/travelloblog/application/views" das Unterverzeichnis "article" und erstelle dort eine Datei mit Namen "index.php". Dies hat den Vorteil, dass wir die Templates in Unterverzeichnissen verwalten können. Im Prinzip steht das "article" für den Namen des Controllers und das "index" für den Namen der Aktionsmethode.
In der Template Datei "index.php" füge bitte folgenden Code ein:
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
</head>
-
<?php foreach($this->articles as $article) : ?>
-
<?php endforeach; ?>
-
</body>
-
</html>
Dieses PHP Skript enthält neben dem HTML Code auch die PHP Anweisungen für die Ausgabe der Template Variablen. Die foreach() Schleife ist in der alternativen Syntax für Kontrollstrukturen notiert (siehe dazu das PHP Manual). Hierbei wird die öffnende Klammer hinter "foreach()" durch einen Doppelpunkt und die schließende Klammer durch ein "endforeach" ersetzt. Du kannst aber natürlich auch die gewohnte Syntax mit geschweiften Klammern verwenden, wenn dir das besser gefällt.
In der Variable $this wird eine Instanz von Zend_View erwartet. Somit stehen uns alle übergebenen Templatevariablen und einige Hilfsmethoden für die Ausgabe zur Verfügung. In $this->title wird somit der Titel der Seite vermutet. Im Prinzip wird nur eine einfache foreach() Schleife durchlaufen und die Werte aus dem Array $this->articles einzeln ausgegeben. Bei der Ausgabe wird für jede Variable die Hilfsmethode escape() für die Maskierung der Ausgabe verwendet. Die Spaltennamen "art_title" oder "art_teaser" sind dir sicher noch aus der Definition unsere MySQL Tabelle "article" bekannt.
Unser erstes View Template ist somit erst einmal fertig. Jetzt möchtest du es sicher in Aktion erleben, also lies gleich weiter.
Aktionsmethode für Artikelliste anpassen
Um nun das Template auch verwenden zu können, öffne bitte die Datei "ArticleController.php" im Verzeichnis "/travelloblog/application/controllers" und ändere die Methode indexAction wie folgt ab:
-
class ArticleController extends Zend_Controller_Action
-
{
-
public function indexAction()
-
{
-
$article = new ArticleModel();
-
-
$order = 'art_cdate DESC';
-
$rowset = $article->fetchAll(null, $order);
-
-
$data = $rowset->toArray();
-
-
$view = new Zend_View();
-
$view->setScriptPath('../application/views');
-
-
$view->title = 'TravelloBlog - List of articles';
-
$view->articles = $data;
-
-
$this->getResponse()->setBody($view->render('article/index.php'));
-
}
-
-
[...]
-
}
Zuerst holen wir uns wieder mit Hilfe unserer Model Klasse die Liste alle Artikel, absteigend sortiert nach dem Datum der Erstellung. Danach wird ein Objekt der Klasse Zend_View instanziert und der Pfad gesetzt, unter dem unsere View Templates zu finden sind. Als nächstes übergeben wir den Seitentitel und das komplette Array mit den Artikeldaten.
Du fragst dich nun sicherlich gerade, was die letzte Zeile zu bedeuten hat. Mit Release 0.6.0 wurde das sogenannte Response Objekt eingeführt. Dies dient als Container für Inhalte und Header, um sie dann in einem Rutsch ausgeben zu können, statt in der Applikation verteilt Ausgaben zu machen. Durch den Aufruf der setBody() Methode wird das Ergebnis der Verarbeitung des Templates als Inhalt für dieses Response Objekt gesetzt. Mit der Methode appendBody() kann man den Inhalt auch schrittweise aufbauen und mit der Methode setHeader() kannst du auch beliebige Header setzen, die dann erst bei der Ausgabe zusammen in einem Rutsch gesendet werden. Weitere Infos zum Response Objekt findest du im Manual.
Um eine Templatevariable zu übergeben, reicht es also aus, einfach eine Eigenschaft der Zend_View Instanz $view mit dem gewünschten Wert zu befüllen. Alternativ kannst du aber auch die assign() Methode verwenden. Dies sähe dann wie folgt aus:
-
$view->assign('title', 'TravelloBlog - List of articles');
-
$view->assign('articles', $data);
Es ist sicher Geschmackssache, wie man die Templatevariablen übergibt. Ich werde in Zukunft die assign() Methode verwenden, weil ich diesen Weg bevorzuge. Du kannst aber auch gerne den anderen Weg gehen, wenn er dir besser gefällt.
Um zu sehen, was nun passiert, rufe bitte die Adresse "http://travelloblog/article" in deinem Browser auf. Die Ausgabe sollte dann in etwa wie folgt aussehen:

Ist zwar noch nicht besonders hübsch, aber es erfüllt erst einmal seinen Zweck. Jetzt möchtest du sicher als nächstes einen Artikel anzeigen lassen. Bevor wir das tun können, müssen wir jedoch einen Umweg um drei Ecken gehen, der uns dann aber zum Ziel führen wird.
Zend_Controller_RewriteRouter und Zend::register()
Um einen einzelnen Artikel anzuzeigen, ändern wir am besten unser Template mit der Artikelliste und fügen dort die Links zu den Artikeln hinzu. Hierbei gibt es jedoch ein kleines Problem.
Und zwar ist es wichtig, für alle Links die gleiche Basis URL zu verwenden, damit unser TravelloBlog auch läuft, wenn du das Projekt in einem Unterverzeichnis laufen hast. Um sich nicht zu verzetteln macht es Sinn, alle internen Links absolut anzugeben. Da wir unser Projekt sowohl in über die Adresse "http://localhost/travelloblog/public/" als auch über "http://travelloblog/" aufrufen können, müssen wir also jeweils die Basis URL herausfinden. Im ersten Fall wäre dies "/travelloblog/public" und im zweiten "". Doch woher nehmen wir die Basis URL?
Vielleicht erinnerst du dich noch an die Komponente Zend_Controller_RewriteRouter, die wir im dritten Teil des Tutorials schon kurz betrachtet haben. Der RewriteRouter stellt genau die Methode getRewriteBase() bereit, die wir nun brauchen. Jetzt stehen wir vor dem nächsten Problem, denn wir können wir im Controller auf eine Instanz des Routers zugreifen?
Auch dafür bietet das Zend Framework wieder eine passende Lösung. Wie ich schon im zweiten Teil des Tutorials kurz erwähnt hatte, bietet die Zend Basisklasse unter anderem auch einen Objektspeicher. In diesem Objektspeichers können wir beliebig viele Objekte ablegen, um dann an anderer Stelle wieder darauf zugreifen zu können. Wie das im Detail funktioniert sehen wir jetzt.
Bitte öffne wieder deine Bootstrap Datei "index.php" im Verzeichnis "/travelloblog/public/" und ändere die letzten Zeilen wie folgt ab:
-
<?php
-
[...]
-
-
$router = new Zend_Controller_RewriteRouter();
-
Zend::register('router', $router);
-
-
$controller = Zend_Controller_Front::getInstance();
-
$controller->setRouter($router);
-
$controller->setControllerDirectory($dir);
-
$controller->dispatch();
-
?>
Wir erstellen also eine Instanz von Zend_Controller_RewriteRouter und legen diese unter dem Namen "router" im Objektspeicher ab. Danach übergeben wir die Instanz direkt an den Front Controller.
Nun ändern wir die indexAction Methode im ArticleController wieder wie folgt:
-
class ArticleController extends Zend_Controller_Action
-
{
-
public function indexAction()
-
{
-
$article = new ArticleModel();
-
-
$order = 'art_cdate DESC';
-
$rowset = $article->fetchAll(null, $order);
-
-
$data = $rowset->toArray();
-
-
$view = new Zend_View();
-
$view->setScriptPath('../application/views');
-
-
$router = Zend::registry('router');
-
$baseurl = $router->getRewriteBase();
-
-
$view->assign('title', 'TravelloBlog - List of articles');
-
$view->assign('articles', $data);
-
$view->assign('baseurl', $baseurl);
-
-
$this->getResponse()->setBody($view->render('article/index.php'));
-
}
-
-
[...]
-
}
Mit Hilfe der Methode Zend::registry() erhalten wir die Instanz des Routers und können damit auf die Methode getRewriteBase() zugreifen und den Wert an die Templatevariable "baseurl" übergeben.
Artikelliste erweitern
Jetzt können wir endlich unser Template mit der Artikelliste ändern und die Links zur Anzeige der Artikel einfügen. Für jeden Artikel erstellen wir eine URL in der Form "article/show/1", wobei statt der "1" die jeweilige Artikel ID verwendet wird.
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
<base href="http://<?php echo $this->escape($_SERVER['HTTP_HOST']) . $this->escape($this->baseurl); ?>/">
-
</head>
-
<body>
-
<h1><?php echo $this->escape($this->title); ?></h1>
-
<?php foreach($this->articles as $article) : ?>
-
<h2><a href="article/show/<?php echo $this->escape($article['art_id']); ?>"><?php echo $this->escape($article['art_title']); ?></a></h2>
-
<?php endforeach; ?>
-
</body>
-
</html>
Wenn du jetzt wieder die Adresse "http://travelloblog/article" in deinem Browser aufrufst, sind die Titel der Artikel verlinkt. Klicke einmal auf einen der Links, um zu sehen, was passiert. Es wird nicht wie erwartet die Methode ArticleController::showAction(), sondern die Methode IndexController::norouteAction() aufgerufen.
Das Problem ist, dass der Router mit den URLs in der Form "article/show/1" noch nichts anfangen kann. Da der nächste Teil des Tutorials sich komplett dem RewriteRouter widmen wird, kommt an dieser Stelle erst einmal nur der PHP Code, um das Problem zu lösen.
Bitte ändere in unserer Bootstrap Datei "index.php" im Verzeichnis "/travelloblog/public/" folgende Zeilen ab:
-
<?php
-
[...]
-
-
-
$router = new Zend_Controller_RewriteRouter();
-
$router->addRoute('myroute', $route1);
-
Zend::register('router', $router);
-
-
[...]
-
?>
Was der Aufruf von addRoute() im Detail bedeutet, werden wir wie gesagt im nächsten Teil des Tutorials sehen. Zum jetzigen Zeitpunkt genügt es, wenn du weißt, dass wir das Problem mit der fehlerhaften Route lösen konnten.
Einen Artikel anzeigen
Für die Anzeige eines Artikels brauchen wir nun ein neues Template. Lege in dem Verzeichnis "/travelloblog/application/views/article" die neue Datei mit Namen "show.php" und folgendem Inhalt an:
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
<base href="http://<?php echo $this->escape($_SERVER['HTTP_HOST']) . $this->escape($this->baseurl); ?>/">
-
</head>
-
<body>
-
<h1><?php echo $this->escape($this->title); ?></h1>
-
<h2><?php echo $this->escape($this->article['art_title']); ?></h2>
-
<p><em><?php echo $this->escape($this->article['art_cdate']); ?></em></p>
-
<p><?php echo $this->escape($this->article['art_text']); ?></p>
-
<hr>
-
<a href="article/">back to list</a>
-
</body>
-
</html>
Im Unterschied zur Artikelliste benötigen wir in diesem Template keine foreach Schleife, sondern können direkt die Daten des Artikels ausgeben. Damit man wieder zurück zur Artikelliste kommt habe ich am Ende der Seite noch einen entsprechenden Link eingefügt.
Als nächstes musst du nun die zugehörige Aktionsmethode anpassen. In unserem "ArticleController.php" ändere bitte die Methode showAction wie folgt ab:
-
class ArticleController extends Zend_Controller_Action
-
{
-
[...]
-
-
public function showAction()
-
{
-
$id = $this->getRequest()->getParam('id');
-
-
$article = new ArticleModel();
-
-
$row = $article->find($id);
-
-
$data = $row->toArray();
-
-
$view = new Zend_View();
-
$view->setScriptPath('../application/views');
-
-
$router = Zend::registry('router');
-
$baseurl = $router->getRewriteBase();
-
-
$view->assign('title', 'TravelloBlog - Show article');
-
$view->assign('article', $data);
-
$view->assign('baseurl', $baseurl);
-
-
$this->getResponse()->setBody($view->render('article/show.php'));
-
}
-
-
[...]
-
}
Zu Beginn benötigen wir die Id für den anzuzeigenden Artikel aus der URL. Dazu verwenden wir das Request Objekt und rufen dort die Methode getParam() auf. Das Request Objekt ist quasi das Gegenstück zum Response Objekt, welches wir weiter oben schon kennen gelernt haben. Weitere Infos zum Request Objekt kannst du auch dem Manual entnehmen.
Diese Artikel Id übergeben wir dann an die find() Methode unserer Artikel Model Klasse, die im Gegensatz zur fetchRow() Methode speziell für die Abfrage eines Datensatzes mit dem Primärschlüssel vorgesehen ist. Den restlichen Code kennen wir vom Prinzip her schon aus der indexAction Methode.
Ein Aufruf der Adresse "http://travelloblog/article/show/1" in deinem Browser sollte dir nun folgende Ausgabe bescheren:

Soweit so gut. Jetzt funktioniert schon einmal die Anzeige unserer Artikelliste sowie die Anzeige eines einzelnen Artikels.
Aufräumarbeiten
Bevor wir weiter machen, wird es wieder Zeit, ein wenig aufzuräumen. Wenn du dir die Methoden indexAction und showAction in unserem ArticleController genauer anschaust, siehst du die Ähnlichkeiten einiger Zeilen. Da lässt sich einiges optimieren.
Auf unserem Umweg vorhin haben wir bereits den Objektspeicher kennen gelernt. Was spricht also dagegen, z.B. auch die Instanz von Zend_View in diesem Objektspeicher abzulegen? Richtig: gar nichts. Also legen wir gleich los.
Bitte öffne wieder unsere Bootstrap Datei "index.php" im Verzeichnis "/travelloblog/public/" und füge folgende Zeilen zwischen der Initialisierung des Routers und der Initialisierung des Controllers ein:
-
<?php
-
[...]
-
-
$view = new Zend_View();
-
$view->setScriptPath('../application/views');
-
-
$baseurl = $router->getRewriteBase();
-
$view->assign('baseurl', $baseurl);
-
-
Zend::register('view', $view);
-
-
[...]
-
?>
Unsere Aktionsmethoden indexAction und showAction im ArticleController können wir folgt abändern:
-
public function indexAction()
-
{
-
$article = new ArticleModel();
-
-
$order = 'art_cdate DESC';
-
$rowset = $article->fetchAll(null, $order);
-
-
$data = $rowset->toArray();
-
-
$view = Zend::registry('view');
-
-
$view->assign('title', 'TravelloBlog - List of articles');
-
$view->assign('articles', $data);
-
-
$this->getResponse()->setBody($view->render('article/index.php'));
-
}
-
-
[...]
-
-
public function showAction()
-
{
-
$id = $this->getRequest()->getParam('id');
-
-
$article = new ArticleModel();
-
-
$row = $article->find($id);
-
-
$data = $row->toArray();
-
-
$view = Zend::registry('view');
-
-
$view->assign('title', 'TravelloBlog - Show article');
-
$view->assign('article', $data);
-
-
$this->getResponse()->setBody($view->render('article/show.php'));
-
}
Die beiden Methoden sind nun deutlich übersichtlicher als vorher, weil wir die Initialisierung von Zend_View an einer zentralen Stelle ausgelagert haben. Das macht das Anpassen der weiteren Aktionsmethoden jedoch deutlich einfacher.
Haupttemplate einrichten
Neben unseren Aktionsmethoden können wir aber auch die Template Dateien ein wenig entschlacken. Da weiter Teil der beiden bisherigen Templates nahezu identisch sind, macht es Sinn, ein Haupttemplate einzurichten, welches dann die individuellen Templates an der richtigen Stelle einbindet.
Lege also in dem Verzeichnis "/travelloblog/application/views" die neue Datei mit Namen "main.php" und folgendem Inhalt an:
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
<base href="http://<?php echo $this->escape($_SERVER['HTTP_HOST']) . $this->escape($this->baseurl); ?>/">
-
<link href="files/css/styles.css" rel="stylesheet" type="text/css">
-
</head>
-
<body>
-
<h1><?php echo $this->escape($this->title); ?></h1>
-
<?php echo $this->render($this->subtemplate); ?>
-
</body>
-
</html>
Mit Hilfe der Methode render() können wir auch innerhalb eines Templates ein weiteres Template verarbeiten. An diese Methode übergeben wir die Templatevariable "subtemplate", welche wir dann in unserem Controller entsprechend füllen müssen. Ich habe zudem gleich eine CSS Datei eingebunden, die wir gleich auch erstellen werden.
Zuerst müssen wir aber noch unsere beiden Templates "index.php" und "show.php" anpassen. Das "index.php" Template sollte dann wie folgt aussehen:
-
<?php foreach($this->articles as $article) : ?>
-
<p><em><?php echo $this->escape($article['art_cdate']); ?></em></p>
-
<p><?php echo $this->escape($article['art_teaser']); ?></p>
-
<hr>
-
<?php endforeach; ?>
Und das "show.php" Template sollte wie folgt aussehen:
Zuguterletzt müssen wir noch die beiden Aktionsmethoden indexAction und showAction im ArticleController anpassen:
-
public function indexAction()
-
{
-
[...]
-
-
$view->assign('title', 'TravelloBlog - List of articles');
-
$view->assign('articles', $data);
-
$view->assign('subtemplate', 'article/index.php');
-
-
$this->getResponse()->setBody($view->render('main.php'));
-
}
-
-
[...]
-
-
public function showAction()
-
{
-
[...]
-
-
$view->assign('title', 'TravelloBlog - Show article');
-
$view->assign('article', $data);
-
$view->assign('subtemplate', 'article/show.php');
-
-
$this->getResponse()->setBody($view->render('main.php'));
-
}
Im Browser kannst du jetzt überprüfen, dass weiterhin alles so wie zuvor funktionieren sollte.
Ein klein wenig Design
Damit unser Blog nicht ganz so langweilig aussieht, bringen wir nun zuerst mehr Struktur in den HTML Code rein und verschönern dann die Ausgabe mit CSS. Da wir nun ein einziges Haupttemplate haben, haben wir uns die Arbeit nun deutlich erleichtert.
Wir gestalten den Seitenaufbau nun mit Hilfe mehrerer div-Blöcke in ein dreispaltiges Layout mit einem Seitenkopf und einem Seitenfuß um. In der linken Spalte platzieren wir später die Navigation und in der rechten Spalte einige Infoboxen, die wir später noch mit externen Inhalten befüllen werden.
Das geänderte Template "main.php" sieht nach den Umbauarbeiten dann wie folgt aus:
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
<base href="http://<?php echo $this->escape($_SERVER['HTTP_HOST']) . $this->escape($this->baseurl); ?>/">
-
<link href="files/css/styles.css" rel="stylesheet" type="text/css">
-
</head>
-
<body>
-
<div id="page">
-
<div id="header">
-
<h1><?php echo $this->escape($this->title); ?></h1>
-
</div>
-
<div id="main">
-
<div id="navi">
-
<ul>
-
<li><a href="#">a</a></li>
-
<li><a href="#">b</a></li>
-
<li><a href="#">c</a></li>
-
<li><a href="#">d</a></li>
-
</ul>
-
</div>
-
<div id="extra">
-
<div class="extrabox">Box 1</div>
-
<div class="extrabox">Box 2</div>
-
<div class="extrabox">Box 3</div>
-
</div>
-
<div id="content">
-
<?php echo $this->render($this->subtemplate); ?>
-
</div>
-
</div>
-
<div id="footer">
-
TravelloBlog 2006
-
</div>
-
</div>
-
</body>
-
</html>
Danach lege bitte im Verzeichnis "/travelloblog/public/files/css" die Datei "styles.css" an und füge dort folgenden Code ein:
-
html,input,textarea {
-
font-family: Arial, Helvetica, sans-serif; font-size: 100.01%;
-
}
-
-
hr {
-
border: none; border-top: 1px dotted; border-color: rgb(159, 202, 135);
-
}
-
-
h1 {
-
font-size: 1.3em; margin: 0em; padding: 0em;
-
}
-
-
h2 {
-
font-size: 1.1em; margin: 0em; padding: 0.2em;
-
background-color: rgb(223, 237, 215);
-
}
-
-
legend {
-
font-weight: bold; border: 1px solid; padding: 0.2em 0.4em;
-
background-color: rgb(223, 237, 215);
-
}
-
-
table {
-
width: 100%;
-
}
-
-
input,textarea {
-
width: 100%;
-
}
-
-
th,td {
-
vertical-align: top;
-
}
-
-
#page {
-
width: 95%; margin: auto;
-
}
-
-
#header {
-
margin-bottom: 1em; border: 1px solid; padding: 1em;
-
background-color: rgb(159, 202, 135);
-
}
-
-
#main {
-
border-right: 1px solid; border-left: 1px solid; padding-left: 1em;
-
padding-right: 1em;
-
}
-
-
#navi {
-
width: 10em; float: left;
-
}
-
-
#extra {
-
width: 10em; float: right;
-
}
-
-
#content {
-
border-left: 1px dotted rgb(159, 202, 135);; padding-left: 1em;
-
border-right: 1px dotted rgb(159, 202, 135); padding-right: 1em;
-
margin-left: 11em; margin-right: 11em;
-
}
-
-
#footer {
-
clear: both; font-size: 0.8em; text-align: center; font-style: italic;
-
margin-top: 1em; padding: 0.4em; border: 1px solid; font-size: 0.8em;
-
background-color: rgb(223, 237, 215); margin-top: 1em;
-
}
-
-
#navi ul {
-
margin: 0; padding: 0;
-
}
-
-
#navi li {
-
list-style: none; margin: 0; padding: 0.2em; display: block;
-
margin-bottom: 0.4em; background-color: rgb(223, 237, 215);
-
border: 1px solid rgb(159, 202, 135);
-
}
-
-
#navi li a {
-
text-decoration: none; color: #000000;
-
}
-
-
.extrabox {
-
font-size: 0.8em; margin-bottom: 1em; border: 1px dotted;
-
}
-
-
.button {
-
width: auto;
-
}
-
-
.error {
-
background-color: rgb(244, 225, 225); border: 1px solid rgb(204, 0, 0);
-
padding: 0.2em; color: rgb(204, 0, 0);
-
}
Wenn du nun wieder die Adresse "http://travelloblog/article/" in deinem Browser aufrufst, sieht das Ganze nach der Schönheitskur schon ein wenig besser aus:

Um die Navigationsspalte links und die Boxen rechts kümmern wir uns dann in weiteren Teilen dieses Tutorials. Zudem werden wir im nächsten Teil des Tutorials ein Formular für das Anlegen und Bearbeiten von Artikeln realisieren.
Wenn du dich ein wenig intensiver mit mehrspaltigem CSS Design auseinander setzen möchtest, empfehle ich dir das sehr gute YAML Tutorial, das vor kurzem in der Version 2.5.1 erschienen ist, die sogar schon den IE7 unterstützt.
Übung: die weiteren Views
Als Übung bis zum nächsten Teil dieses Tutorials erstelle für die Kategorien und Tags die Templates für die Liste und die Anzeige einer Kategorie bzw. eines Tags. Zudem musst du auch in den Controller Klassen CategoryController und TagController die Aktionsmethoden indexAction und showAction überarbeiten, um die neuen Templates zu verwenden. Hierbei kannst du dich auch an dem ArticleController orientieren. Bitte verwende für showAction Methode zur Anzeige eines Tags bzw. einer Kategorie nicht den jeweiligen Primärschlüssel, sondern das Feld "tag_path" bzw. "cat_path".
Zur Kontrolle lade dir den aktuellen Stand des Tutorials runter. Dann kannst du deine Ergebnisse damit vergleichen.
Download
Der aktuelle Stand des Tutorials nach diesem fünften Teil kann herunter geladen werden. Hier sind auch alle Templates enthalten. Die Textdateien "empty.txt" in einigen Verzeichnissen habe ich nur angelegt, weil mein Winzip keine leeren Verzeichnisse im Zip Archiv anlegt:
Die Zip Datei enthält nicht die aktuelle Version des Zend Frameworks. Dies musst du bitte selber in das entsprechende Verzeichnis kopieren.
Zusammenfassung
In diesem fünften Teil des Zend Framework Tutorials haben wir die View Komponente unseres TravelloBlogs mit Hilfe der Zend_View Komponente des Zend Frameworks realisiert. Wir haben mehrere Templates für die Anzeige der Artikelliste und die Ausgabe eines Artikels erstellt und diese Templates mit Hilfe von Zend_View ausgegeben. Zudem haben wir ein globales Template für den generellen Seitenaufbau erstellt und dem Projekt mit Hilfe von CSS ein wenig Design verpasst.
Im nächsten Teil werden wir uns um die Formularverarbeitung kümmern. Wir werden ein Formular für das Anlegen und Ändern von Artikel erstellen und die schrittweise Verarbeitung des Formulars in unserem ArticleController realisieren. Außerdem werden wir uns um die Startseite unseres TravelloBlogs kümmern.
Fragen, Probleme und Anregungen bitte nicht per E-Mail senden, sondern hier in die Kommentare stellen. Dann haben alle etwas davon.
Change Log
An dieser Stelle werden Änderungen an diesem Tutorial Teil zusammengefasst, die nach dem Ersterscheinen (18.9.2006) notwendig waren, z.B. durch neuere Versionen des Zend Frameworks oder Änderungen am Konzept des Tutorials oder den Anforderungen an unser TravelloBlog.
- 22.09.2006: Zwei Dateien im Zip-Archiv waren fehlerhaft und wurden korrigiert
- 05.11.2006: Tutorial für Release 0.2.0 aktualisiert
- 28.12.2006: für Release 0.6.0 aktualisiert


Montag, 18.09.2006, um 08:24
[...] Db Adapter, Zend Db Table, Zend Db Table Row, Zend Db Table Rowset Bookmark: del.icio.us yigg.de mister-wong.de [...]
Montag, 18.09.2006, um 08:25
[...] MVC Design Pattern und Zend_View [...]
Freitag, 22.09.2006, um 17:51
Der Inhalt von "\application\views\article\index.php" im Zip "version-0.5.zip" unterscheidet sich von dem oben..: "$this->tags as $tag" anstelle von "$this->articles as $article"
Gruss
Daniel
Freitag, 22.09.2006, um 18:16
Hallo Daniel,
danke für den Hinweis. Ich hatte an dem Teil noch einige Lastminute Änderungen gemacht und hatte dabei einige Problem mit Subversion. Deswegen war das Template im Zip zerstört. Habe es nun aber aktualisiert.
Gruß,
Ralf
Montag, 25.09.2006, um 09:34
Hallo Ralf,
im Quellcode für das Haupttemplate muss doch sicherlich der Pfad zur CSS-Datei nicht [b]href="files/css/styles.css"[/b] sondern [b]href="_files/css/styles.css"[/b] heissen.
Gruß, Felix
Montag, 25.09.2006, um 10:09
Hallo Felix,
stimmt natürlich, wenn du das Tutorial bisher Schritt für Schritt verfolgt hast. Ich habe aber das Verzeichnis in der Applikation anscheinend nicht "_files" sondern "files" benannt. Ist eine kleine Unschärfe, aber ich hoffe, dass ist kein grosses Hindernis. Muss ich dann mal anpassen.
Gruß,
Ralf
Montag, 25.09.2006, um 10:10
[...] Im fünften Teil vom Zend Framework Tutorial haben wir die Zend_View Komponente kennen gelernt und schon für die Anzeige der Artikelliste und eines einzelnen Artikels verwendet. Wir haben ein Haupttemplate mit dem Seitenaufbau sowie mehrere Subtemplates erstellt. Zudem haben wir ein wenig CSS unser TravelloBlog ein wenig aufgehübscht. Wenn du die ersten fünf Teile noch nicht gelesen hast, hole dies bitte schnell nach. [...]
Mittwoch, 01.11.2006, um 03:33
Hi Ralf,
super Tut! Bin begeistert!!
Für die Leute, die aufs Update hier nicht warten möchten.
$router->addRoute ist seit der Version 0.2 geändert. Mehr Infos gibt es momentan wohl nur hier
http://framework.zend.com/issues/browse/ZF-354
Grüße
Stoyanov
Mittwoch, 01.11.2006, um 10:20
Hallo Stoyanov,
genau, danke schon mal für den Hinweis für die anderen Leser. Das Tutorial Upgrade kommt hoffentlich bald.
Gruß,
Ralf
Freitag, 17.11.2006, um 20:53
Not very simple but still effective solution to get reed of those $view->render('main.php') in every action is using controller plugins:
http://polarblogs.com/luke/archives/16
Sorry für en...
Donnerstag, 07.12.2006, um 00:16
Hey Ralf und erstmal ein Lob zu dem super Tutorial.
Leider habe ich ein kleines Problem und ich weiß nicht wieso:
Wenn ich in meiner Bootstrap Datei folgenden Eintrag ergänze
$route1 = new Zend_Controller_Router_Route(':controller/:action/:id', array('action' => 'index'));
dann bekomme ich folgenden Fehler
Catchable fatal error: Object of class Zend_Controller_Router_Route could not be converted to string
Ich habe auch das fertige Beispiel von hier benutzt, selber Fehler. Ich bin ratlos, scheinbar wird dem Konstruktor dort irgendwie ein Objekt statt String übergeben, wo das trim erfolt - aber wieso?
Freitag, 08.12.2006, um 13:39
@Sebastian Semrok: http://drupal.org/node/98393
Samstag, 30.12.2006, um 06:51
Hiho,
Bin gerade so ein bisschen am lesen und testen und du erwähnst da oben die Methode setRewriteBase()
Das ZF_Manual sagt dazu in (aktuell) Kapitel 4.4.3.6
[quote]Unlike the original Router, the current RewriteRouter can be used in subdirectories. The original RewriteRouter's setRewriteBase() method is no longer available however. Instead the base URL will be automatically detected by Zend_Controller_Request_Http.
Should the base URL be detected incorrectly you can override it with your own base path with the help of Zend_Controller_Request_Http by calling the setBaseUrl() method (see Section 4.4.2.3, “Base Url and subdirectories”).[/quote]
Ansonsten krieg ich das sogar hier auf dem Mac zum laufen *G*
Vielleicht hab ich im neuen Jahr mal Zeit und schick dir was, damit du erwähnen kannst, was und wo man bei MAMP ("Mac-Version" von XAMMP) tun muss, um es zum laufen zu kriegen. ... und überhaupt ... ein paar sachen sind etwas anders.
Samstag, 30.12.2006, um 12:00
Hallo Warui,
die Methode setRewriteBase() gibt es seit 0.6.0 nicht mehr beim RewriteRouter. Ich hatte bei der Überarbeitung vergessen, diese Codezeilen zu entfernen. Danke für den Hinweis.
Gruß,
Ralf
Samstag, 30.12.2006, um 22:33
Also ich habe jetzt längere Zeit (> 4 Stunden) rumprobiert und komme zu dem Ergebnis, dass ZF mich nicht mag :-/
Konkret geht es um den Router, den du registriert hast ($router1).
Wenn ich den einbaue und dann genau so benutze, schmeißt er mir ne Zend_Controller_Dispatcher_Exception zu.
Ich hab auch rausgefunden, warum:
:id wird nicht als Variable umgewandelt, sondern er sucht dann nach irgendwas .... keine Ahnung, nach was.
Deshalb habe ich $router1 mal umgeschrieben, damit es so aussieht:
Jetzt leitet er eine Anfrage an http://travelloblog/article/show/13 auch weiter an die showAction des ArticleController. Soweit so gut. Hier aber kann ich den Wert nicht abgreifen :-(
Ich habe dazu folgendes versucht:
Aus $id = $this->getRequest()->getParam('id'); wurde $id = $this->getRequest()->getParams();
und das habe ich mir mit Zend::dump($id) anzeigen lassen. Das Ergebnis sah so aus:
array(3) {
["controller"] => string(7) "article"
["action"] => string(4) "show"
[13] => string(0) ""
}
Doof das.
Und nun?
Grüße
Warui
P.s: Da stehen immer noch einige Dinge mit der BaseURL drin ... die getter zB. und in den Templates
ZF 0.6 sollte das laut Manual inzwischen automatisch machen.
Samstag, 30.12.2006, um 23:05
Nochmal Ps (kannst du dann ja wieder löschen ;) ):
deine CSS-Datei sucht in einem Ordner files, der Ordner heißt aber eigentlich _files :D
Is mir nur aufgefallen, nachdem er mein CSS nicht angezeigt hat *G*
Und verweis besser nochmal auf den htaccess-eintrag dort mit
RewriteEngine off
Options -Indexes
Montag, 08.01.2007, um 00:09
Hallo!
Aber der neuen Version kann man nich mehr mit getRewriteBase() auf die baseUrl zugreifen.
Änderungen:
Man muss in der Bootstrap-Datei den Front-Controller in die Registry laden
---> Zend::register('front', $controller);
Dann kann man in der ArticleController-Klasse diese so laden:
---> $controller = Zend::registry('front');
$baseurl = $controller->getBaseUrl();
Hoffe konnte so helfen ;-)
MFG Nilson
Montag, 08.01.2007, um 13:26
Also ich habe momentan ein Problem dabei, die einzelnen Artikel anzuzeigen.
Und zwar habe ich mit var_dump erfahren, dass mit der Methode find($id) ein Array der DB erstellt wird, wo aber alles den Wert NULL hat.
ISt da durch ein Update vielleicht was neues erschienen oder woran kann das liegen?
MFG Nilson
Freitag, 19.01.2007, um 20:55
Nilson schrieb
hoffe konnte so helfen ;-)
--> jaein
mein routing part schaut nach deiner änderung so aus
// routing
$router = new Zend_Controller_RewriteRouter();
Zend::register('router', $router);
$controller = Zend_Controller_Front::getInstance();
Zend::register('front', $controller);
$controller->setRouter($router);
$controller->setControllerDirectory($dir);
$controller->dispatch();
das problem was ich nun habe ist, dass mit
$controller = Zend::registry('front');
$baseurl = $controller->getBaseUrl();
keine base url zurückgegeben wird :( normalerweise sollte dort doch /travelloblog/public/article stehen, oder liege ich da falsch ?? Würde mich freuen wenn da jemand eine antwort darauf hat.
Mfg Thomas.
Samstag, 20.01.2007, um 12:08
@ thomas
Also erstmal solltest du noch $controller->setBaseUrl() anwenden und deine BaseUrl setzen.
Und ich habe nun den Front Controller nicht mehr in der Registry....weil das völliger Qutasch ist.
Da Der Front Controller sowieso ein SingletonPattern ist, kannste ganz einfach drauf zugreifen.
Zend_Controller_Front::getInstance()->getBaseUrl();
MFG Nilson
Sonntag, 21.01.2007, um 15:17
keine Ahnung ob ich gerade unfähig bin aber ich bekomm es nicht hin.
-->
mein routing part
// routing
$router = new Zend_Controller_RewriteRouter();
Zend::register('router',$router);
$controller = Zend_Controller_Front::getInstance();
$controller->setRouter($router);
$controller->setControllerDirectory($dir);
$controller->setBaseUrl('/testTheBest');
$controller->throwExceptions(true); // exceptions helfen ja manchmal
try
{
$controller->dispatch();
}
catch (Exception $e)
{
require_once($dir . '/IndexController.php');
echo $e;
}
ich bekomme dann folgende exception mit der ich nicht wirklich viel anfangen kann.
exception 'Zend_Exception' with message 'File "../application/controllers\PublicController.php" was not found.' in C:\Programme\xampp\htdocs\HKSB_Web\library\zf\Zend.php:175 Stack trace: #0 C:\Programme\xampp\htdocs\HKSB_Web\library\zf\Zend.php(103): Zend::loadFile('PublicControlle...', Array, true) #1 C:\Programme\xampp\htdocs\HKSB_Web\library\zf\Zend\Controller\Dispatcher.php(511): Zend::loadClass('PublicControlle...', Array) #2 C:\Programme\xampp\htdocs\HKSB_Web\library\zf\Zend\Controller\Front.php(715): Zend_Controller_Dispatcher->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http)) #3 C:\Programme\xampp\htdocs\HKSB_Web\public\index.php(36): Zend_Controller_Front->dispatch() #4 {main}
ich glaub ich werde mal auf 0.6.0 zurück switchen, damit ich dann wenigstens weiter machen kann :(.
gruß thomas
Montag, 22.01.2007, um 16:35
hmm da stimmt das nicht mit dem routing.....also
1. Frage
benutzt du einen Erweiterten ActionController?
2. Frage
haste das Routing in den public Ordner gelenkt....denn bei dir versucht er einen PublicController zu laden, was mich darauf schließen lässt...dass das mod_rewrite mit dem Routing nicht übereinstimmt....denn eigentlich müsste er ja wenn du es auf den public Order gesetzt hast den IndexController.php öffnen :)
MFG Nilson
Dienstag, 30.01.2007, um 00:55
Bei mir fehlte in articel/index.php - analog zu show.php noch die Zeile
escape($_SERVER['HTTP_HOST']) . $this->escape($this->baseurl); ?>/">
Problem war bei einem direkten Aufruf von /articel/ (mit Slash am Ende. Dabei sind die Links zu show.php folgendermaßen "verdoppelte": /articel/articel/show/. Habe ich den Slash weggelassen, war alles o.k.
Ich habe den Effekt übrigens mit einem eigenen, vom Tutorial adaptierten Beispiel nachgespielt - vielleicht habe ich aber auch dabei irgend was anderes falsch gemacht..;-(
Freitag, 16.02.2007, um 14:29
Irgendwie lädt er bei mir die stylesheets nicht rein. Hab die .htaccess files so angelegt wie beschrieben. Den Fehler mit _files hab ich auch abgeändert.
Freitag, 16.02.2007, um 14:41
Okay hab fehler gefunden. Er mag das Options -Indexes nicht in /blog/public/files/.htaccess file nicht. Tue ich es auskommentieren lädt er die styles.css ein.
Mittwoch, 03.10.2007, um 17:03
Je weiter ich einsteige, desto mehr begreife ich Zusammenspiel von ZF. Geniales Konstrukt und ohne dein Tutorial wäre das nicht möglich gewesen so schnell Überblick zu bekommen.
An dieser Stelle einen riesen Dank von mir.
Einfach TOP!
Dienstag, 13.11.2007, um 17:10
Customized Design Solutions...
I couldn't understand some parts of this article, but it sounds interesting...
Freitag, 01.02.2008, um 17:22
Hier nur mal einige Änderungen in ZF 1.0.3:
Montag, 18.02.2008, um 01:43
Es fehlt noch ein
$front->setRouter($router);
bzw.
$controller->setRouter($router);
in der index.php, damit der Router funktioniert.
Dienstag, 13.05.2008, um 15:17
hi Ralf, tolles Tutorial
ich brauche Deine Hilfe und zwar ich habe mir ZF 1.5.1 heruntergeladen und bin dann auf deine Seite gekommen. Bis heutigen Tag (Teil 5 :)) hat es bei mir alles gut geklappt (mit kleinen Anpassungen zu 1.5.1) nun jetzt habe ich mit dem showAction und show.phtml nur Problemen
Beispiel:
[php}
Notice: Undefined index: art_text in C:\wamp\www\travelloblog\application\views\scripts\article\show.phtml on line 12
[/php}
showAction sieht bei mir so aus :
show.phtml
var_dump($data) zeigt
array
0 =>
array
'art_id' => string '2' (length=1)
'art_user_id' => string '1' (length=1)
'art_cdate' => string '2006-08-24 18:13:45' (length=19)
'art_udate' => string '2006-08-24 18:13:45' (length=19)
'art_title' => string 'Another test' (length=12)
'art_text' => string 'This is the second test article which contains some text.' (length=57)
'art_teaser' => string 'This is another teaser' (length=22)
Habe ich etwas übersehen ??
Dienstag, 13.05.2008, um 15:21
sorry noch mal show.phtml
escape($this->title); ?>
escape($_SERVER['HTTP_HOST']) . $this->escape($this->baseurl); ?>/">
escape($this->title); ?>
escape($this->article['art_title']); ?>
escape($this->article['art_cdate']); ?>
escape($this->article['art_text']); ?>
back to list