Alapesetben lista v. grid lapozásakor AJAX kérést küldünk a szervernek. Ekkor a szerver az egész oldal tartalmát újra lerendereli a kapott kérés paramétereinek megfelelően. Miután a szerver visszaküldte a kész oldalt, a widget kiválasztja a neki szükséges részt, és frissíti magát. Miért rossz ez? Amikor a szerver az egész oldalt elkészíti, akkor az oldalon található összes utasítást végrehajtja: lefuttatja a widget-eket, elvégzi az adatbázis lekérdezéseket, újra létrehozza az objektumokat, azaz rengeteg olyan munkát elvégez, amire nincs szükség, ezzel is terhelve a rendszer erőforrásait.
Filterek segítségével, melyek a controller action-ök előtt hajtódnak végre, szerencsére el tudjuk kerülni ezeket a felesleges munkákat. Az ötlet az, hogy készítünk egy metódust, mely kizárólag a grid/list nézetet adja vissza, és AJAX kérések esetén ezt a metódust haszáljuk a kérés teljesítéséhez.
Tegyük fel, hogy a User model-t listázó grid/listview-val dolgozunk. A UserController-en belül létre kell hoznunk a két metódust, melyek a lista/grid ajax-os renderelését végzik:
public function getListViewForUserList() { $dataProvider = new CActiveDataProvider('User');
$this->renderPartial('/user/_listView', array( 'dataProvider' => $dataProvider, // Kézzel beállítjuk az ID-ket, hogy hivatkozhassunk rájuk. FONTOS! 'htmlOptions' => array( 'id' => 'UserList', ), )); }
public function getGridViewForUserGrid() { $model = new User('search'); $model->unsetAttributes(); if (isset($_GET['User'])) { $model->attributes = $_GET['User']; }
$this->renderPartial('/user/_gridView', array( 'model' => $model, // Kézzel beállítjuk az ID-ket, hogy hivatkozhassunk rájuk. FONTOS! 'htmlOptions' => array( 'id' => 'UserGrid', ), )); }
az id-ket az eredeti action-ökben is be kell kézzel állítani, hogy jól jöjjenek létre.
public function actionIndex() {
$dataProvider = new CActiveDataProvider('User');
$this->render('index', array( 'dataProvider' => $dataProvider, 'htmlOptions' => array( 'id' => 'UserList', ), )); }
public function actionAdmin() { $model = new User('search'); $model->unsetAttributes(); if (isset($_GET['User'])) { $model->attributes = $_GET['User']; }
$this->render('admin', array( 'model' => $model, 'htmlOptions' => array( 'id' => 'UserGrid', ), )); }
Ezt követően létrehozzuk a view file-okat, amiket az új metódusaink használnak:
Alapértelmezett esetben a protected/views/user/ mappán belül hozzuk létre a _gridView.php és _listView.php file-okat az alábbi tartalommal:
_gridView.php <?php // A $htmlOptions-ben adjuk át a widget id-ját, hogy később tudjunk rá hivatkozni. $this->widget('zii.widgets.grid.CGridView', array( 'htmlOptions' => $htmlOptions, 'dataProvider' => $model->search(), 'filter' => $model, 'columns' => array( 'id', 'username', 'password', 'email', array( 'class' => 'CButtonColumn', ), ), ));
_listView.php <?php // A $htmlOptions-ben adjuk át a widget id-ját, hogy később tudjunk rá hivatkozni. $this->widget('zii.widgets.CListView', array( 'htmlOptions' => $htmlOptions, 'dataProvider' => $dataProvider, 'itemView' => '_view', ));
Miután elkészítettük a két view file-t, akkor egyből fel is tudjuk őket használni az eredeti grid és lista nézetekben. Ez fontos lépés, mert így bebiztosítjuk, hogy az eredeti nézetek esetében is az általunk megszabott id-vel jöjjenek létre a widget-ek, valamit a kódunk is tisztábbá és könnyebben kezelhetővé válik, mivel ugyanazt a nézetet nem tároljuk több helyen egyszerre.
protected/views/user/admin.php ... <?php $this->widget('zii.widgets.grid.CGridView', array( 'id' => 'user-grid', 'dataProvider' => $model->search(), 'filter' => $model, 'columns' => array( 'id', 'username', 'password', 'email', array( 'class' => 'CButtonColumn', ), ), ));
HELYETT
<?php $this->renderPartial('/user/_gridView', array( 'model' => $model, 'htmlOptions' => $htmlOptions, ));
Hasonlóan protected/views/user/index.php
<?php $this->widget('zii.widgets.CListView', array( 'dataProvider' => $dataProvider, 'itemView' => '_view', ));
HELYETT
... <?php $this->renderPartial('/user/_listView', array( 'dataProvider' => $dataProvider, 'htmlOptions' => $htmlOptions, ));
A filter, ami AJAX kérés esetén a megfelelő metódust hívja meg. A protected mappán belül hozzunk létre egy filters mappát, és ebben egy file-t AjaxViewFilter.php néven a következő kóddal:
class AjaxViewFilter extends CFilter { protected function preFilter($filterChain) { // POST kérésekkel nem foglalkozzunk. if (Yii::app()->request->isPostRequest) { return parent::preFilter($filterChain); }
if (Yii::app()->request->isAjaxRequest && !empty($_GET['ajax'])) { // A GET kérés 'ajax' paramétere tartalmazza a widget ID-jét. $id = $_GET['ajax']; // Mivel a UserController-ben beállítottuk, hogy a widget-ek milyen ID-vel jöjjenek // létre, ezért itt most egyszerűen szét tudjuk választani őket. stripos($id, 'grid') !== false ? $view = 'GridView' : $view = 'ListView'; // Felépítjük az AJAX kérést kiszolgáló metódus nevét. // (Ennek a mintájára hoztuk létre a UserController-ben a két új metódusunkat.) $method = "get{$view}For{$id}"; // Ha létezik a metódus, akkor meghívjuk, egyébként jelezzük, hogy nem létezik. if (method_exists($filterChain->controller, $method)) { $filterChain->controller->$method(); Yii::app()->end(); } else { $className = get_class($filterChain->controller); throw new CHttpException(400, "C{$view} handler function {$method} not defined in {$className}."); } }
return parent::preFilter($filterChain); } }
Ez a filter az összes model grid/listájának a szűrésére alkalmas, ha a $method = „get{$view}For{$id}”; minta alapján nevezzük el a controller-ekben az AJAX kéréseket teljesítő metódusainkat.
Végezetül beállítjuk a UserController-ben az új filterünket:
... public function filters() { return array( 'accessControl', // perform access control for CRUD operations 'postOnly + delete', // we only allow deletion via POST request // Itt állítjuk be a filtert (az index és admin action-öket figyeli) array('application.filters.AjaxViewFilter + index, admin'), ); }