Nicht-HTML-Responses mit dem Zend Framework
Wenn man dynamische Bilder oder RSS-Feeds oder einen AJAX/JSON-Service oder ein Excel-Export mithilfe des Zend Frameworks erstellen will, mußt man 2-3 wichtige Dinge beachten. Der Code soll dann in einem Rss-/Graph-/Ajax-/Export-Controller stehen.
Ein Problem bekommt man, wenn man ein Layout benutzt (Zend_Layout). Denn dann wird dieses Layout immer ausgegeben. Im hier betrachteten Fall wäre das aber sehr schädlich, denn dadurch würden wir unser Bild/RSS-Feed/AJAX/Excel-Response zerstören.
Unschön kann man das wie folgt lösen:
public function rssAction() { // calculate rss data and echo it (with correct headers) exit; }
Richtig und deutlich schöner ist das Abschalten des Layout in der Action, wie folgt:
public function rssAction() { // disable layout $this->_helper->layout()->disableLayout(); // disable view rendering $this->_helper->viewRenderer->setNoRender(); // calculate rss data and echo it (with correct headers) }
Wir schalten auch gleich noch den ViewRenderer mit aus, damit auch nicht versucht wird, ein Viewscript zu rendern (das es wahrscheinlich garnicht gibt).
Dieses RSS-Beispiel funktioniert natürlich genauso für die anderen Beispiele, wo kein klassischer HTML-Quelltext zurückgegeben werden soll, sondern eine Antwort in einem anderen Format gefordert ist.
Damit kann man dann seine dynmisch erstellten Bilder (z.B. mittels pChart, jpgraph oder direkt die GD-Funktionen/image* in php), RSS-Feeds (Zend_Feed), Ajax-Services (Zend_Json) usw. realisieren.
Hier noch schnell ein Beispiel eines Excel-Exports aus einer Datenbank, mit Hilfe der Spreadsheet-Klasse aus dem PEAR-Framework (vereinfacht auf das Wesentliche):
class ExportController extends Zend_Controller_Action { public function excel() { // disable layout $this->_helper->layout()->disableLayout(); // disable view rendering $this->_helper->viewRenderer->setNoRender(); // get some data from database here // create empty file //include 'Spreadsheet/Excel/Writer.php'; $excel = new Spreadsheet_Excel_Writer(); // add worksheet $sheet =& $excel->addWorksheet('Daily Export'); $sheet->setColumn(0,0,20); $sheet->setColumn(1,1,15); $sheet->setColumn(2,2,18); $sheet->setColumn(3,3,23); $sheet->setColumn(4,4,35); $sheet->setColumn(5,5,15); $format_bold =& $excel->addFormat(); $format_bold->setBold(); $format_headline =& $excel->addFormat(); $format_headline->setBold(); $format_headline->setSize(20); $format_headline->setAlign('center'); // headline $sheet->write(0, 0, 'Results: '.date('d.m.Y H:i'), $format_headline); $sheet->mergeCells(0,0,0,5); // add data to worksheet $rowCount=2; foreach ($data as $groupName=>$serverData) { $sheet->write($rowCount, 0, $groupName, $format_bold); $rowCount++; foreach ($serverData as $row) { $colcount = 0; foreach ($row as $key => $value) { $sheet->write($rowCount, $colcount, $value); $colcount++; } $rowCount++; } $rowCount++; } // send client headers $excel->send('daily_export_'.date("Ymd-His").'.xls'); } }
Dies hier ist alter Code, mittlerweile nutzen wir PHPExcel.
Die Beschreibung humpelt etwas: Zend_View (und Zend_Layout) sind beide nicht pauschal auf HTML festgelegt. Es reicht also völlig aus, wenn das View-Skript eben kein HTML enthält, sondern etwas Anderes, zB XML (RSS). Und anstelle das Layout gleich zu deaktivieren, kann man es auch (hier) ein XML-Layout einstellen. So kann man Nicht-HTML auf alt-hergebrachten Weg rendern lassen, was vorallen die Übersicht steigert. Content-Type muss man allerdings trotzdem setzen 😉
Dein erstes Beispiel ist nebenbei nicht nur „unschön“, sondern kann auch negative sideeffects haben: Was ist mit den Plugins mit postDispatch()-Hooks?
KingCrunch
6 Jul 09 at 17:41
Deshalb schrieb ich ja „unschön“. Es ist die erste Idee, die man so hat, wenn man neu mit dem ZF arbeitet, und es funktioniert ja auch in 99% aller Fälle.
Man könnte sicherlich noch weitere Beispiele finden, warum es „unschön“ ist, postDispatch-Hooks sind ein weiteres Beispiel.
Michael Kliewe
6 Jul 09 at 18:59
Wenn man JSON als Antwort aus dem Controller heraus senden will, kann man das auch noch einfacher machen, ohne selbst den ViewRenderer oder das Layout auszuschalten, und zwar mit folgendem:
$this->getHelper(‚Json‘)->sendJson($foo);
Dabei wird auch gleichzeitig alles ins JSON-Format konvertiert 🙂
Dennis Becker
10 Jul 09 at 10:01
Du hast Recht, denn in dem Helper wird das Layout disabled. Es ist also unnötig, dies in der Action zu tun.
Auch hier nachzulesen:
http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelpers.json
Aber irgendwer muss das Layout disablen, soweit sind wir uns einig 😉
Michael Kliewe
10 Jul 09 at 10:54
@Kingdings
Was ist dann mit Plugins mit PostDispatch die nichts ausgeben aber etwas ausführen sollen, un soforn hätten auch andere lösungen nachteile. ich finde diese am schönsten von dem anderem „kram“ den ich bisher gesehen habe. Das Layout lässt sich auch ohne den helper disablen, ($this->view->layout()->disableLayout();)
Natürlich kann man auch ein XML Layout basteln aber hier übernimmt ja eine andere klasse (Spreadsheet_dingens) die komplette generierung.
Seo Consultant
23 Jul 09 at 17:09