Ich bin endlich mal auf den JSON trip gekommen ;) Gibt es ja schon seit einiger Zeit, aber ich dachte mir immer .. "warum? xml is auch ok.."

Aber direkt in Javascript ein Daten-Objekt zu haben ist einfach göttlich. Vor allem in Verbindung mit Prototype 1.5.0 RC1. Bevor ich jetzt zum eigentlichen Thema des Posts komme möchte ich die Möglichkeit des Zusammenspiels von JSON, CakePHP und Prototype verdeutlichen.

Bei Cake gibt es ja die Model Funktionen findBy, findAll, read, etc... welche ein assoziatives Daten-Array aus der Datenbank liefert. Nun mit meinem JSON View wird das Array in JSON umgewandelt, die Daten als text/x-json geliefert und der notwendige X-JSON header für Prototype gesetzt. Super ist auch das jedes Modell ein eigenes Objekt mit Kindobjekten darstellt!

Um den View zu benutzen braucht man lediglich $this->view = "json"; in die Controller-Action zu schreiben und per $this->set die Daten bereitstellen. Super easy.. :-)

Mit Prototype 1.5.0 RC1 gibt es neue String Funktionen und die Template Klasse. Die Objekt Funktion .each( ) kommt hier zum Einsatz und macht es möglich mit nur wenigen Zeilen ein MySQL Result über AJAX in eine schicke HTML Liste zu verwandeln. JSON ist eben ein purer Datencontainer und genau das gibt uns die Freiheit und Geschwindigkeit die wir wollen.

Hier die von bake.php erstellte und von mir modfizierte Controller-Action:

PHP:
  1. function index($output_json = false)
  2.     {
  3.         $this->User->recursive = 0;
  4.         if ($output_json)
  5.         {
  6.             $this->view = "json";
  7.             $this->set('json_output', $this->User->findAll());
  8.         }
  9.         else
  10.         {
  11.             $this->set('users', $this->User->findAll());
  12.         }
  13.     }

Sobald ich der url /users/index/ ein "json" anhänge (bzw. irgendwas... *hust*) wird pures JSON geliefert. Über die Weiche bin ich mir noch nicht einig, aber das ist ja nur Kosmetik. Mir ging es erstmal darum das es läuft .. eine Funktion, zwei Ausgabemöglichkeiten.

So.. nun kommt der Clou.. Die Ajax Funktion welche mir eine Liste aller Benutzer erstellt ohne das ich grossartig mich um irgendwelche Bezeichner kümmern muss.

JAVASCRIPT:
  1. getUsers: function(){
  2.   var userTpl = new Template('<ul><li>#{username}</li><li>#{email}</ul>');
  3.   new Ajax.Request('/users/index/json', {
  4.     onComplete:function(html,json){
  5.       json.each(function(Data){
  6.         $('out').innerHTML+=userTpl.evaluate(Data.User);
  7.       });
  8.     }
  9.   });
  10. }

Wie man im Template schon erkennen kann, verwende ich die Feldnamen der MySQL-Tabelle als Variable. Also fast genauso wie in CakePHP selbst. Bei dem onComplete müssen zwei Parameter gesetzt werden wobei zu beachten ist, dass der erste eigentlich nicht gebraucht wird (html) und dass der zweite (json) nur in einem Ajax-Objekt gefüllt wird wenn auch wirklich JSON ausgeliefert wird!

Das ist der eigentliche Grund für das Erstellen einer neuen View-Klasse welche die relevanten Header setzt. Innerhalb der each-Schleife stehen alle Model Arrays zur Verfügung die von CakePHP Assoziationen herrühren. In meiner Testumgebung ist das natürlich "User", aber auch "Profile". Ihr könnt das wie gewohnt mit $this->recursive regulieren. Die Anzahl und Tiefe der verfügbaren Objekte ist, soweit ich weiss, unbegrenzt. Mit der .each() Funktion lässt sich also das gesamte Ergebnis-Set in Javascript problemlos aufsplitten und verwerten. $('out') ist lediglich ein leeres DIV. Man muss allerdings beachten das die Template Klasse HTML generiert und keine DOM Objekte - was zum Teil eine Limitierung sein kann.

