先介绍下DWZ,DWZ框架是个国产的开源JS框架,其目的就是使开发人员不写JS的情况下依然能做出AJAX交互, DWZ框架完全开源,源码没有做任何混淆处理,非常方便修改。本文采用的是DWZ 1.4。
在Yii的官网上有个DWZ的扩展,可以去看下。版本和风格我不太喜欢,所以直接用的DWZ的原版。
在文章列表页面,我采用CActiveDataProvider 来获取数据并分页。经过测试发现,这种方式的分页只接受GET方式提交数据。而DWZ的分页,是基于AJAX的POST提交,所以无法分页。
解决的方法大致上有以下几种:
1、修改分页代码,使用传统的DAO方式获取数据。
按着以前的思路,通常是这里自己建立AR,利用GET/POST提交过来的currentPage等信息,编写criteria,然后用DAO去数据库查询。
但是框架的很大一个作用就是管理代码,减少重复的造轮子repeat,用Yii的CActiveDataProvider,多简洁~所以果断没有用这种方式。
2、修改DWZ,使分页使用GET方式提交。
DWZ的分页组件的使用方法:
第一步,先写个有关分页的表单,后边用AJAX提交。
<form id="pagerForm" action="xxx" method="post"> <input type="hidden" name="pageNum" value="1" />/><!--【必须】value=1可以写死--> <input type="hidden" name="numPerPage" value="20" /><!--【可选】每页显示多少条--> <!--【可选】其它查询条件,业务有关,有什么查询条件就加什么参数。 也可以在searchForm上设置属性rel=”pagerForm”,js框架会自动把searchForm搜索条件复制到pagerForm中 --> <input type="hidden" name="name" value="xxx" /> </form>
表单的ID是pageForm是默认值,其中必须有一个name="pageNum"的项目,因为提交的时候JS会检查这个项。 第二步,写入分页组件的HTML。DWZ支持HTML扩展方式调用。
<div class="pagination" targetType="navTab" totalCount="200" numPerPage="20" pageNumShown="10" currentPage="1"></div>
以上就可以实现DWZ的分页了。 分页相关代码在dwz.ajax.js中,如下:
/** * 处理navTab中的分页和排序 * targetType: navTab 或 dialog * rel: 可选 用于局部刷新div id号 * data: pagerForm参数 {pageNum:"n", numPerPage:"n", orderField:"xxx", orderDirection:""} * callback: 加载完成回调函数 */ function dwzPageBreak(options){ var op = $.extend({ targetType:"navTab", rel:"", data:{pageNum:"", numPerPage:"", orderField:"", orderDirection:""}, callback:null}, options); var $parent = op.targetType == "dialog" ? $.pdialog.getCurrent() : navTab.getCurrentPanel(); if (op.rel) { var $box = $parent.find("#" + op.rel); var form = _getPagerForm($box, op.data); if (form) { $box.ajaxUrl({ type:"POST", url:$(form).attr("action"), data: $(form).serializeArray(), callback:function(){ $box.find("[layoutH]").layoutH(); } }); } } else { var form = _getPagerForm($parent, op.data); var params = $(form).serializeArray(); if (op.targetType == "dialog") { if (form) $.pdialog.reload($(form).attr("action"), {data: params, callback: op.callback}); } else { if (form) navTab.reload($(form).attr("action"), {data: params, callback: op.callback}); } } }
当指定rel的时候,可以直接修改POST为GET。当按照上面的方式去写的时候,没有rel值,则默认采用的是reload当前navTab的方式,这尼玛就得去dwz.navTab.js中修改:
reload: function(url, options){ var op = $.extend({data:{}, navTabId:"", callback:null}, options); var $tab = op.navTabId ? this._getTab(op.navTabId) : this._getTabs().eq(this._currentIndex); var $panel = op.navTabId ? this._getPanel(op.navTabId) : this._getPanels().eq(this._currentIndex); if ($panel){ if (!url) { url = $tab.attr("url"); } if (url) { if ($tab.hasClass("external")) { navTab.openExternal(url, $panel); } else { $panel.ajaxUrl({ type:"POST", url:url, data:op.data, callback:function(response){ navTab._loadUrlCallback($panel); if ($.isFunction(op.callback)) op.callback(response); } }); } } } }
可以在提交的时候进行判断,是否有pageNum,如果有则GET,如果没有则POST。
至此改造完成,但是由于修改了DWZ的框架代码,以后不能平滑升级=。=比较讨厌,所以没有采用。
3、修改Yii,使CActiveDataProvider的分页支持POST提交。
刚遇到这个问题的时候感觉很好奇,为啥分页只支持GET方式?
从头开始看。
获取数据时候使用的是:$dataProvider = new CActiveDataProvider ( 'Post' );//没有加条件 然后看CActiveDataProvider 。
在构造函数中按着输入的‘Post’建立相应的model CActiveRecord。所以自己写AR就是在重复框架做的事情。
在view的页面中使用了zii的CListView,里面的渲染函数中调用了dataProvider(CActiveDataProvider )的fetchData()函数以获取数据。
protected function fetchData() { //克隆了检索条件 $criteria=clone $this->getCriteria(); //提取分页参数 if(($pagination=$this->getPagination())!==false) { $pagination->setItemCount($this->getTotalItemCount()); $pagination->applyLimit($criteria); } //保存原有的条件 $baseCriteria=$this->model->getDbCriteria(false); if(($sort=$this->getSort())!==false) { // set model criteria so that CSort can use its table alias setting if($baseCriteria!==null) { $c=clone $baseCriteria; $c->mergeWith($criteria); $this->model->setDbCriteria($c); } else $this->model->setDbCriteria($criteria); $sort->applyOrder($criteria); } //按条件查询数据 $this->model->setDbCriteria($baseCriteria!==null ? clone $baseCriteria : null); $data=$this->model->findAll($criteria); // 还原初始的条件 $this->model->setDbCriteria($baseCriteria); return $data; }
其实关键也就是在提取分页参数那里。这里利用了PHP的对象参数的特性,PHP函数中的参数如果是类对象,那么这个参数都是引用传参,无论是否是显性的。
OK。进入CPagination的applyLimit()函数看看。
public function applyLimit($criteria) { $criteria->limit=$this->getLimit(); $criteria->offset=$this->getOffset(); } /** * @return integer the offset of the data. This may be used to set the * OFFSET value for a SQL statement for fetching the current page of data. * @since 1.1.0 */ public function getOffset() { return $this->getCurrentPage()*$this->getPageSize(); } public function getCurrentPage($recalculate=true) { if($this->_currentPage===null || $recalculate) { if(isset($_GET[$this->pageVar])) { $this->_currentPage=(int)$_GET[$this->pageVar]-1; if($this->validateCurrentPage) { $pageCount=$this->getPageCount(); if($this->_currentPage>=$pageCount) $this->_currentPage=$pageCount-1; } if($this->_currentPage<0) $this->_currentPage=0; } else $this->_currentPage=0; } return $this->_currentPage; }
applyLimit函数修改了$criteria的offset,offset=$this->getCurrentPage()*$this->getPageSize()。
getCurrentPage()函数使用$this->_currentPage=(int)$_GET[$this->pageVar]-1;获取currentPage。
至此为何Yii的分页只接受GET参数的原因就探索完了。如果要让Yii支持POST分页,修改这里即可。
但是,那个pageVar又是个神马东西?
前面的查看fetchData跳过了一个问题,那就是,这个pagination是怎么来的?
追到CActiveDataProvider的父类,CDataProvider,查看getPagination()函数:
public function getPagination() { if($this->_pagination===null) { $this->_pagination=new CPagination; //从分页的结果上来看,最终的pageVar就是Post_page if(($id=$this->getId())!='') $this->_pagination->pageVar=$id.'_page'; } return $this->_pagination; }
可以看出pageVar其实是 CDataProvider的ID和默认值page通过'_'连接的字符串。
最后回过头来看看CActiveDataProvider的构造函数:
public function __construct($modelClass,$config=array()) { if(is_string($modelClass)) { $this->modelClass=$modelClass; $this->model=CActiveRecord::model($this->modelClass); } else if($modelClass instanceof CActiveRecord) { $this->modelClass=get_class($modelClass); $this->model=$modelClass; } //设置ID,ID值为model的类名 $this->setId($this->modelClass); foreach($config as $key=>$value) $this->$key=$value; }
$this->setId($this->modelClass);,类的ID就是model的名字,在分页页面中的model是Post,所以最终的pageVar就是Post_page。
至此Yii分页部分的逻辑分析完毕,学习结束做个笔记。
4、不好意思我标题党了。 其实最后我并没有按照上面的方式去修改。。。 我的解决办法是:
if(isset($_POST['pageNum'])) $_GET['Post_page'] = intval($_POST['pageNum']);
have fun :)
留言交流