Yii与DWZ整合-关于分页

这篇日志发布时间已经超过一年,许多内容可能已经失效,请读者酌情参考。

先介绍下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 :)

留言交流

没有评论
点击换图