Alternativ zu der Template() Methode, kann man sich natürlich auch dem Script.aculo.us Builder bedienen um mit den Ergebnissen echte DOM Objekte zu erzeugen. Beispiel:

JAVASCRIPT:
  1. json.each(function(Data){
  2.         $('out').appendChild( Builder.node('p', Data.User.username) );
  3.       });

Ist Geschmacksache und auch abhängig davon ob man Script.aculo.us benutzen mag. Mit dem Builder kann man selektiver sein und sich freier im DOM bewegen.

Kommen wir nun zum View und der JSON Klasse.

PHP:
  1. <?php
  2. /**
  3. * JSON View
  4. *
  5. * @license MIT-style
  6. * @author Kjell Bublitz
  7. * @version 1.0
  8. */
  9. vendor('JSON');
  10.  
  11. class JsonView extends View
  12. {
  13.     function render($action = null, $layout = 'ajax')
  14.     {
  15.         $expectedVar = 'json_output';
  16.  
  17.         if(isset($this->_viewVars[$expectedVar]))
  18.         {
  19.             $JsonClass = new Services_JSON();
  20.  
  21.             $rawControllerData = $this->_viewVars[$expectedVar];
  22.             $jsonEncoded = $JsonClass->encode($rawControllerData);
  23.         }
  24.         else
  25.         {
  26.             die('bad data, need '.$expectedVar);
  27.         }
  28.  
  29.         // uncached, with xjson bit for prototype.js
  30.         header("Pragma: no-cache");
  31.         header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
  32.         header('Content-Type: text/x-json');
  33.         header("X-JSON: ".$jsonEncoded);
  34.  
  35.         // default echo
  36.         echo($jsonEncoded);
  37.     }
  38. }
  39. ?>

Die Variable "expectedVar" enthält den Namen der Variable welche es im Controller zu setzen gilt. Wie oben im Controller zu sehen.

PHP:
  1. $this->set('json_output', $this->User->findAll())

Wenn diese gefunden wird geht von da an alles automatisch. Da dieser View nur für AJAX gedacht ist habe ich auch gleich das caching abgestellt. Den Layout-Parameter habe ich auf das Standard-Layout "Ajax" gestellt - der Einfachheit halber.

Dieser View muss als json.php im Verzeichnis "/view/" gespeichert werden. Im Controller (oder Action) müsst ihr dann $view auf "json" setzen und der View ist aktiv.

Nun zur Klasse: Wie auch die Json Komponente von Zach Cox, benutze ich ebenfalls die Services_JSON Klasse von Michal Megurski. Ich konnte bisher noch keine Probleme festellen. Vielleicht müsste man sich noch um das Charset-Encoding kümmern...

Wenn ihr die JSON Klasse von Pear.net runtergeladen habt, kopiert diese in euer "/vendor/" Verzeichnis und es sollte funktionieren. So.. und nun wünsche ich viel Spass :-)

4 Kommentare zu “JSON View für CakePHP”

  1. 1 Markus Says:
    Netter Artikel aber wollte eigentlich nicht abstimmen - schon gar nicht nur mit einem Stern.
    Wollte lediglich testen ob man sich registrieren muss um abzustimmen ;)

    Gruß
    Markus

  2. 2 Eelco Wiersma Says:
    Fortunately I'm Dutch so I could understand most of your post and I must say I wish I found this post earlier.

    I also did some work on JSON with Cakephp, I've ported the Services_JSON package to a Cakephp component which makes working with it a little easier.

    By the way do you mind if you translate your post to english?

  3. 3 Eelco Wiersma Says:
    Oops, made a little typo in my post I meant:

    By the way do you mind if I translate your post to english?

  4. 4 Patrick Says:
    Sehr praktisch!
    Allerdings ist mir beim Einbau in Cake 1.2 aufgefallen, dass von 1.1 zu 1.2 $this->_viewVars in $this->viewVars geändert wurde!

Trackbacks/Pingbacks

Noch keine Trackbacks/Pingbacks von anderen Blogs

Diesen Eintrag kommentieren