YII Ajax Paginierung Controller

Bei meinem letzten Projekt habe ich mit dem großartigen Yii Framework eine Seite erstellt, die einerseits den aktuellen Trends folgen sollte, mir persönlich aber auch wichtig war, dass eine „No-Script“ Variante weitestgehend funktioniert.

Viele Inhalte der Seite sollten blätterbar sein und häufig wurde per Tab Navigationen Inhalt ausgespielt. Spontan fallen einem folgende Möglichkeiten ein:

  1. Alte Schule: Alles per kompletten Seiten Abruf regeln. Der Vorteil dieser Lösung ist nicht von der Hand zu weisen, nahezu jeder Browser (alt/neu/mobil/text) kann diese Variante umsetzten. Ein weiterer Vorteil ist die „überschaubare“ Datenbanklast, durch den Einsatz von LIMIT und OFFSET im QUERY. Und sollte unser Besucher mal eine Suchmaschine sein, kann er den kompletten Inhalt finden.
  2. Alte Schule in neuer Verpackung: Es werden alle Inhalte ausgespielt, doch nur die ersten 10 angezeigt. Mit JavaScript wird eine Blätterfunktion geboten, die dann immer die passenden Elemente versteckt oder anzeigt. Der erste Nachteil ist dass der Benutzer erst alle Inhalte kurz sieht, bis sie vom JavaScript versteckt werden. Würde man im Template die Inhalte verstecken, würden Browser ohne Javascript ja nur einen Bruchteil der Seite anzeigen. Ausserdem erzeugt die Generierung der Seite sehr viel Serverlast und mit immer mehr Inhalt ist die Wahrscheinlichkeit das ein Besucher wirklich allen Inhalt anschaut immer seltener. So arbeitet der Server im Endeffekt umsonst. Weiter erhält man unter Umständen sehr langen Quelltext und die Seite kann träge auf die Blätterfunktion reagieren. Auch Suchmaschinen könnten hier Inhalte finden, die ein Nutzer auf den ersten Blick nicht sieht (weil er noch nicht geblättert hat). Im Endeffekt ist diese Methode nur EyeCandy und sollte höchstens eingesetzt werden, wenn man nicht fähig ist, die Datenbank richtig abzufragen – dies kann ja systembedingt vorkommen, wenn wir nicht eine mySQL Umgebung voraussetzten.
  3. Echtes Ajax: Das ist natürlich der richtige Weg. Wir fragen immer nur soviel aus der Datenbank ab, wie angefordert ist und tauschen den Seiteninhalt entsprechend aus. Der Besucher wird nicht durch einen Seiten Abruf abgelenkt, der Browser zeigt weiterhin den gleichen Seiten Anblick an. Eigentlich nur Vorteile, doch wenn man sich nur auf AJAX verlässt werden die Suchmaschinen Teile des Inhaltes nicht mehr finden und Besucher mit Browsern die AJAX nicht unterstützen (alt/mobil/lynx?/shell) sind schon wieder gekniffen.
  4. „Hybrid“ AJAX: Und darauf will ich eigentlich heraus. Man baue sich einen Controller der einerseits der alten Schule folgt und über Seitenaufrufe die Blätterfunktion unterstützt, der aber gleichzeitig einen AJAX Aufruf erkennt und dann nur die gewünschte Information zurück gibt.

Im folgenden beschreibe ich meine Version eines YII Framework „hybriden“ AJAX Blätter Controller.

Im ersten Schritt erstellt man die „action“ die später per AJAX auch blättern können soll.

class MyController extends CController {
public actionShowEntry()
{
$model = $this->loadModel();
$this->render('showEntry',array('model'=>$model));
}
...
}

Gehen wir einfach davon aus das unser Model zB Kommentare als Relation definiert hat und die „showEntry“ Ansicht, gibt zB einen Blog Eintrag mit den Kommentaren aus.

Nun hat der Eintrag 38 Kommentare und mehr und die Seite ist dadurch sehr groß geworden, die Kommentare sollten also geblättert werden.

class MyController extends CController {
public actionShowEntry()
{
$model = $this->loadModel();
$comments = Comments::model()->findAll(array('limit'=>10,'order' => '`date` DESC'));
$this->render('showEntry',array('model' => $model, 'comments' => $comments));
}
...
}

