先介绍下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 :)
留言交流