场景:
有一个饭店表 restaurant,存放所有饭店记录。我需要一个功能,将饭店按照不同的条件进行多重查询。就象这样:
氛围:浪漫 / 商务会谈 / 茅草屋
菜系:川菜 / 鲁菜 / 家常菜...
区域:东区 / 西区 / 南区 / 北区...
我点击一个或多个条目,则下方自动刷出符合条件的饭店来。
例如我点击了 浪漫、商务会谈、鲁菜,由于浪漫、商务会谈是同属于氛围的,应该认为用户想查找的是“或”的关系,不管出现二者的哪一个,都属于查询范围;而鲁菜是属于菜系的,那么与前两个条件来说,应该是“与”的关系,即:用户想查(氛围==浪漫 || 氛围==商务会谈)&& (菜系==鲁菜)的所有饭馆的集合。
这里有一个逻辑上的小别扭:如果我氛围选了浪漫,菜系不选,那么是不是要选择(氛围==浪漫)&& (菜系==none)吗?这样的结果肯定为空。实际上,不选菜系表示对菜系无要求,所以范围应该是(氛围==浪漫)&& (菜系==所有)。
考虑到这个功能比较常见,打算做一个通用的东西出来
感觉这是个难度比较大的功能,来做一下吧。
1、建立几个表:tag_tablename、tag_title、tag_text、tag_refer。其中
tag_tablename - 在这里只有一条记录 restaurant,但将来可能会有其它的,如article、user等
tag_title - 这是大的分类,分别为氛围、菜系、区域,每一条的tablename_id为1,即restaurant
tag_text - 这是具体的tag,如浪漫、川菜、鲁菜、东区。。等等
tag_refer - 这是一个多对多的表,它记录了restaurant表中的记录与tag_text表中的记录的多对多的对应关系
2、创建 Model。这里,TagTablename里实现了tag_titles,TagTitle里面实现了tag_texts。
3、写实现的类 CAnjoTag
// 生成: // 氛围:浪漫 / 商务会谈 / 茅草屋... // 菜系:川菜 / 鲁菜 / 家常菜... // 区域:东区 / 西区 / 南区 / 北区... // 这样的样式,并且最好能接受点击,并且最好在点击后调用ajax更新下面的查询结果 public static function tag_list($tablename, $options=array()) { $r=' <ul class="select">'; 。。。 return $r; }
这里面的具体实现就不贴出来了,主要是一步步生成html代码,显示前面的那些内容,并接受点击,点击一次选择,再次点击则取消选择。点击会触发一个javascript函数,它回调另一个javascript函数,用以 Ajax 方式更新查询区域的内容。
然后,这样调用和更新ajax获取的内容
<?php echo CAnjoTag::tag_list('Restaurant', array('click_callback'=>'tag_clicked')); ?> <script> function tag_clicked(ids) { // console.log(ids); $('#rest-grid').load('?r=rest/ajaxAction&action=rest_query', {tagtext_ids: ids}); } </script>
在RestController中这样实现:
public function actionAjaxAction($action) { $r=''; switch ($action) { case 'rest_query': $ids=Yii::app()->request->getParam('tagtext_ids'); if (substr($ids, -1)==',') $ids=substr($ids, 0, -1); //删除结尾的 , $ids=trim($ids); if ($ids!='') { $cond="id in (select record_id from {{tag_refer}} where tag_text_id in ($ids))"; $criteria = new CDbCriteria; $criteria->condition=$cond; $count = Restaurant::model()->count($criteria); $pager = new CPagination($count); $pageSize=10; $pager->params = array('tagtext_ids'=>$ids, 'action'=>'rest_query');//分页中添加其他参数 $pager->pageSize=$pageSize; $pager->applyLimit($criteria); $dataProvider=new CActiveDataProvider('Restaurant', array( 'criteria'=>$criteria, 'sort'=>array( 'defaultOrder'=>'id desc', ), 'pagination'=>$pager, )); $this->renderPartial('_query_result', array('dataProvider'=>$dataProvider)); die(); } break; } echo $r; }
4、ajax换页问题
调试ajax换页时费了很大劲。总是做不到按Ajax方式显示下一页,一点页码的链接就跳到新页上了。看一下console的错误信息,不能ajax调用的原因是在 jquery.yiigridview.js 中,出现了错误:
$.param.querystring is not a function
网上解释说:这个错误会导致你无论怎么选择filter,都不会发送请求道cintroll去取数据,解决办法就是,无论在什么地方调用jquery,最好使用Yii的registe来注册,而不要使用<script>来引入.
<?php Yii::app ()->clientScript->registerCoreScript ( 'jquery' ); ?>
但bootstrap提示,
Uncaught Error: Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3
于是找到 Yii的核心script所在的位置:
D:\xampp\htdocs\yii-1.1.14.f0fee9_201505\framework\web\js\source
这个jquery.js 是1.8.3的,我复制了一个1.11.3的进来,一切正常了。
这个jquery的问题,真的要改变习惯了。以前也会因为jquery的多次引用而造成类似的这种莫名其妙的错误,试着改变jquery的位置或显式地引用一下它,有时也能解决,但终归不是正途。
(另:看了 jquery.yiigridview.js的源码,原来是Yii的作者写的。真牛~前端也很强嘛)
5. 待改进:
在actionAjaxAction中,$cond="id in (select record_id from {{tag_refer}} where tag_text_id in ($ids))"; 这个写法是不严谨的。它把所有的条件都当做“或”来处理了,没有实现前面分析的结论。这是小问题,以后再细细实现。
另一个是,在饭店信息的修改界面,我需要调用 CAnjoTag 的另一个方法,显示饭店的相关tag属性并可以修改。这同样是我的初衷之一,下一步会去做。