Die Kommentarflut ist unter Kontrolle, es werden nur noch die zehn Neuesten ausgespielt. Fehlt nur die Blätter-Navigation und diese muss wissen wie viel Kommentare denn komplett vorhanden sind und wie viele auf einer Seite ausgespielt werden sollen.

„OT:“ Dazu kann man das Pagination Objekt von YII nutzen, aber im Falle dass man von 100 Kommentaren nur die ersten 30 ausspielen lassen will („Kommentar Teaser“) und jeweils nur sechs pro Seite überschreibt das Pagination Objekt leider immer das selbst gesetzte LIMIT. Daher bleibe ich hier bei der Variante alles „selbst“ zu erledigen, der Grundgedanke ist aber auch auf eine Variante mit dem Pagination Objekt von YII anwendbar.

Zurück zum Controller:

class MyController extends CController {
public $pageSize = 10;
public function actionShowEntry()
{
$model = $this->loadModel();
$comments = Comments::model()->findAll(array('limit'=>'0,'.$this->pageSize,'order' => '`date` DESC'));
$commentCount = Comments::model()->count();
$this->render('showEntry',array('model'=>$model,'comments'=>$comments,'commentCount' => $commentCount));
}
}

Auf die $pageSize Variable kann man in der View über $this->pageSize zugreifen, da man im Controller Kontext ist.

Der letzte Schritt fiel etwas klein aus, es wurde lediglich eine neue Variable eingeführt, die zur Setzung des mySQL LIMITs und der BlätterNavigation Ausspielung genutzt wurde, und es wurden alle Kommentare gezählt und der Ansicht mitgegeben.

Angenommen in der View existiert jetzt eine Navigation zum Blättern, die entweder per GET Parameter oder per Routing die aus zu spielende Seite festlegt. Dann sieht die Verarbeitung im Controller wie folgt aus:

class MyController extends CController {
public $pageSize = 10;
public function actionShowEntry()
{
$page = (isset($_GET['page']) ? intval($_GET['page']) : 0);
$offset = $page * $this->pageSize;
$model = $this->loadModel();
$comments = Comment::model()->findAll(array('limit' => $offset.','.$this->pageSize,'order'=>'date DESC'));
$commentCount = Comment::model()->count();    $this->render('showEntry',array('model'=>$model,'comments'=>$comments,'commentCount'=>$commentCount));
}
}

Jetzt hat man ungefähr das was als Möglichkeit 1 angeführt wurde. Und man ist auch kurz vor dem Ziel des hybriden Ajax. Das YII Framework bietet die Möglichkeit einen AJAX REQUEST zu erkennen und zwar mit folgendem Aufruf:

"Yii::app()->request->isAjaxRequest"

. Wer aber auf Nummer sicher gehen will, der gibt seinem AJAX Request einfach einen weiteren Parameter mit, in diesem Fall gibt man noch eine GET Variable isAjaxRequest mit an und unterscheidet mit Hilfe dieser im Controller was zu tun ist.

class MyController extends CController {
public $pageSize = 10;
public function actionShowEntry()
{
$page = (isset(_GET['page']) ? intval($_GET['page']) : 0);
$offset = $page * $this->pageSize;
$comments = Comments::model()->findAll(array('limit' => $offset.','.$this->pageSize, 'order' => 'date DESC'));
if (isset($_GET['isAjaxRequest'])) {
$this->renderPartial('commentList',array('comments'=>$comments);
} else {
$model = $this->loadModel();
$commentCount = Comment::model()->count();
$this->render('showEntry',array('model'=>$model,'comments'=>$comments,'commentCount'=>$commentCount));
}
}
}

Die neu aufgetauchte View commentList sollte innerhalb der showEntry View ebenfalls mit dem gleichen Aufruf zur Ausspielung der Kommentare genutzt werden. Die notwendigen Änderungen an der Blätter Navigation müssen per JavaScript implementiert werden oder man tauscht es in der commentList View direkt mit aus.

Fußnote: Der Kommentar Akquise fehlt noch die Identifizierung zu dem „Eintrag“ Model, dies wurde aus Gründen der Übersicht weggelassen. Außerdem bietet es sich an „scopes“ im Kommentar Model zu implementieren, damit die LIMIT Anweisung einfacher zu lesen wird.
Im Frontend kann man die AJAX Paginierung noch mit dem jQuery history plugin realisieren und schon ist die „stateful hybrid pagination“ komplett.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.