場景:
有一個飯店表 restaurant,存放全部飯店記錄。我須要一個功能,將飯店按照不一樣的條件進行多重查詢。就象這樣:
氛圍:浪漫 / 商務會談 / 茅草屋
菜系:川菜 / 魯菜 / 家常菜...
區域:東區 / 西區 / 南區 / 北區...
我點擊一個或多個條目,則下方自動刷出符合條件的飯店來。
例如我點擊了 浪漫、商務會談、魯菜,因爲浪漫、商務會談是同屬於氛圍的,應該認爲用戶想查找的是「或」的關係,無論出現兩者的哪個,都屬於查詢範圍;而魯菜是屬於菜系的,那麼與前兩個條件來講,應該是「與」的關係,即:用戶想查(氛圍==浪漫 || 氛圍==商務會談)&& (菜系==魯菜)的全部飯館的集合。
這裏有一個邏輯上的小別扭:若是我氛圍選了浪漫,菜系不選,那麼是否是要選擇(氛圍==浪漫)&& (菜系==none)嗎?這樣的結果確定爲空。實際上,不選菜系表示對菜系無要求,因此範圍應該是(氛圍==浪漫)&& (菜系==全部)。
考慮到這個功能比較常見,打算作一個通用的東西出來
感受這是個難度比較大的功能,來作一下吧。
一、創建幾個表: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表中的記錄的多對多的對應關係
二、建立 Model。這裏,TagTablename裏實現了tag_titles,TagTitle裏面實現了tag_texts。
三、寫實現的類 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;
}
四、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屬性並能夠修改。這一樣是個人初衷之一,下一步會去作。