用YII实现多重查询(基于tag)

场景:
有一个饭店表 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:xampphtdocsyii-1.1.14.f0fee9_201505frameworkwebjssource
 
这个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属性并可以修改。这同样是我的初衷之一,下一步会去做。
 
 
 
原文地址:https://www.cnblogs.com/anjo/p/5917616.html