前陣子碰上一個關於產品多屬性搜索的問題,給實現了,現在拿出來跟大家探討探討,有什么好建議記得留下。
首先說明下,下面的實現,都僅僅是簡易版,純屬拋磚引玉而為,更強大的功能只能做相應的額外擴展才行。
本文略過分類、屬性、產品的創建過程的源碼解析,如下僅附上圖片說明。
圖一:創建分類(僅兩級)
圖二:創建屬性
圖三:創建產品(這里屬性是可多選的)
下面直奔產品多屬性搜索環節。
首先說明一下,本文所用的搜索程序首次加載會將庫中所有的產品顯示出來,然后通過搜索結果,隱藏掉不匹配的產品,顯示正確的產品,從而實現搜索效果。
下面是整個搜索過程的圖片展示。
圖四:搜索首次加載結果
圖五:父分類搜索(含子分類數據)
圖六:子分類搜索
圖七:分類+多屬性搜索(1)
圖八:分類+多屬性搜索(2)
圖九:分類+多屬性搜索(3)
圖十:分類+多屬性搜索(4)
圖十一:分類+多屬性搜索(5)
圖十二:分類+多屬性搜索(6)
圖十三:分類+多屬性搜索(7)
搜索程序前台源碼(displayProduct.php):
View Code
1 <?php 2 require 'product.model.php'; 3 ?> 4 <!DOCTYPE> 5 <html> 6 <head> 7 <title>產品展示搜索</title> 8 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 9 <meta http-equiv="Content-Language" content="zh-CN" /> 10 <script type="text/javascript" src="http://files.cnblogs.com/Zjmainstay/jquery-1.6.2.min.js"></script> 11 </head> 12 <body> 13 <style> 14 .product{float:left;width: 20%;} 15 body{font-size:14px;} 16 .p_price span{color:#FF0000;} 17 del{color:#C0C0FF;} 18 li{list-style:none;} 19 #searchProduct ul{clear:left;float:left;margin: 3px 0;} 20 .parentAttr{font-size:16px;font-weight:bold;float: left;margin-right: 10px;width: 100px;} 21 .searchAttr{border:1px solid #CFCFCF;height:20px;line-height:20px;float:left;cursor:pointer;margin: 0 3px;padding: 0 3px;} 22 #productList{margin: 0 auto;width: 960px;clear:left;} 23 .selectedAttr{background-color:#ABC;} 24 #searchBar{clear:left;float:left;margin-left: 40px;} 25 #categories span{margin-left:40px;} 26 #emptyProduct{display:none;} 27 </style> 28 <div id="container"> 29 <!-- 顯示分類 --> 30 <div id="categories"> 31 <span class="parentAttr">產品分類</span> 32 <?php echo Product::getCategoryList(); ?> 33 </div> 34 <!-- 顯示多屬性搜索選項 --> 35 <div id="searchProduct"> 36 <?php echo Product::getAttributeList(true); ?> 37 <input type="button" id="searchBar" value="搜索" /> 38 </div> 39 <!-- 顯示搜索結果 --> 40 <div id="productList"> 41 <?php echo Product::getProductList(); ?> 42 <div id="emptyProduct">沒有搜索到結果!</div> 43 </div> 44 </div> 45 <script type="text/javascript"> 46 $(document).ready(function(){ 47 // 搜索選項選中 48 $(".searchAttr").click(function(){ 49 $(this).toggleClass('selectedAttr'); 50 }); 51 //分類選定觸發搜索 52 $("#categoryList").change(function(){ 53 var catid = $.trim($(this).val()); 54 if(isNaN(catid)) return false; 55 56 $.ajax({ 57 url:'displayProduct.process.php', 58 type:'POST', 59 data:{catid:catid,type:'cate'}, 60 dataType:'json', 61 success:function(data){ 62 if(data.status == 1){ 63 $(".product").hide(); //先將所有產品隱藏,后面在通過搜索結果顯示相應的產品 64 if(data.products.length == 0){ //如果搜索結果為空 65 $("#emptyProduct").show(); //顯示“沒有搜索到結果!” 66 }else { //否則,隱藏“沒有搜索到結果!”,並逐個顯示搜索結果中的產品 67 $("#emptyProduct").hide(); 68 $.each(data.products,function(i){ 69 $("#product-"+data.products[i]).show(); 70 }); 71 } 72 }else { 73 alert(data.msg); 74 } 75 }, 76 error:function(msg){ 77 alert(msg); 78 } 79 }); 80 }); 81 //搜索按鈕觸發搜索 82 $("#searchBar").click(function(){ 83 if($(".selectedAttr").length == 0) { 84 $("#categoryList").change(); //若搜索屬性為空,則僅根據分類進行搜索(清除所有選中屬性的情況) 85 return false; 86 } 87 88 //進行搜索屬性拼接,同級屬性(OR)用','分割,不同級屬性(AND)用'|'分割 89 var searchString = ''; 90 var searchArray = []; 91 $("#searchProduct ul").each(function(){ 92 $(".selectedAttr",$(this)).each(function(){ 93 var attr = $.trim($(this).attr('attr')); 94 if(!isNaN(attr)) { 95 searchString += attr + ','; 96 searchArray.push(attr); 97 } 98 }) 99 searchString = searchString.substr(0,searchString.length-1) + '|'; 100 }); 101 searchString = searchString.substr(0,searchString.length-1); 102 103 if(searchString == '') return false; 104 105 var catid = $.trim($("#categoryList").val()); 106 if(isNaN(catid)) catid = 0; 107 108 $.ajax({ 109 url:'displayProduct.process.php', 110 type:'POST', 111 data:{searchString:searchString,catid:catid,type:'attr'}, 112 dataType:'json', 113 success:function(data){ 114 if(data.status == 1){ 115 $(".product").hide(); //先將所有產品隱藏,后面在通過搜索結果顯示相應的產品 116 if(data.products.length == 0){ //如果搜索結果為空 117 $("#emptyProduct").show(); //顯示“沒有搜索到結果!” 118 }else { //否則,隱藏“沒有搜索到結果!”,並逐個顯示搜索結果中的產品 119 $("#emptyProduct").hide(); 120 $.each(data.products,function(i){ 121 $("#product-"+data.products[i]).show(); 122 }); 123 } 124 }else { 125 alert(data.msg); 126 } 127 }, 128 error:function(msg){ 129 alert(msg); 130 } 131 }); 132 }); 133 }); 134 </script> 135 </body> 136 </html>
搜索程序后台源碼(displayProduct.process.php):
View Code
1 <?php 2 require 'product.model.php'; 3 switch($_POST['type']){ 4 case 'attr': 5 echo json_encode(Product::searchProductByAttribute(mysql_escape_string($_POST['searchString']),(int)$_POST['catid'])); 6 break; 7 8 case 'cate': 9 echo json_encode(Product::searchProductByCategory((int)$_POST['catid'])); 10 break; 11 default: 12 echo json_encode(array('status'=>0,'msg'=>'非法查詢類型!')); 13 break; 14 } 15 exit;
搜索程序后台數據處理層(product.model.php)源碼:
View Code
1 <?php 2 require '../db.php'; 3 class Product{ 4 public static function getCategoryList(){ 5 global $db; 6 $sql = "SELECT id,name,0 AS ordering,id AS 'groupcol' FROM `ju_categories` WHERE parent=0 7 UNION 8 SELECT id,name,ordering,parent AS 'groupcol' FROM `ju_categories` 9 WHERE parent IN( 10 SELECT id FROM `ju_categories` WHERE parent=0 11 ) ORDER BY `groupcol`,`ordering`"; 12 $result = mysql_query($sql,$db); 13 $categoryList = ''; 14 while($row = mysql_fetch_assoc($result)){ 15 if($row['id'] != $row['groupcol']) $pref = '-'; 16 else $pref = ''; 17 $categoryList .= '<option value="'.$row['id'].'">'.$pref.$row['name'].'</option>'; 18 } 19 20 $categoryList = '<select id="categoryList"><option value="0">Root</option>' . $categoryList . '</select>'; 21 return $categoryList; 22 } 23 24 public static function getAttributeList($search=false){ 25 global $db; 26 $sql = "SELECT id,name,0 AS ordering,id AS 'groupcol' FROM `ju_attributes` WHERE parent=0 27 UNION 28 SELECT id,name,ordering,parent AS 'groupcol' FROM `ju_attributes` 29 WHERE parent IN( 30 SELECT id FROM `ju_attributes` WHERE parent=0 31 ) ORDER BY `groupcol`,`ordering`"; 32 $result = mysql_query($sql,$db); 33 $attributeList = ''; 34 if($search){ 35 while($row = mysql_fetch_assoc($result)){ 36 if($row['id'] == $row['groupcol']) { 37 $attributeList .= '</ul><ul><li class="parentAttr">'.$row['name'].'</li>'; 38 }else { 39 $attributeList .= '<li attr="'.$row['id'].'" class="searchAttr">'.$row['name'].'</li>'; 40 } 41 } 42 if(stripos($attributeList,'</ul>') === 0) $attributeList = substr($attributeList,5); 43 $attributeList .= '</ul>'; 44 }else { 45 while($row = mysql_fetch_assoc($result)){ 46 if($row['id'] != $row['groupcol']) { 47 $attributeList .= '<option value="'.$row['id'].'">-'.$row['name'].'</option>'; 48 } 49 else { 50 $attributeList .= '<option value="'.$row['id'].'" class="parentAttr">'.$row['name'].'</option>'; 51 } 52 } 53 54 $attributeList = '<select id="attributeList"><option value="0" class="root">-請選擇添加-</option>' . $attributeList . '</select>'; 55 } 56 return $attributeList; 57 } 58 59 public static function save($data = array()){ 60 global $db; 61 $name = mysql_escape_string($data['name']); 62 $sku = mysql_escape_string($data['sku']); 63 $catid = (int)$data['catid']; 64 $origPrice = mysql_escape_string($data['origPrice']); 65 $price = mysql_escape_string($data['price']); 66 $stock = mysql_escape_string($data['stock']); 67 $attrs = implode(',',(array)$data['attrs']); 68 69 if(empty($name)) { 70 echo '分類名不能為空!'; 71 exit; 72 } 73 74 $sql = "INSERT INTO `ju_products`(`id`,`name`,`sku`,`catid`,`origPrice`,`price`,`stock`,`attributes`,`created_on`)" 75 ." VALUES(null,'$name','$sku','$catid','$origPrice','$price','$stock','$attrs',now())"; 76 if(mysql_query($sql,$db)){ 77 $productId = mysql_insert_id($db); 78 $sql = "INSERT INTO `ju_product_attributes`(`product_id`,`attribute_id`) VALUES"; 79 foreach($data['attrs'] as $attr){ 80 $sql .="('$productId','$attr'),"; 81 } 82 $sql = rtrim($sql,','); 83 mysql_query($sql,$db); 84 return true; 85 }else { 86 return false; 87 } 88 89 } 90 91 public static function getProductList(){ 92 global $db; 93 $productTpl = <<<TPL 94 <div class="product" id="product-%d"> 95 <div class="p_image"><img src="%s" alt="%s" width="150" height="200"/></div> 96 <div class="p_title">%s</div> 97 <div class="p_price"> 98 <span>¥%.2f</span> 99 <del>¥%.2f</del> 100 </div> 101 </div> 102 TPL; 103 $sql = "SELECT id,name,price,origPrice FROM `ju_products` ORDER BY id"; 104 $result = mysql_query($sql,$db); 105 $productList = ''; 106 while($row = mysql_fetch_assoc($result)){ 107 $productList .= vsprintf($productTpl,array($row['id'],'#',$row['name'],$row['name'],$row['price'],$row['origPrice'])); 108 } 109 return $productList; 110 } 111 112 public static function searchProductByCategory($catid){ 113 global $db; 114 if(!isset($catid)) return array('status'=>0,'msg'=>'分類不能為空!'); 115 116 $categories = self::getSubCategories($catid); //默認遞歸包含子分類 117 $sql = "SELECT id FROM `ju_products` WHERE catid IN(".implode(',',$categories).") ORDER BY id"; 118 $result = mysql_query($sql,$db); 119 $productArray = array(); 120 while($row = mysql_fetch_assoc($result)){ 121 array_push($productArray,$row['id']); 122 } 123 return array('status'=>1,'products'=>$productArray); 124 } 125 public static function searchProductByAttribute($searchString,$catid=0){ 126 global $db; 127 if(empty($searchString)) return array('status'=>0,'msg'=>'搜索條件不能為空!'); 128 129 if(empty($catid)) $where = array(); 130 else $where = array("p.catid IN(".implode(',',self::getSubCategories((int)$catid)).")"); 131 $ands = explode('|',$searchString); 132 foreach($ands as $and){ 133 $andString = ""; 134 $ors = explode(',',$and); 135 foreach($ors as $or){ 136 $andString .= "LOCATE(',{$or},',pas.attribute_ids) OR "; 137 } 138 $andString = '('.substr($andString,0,strlen($andString)-4).')'; //-4去掉末尾“ OR ” 139 $where[] = $andString; 140 } 141 142 $sql = " 143 SELECT p.id FROM `ju_products` as p 144 INNER JOIN ( 145 SELECT product_id,concat(',,',group_concat(attribute_id),',,') as attribute_ids FROM `ju_product_attributes` GROUP BY product_id 146 ) as pas ON p.id=pas.product_id 147 WHERE ".implode(' AND ',$where)." 148 group by p.id 149 "; 150 $result = mysql_query($sql,$db); 151 $productArray = array(); 152 while($row = mysql_fetch_assoc($result)){ 153 array_push($productArray,$row['id']); 154 } 155 return array('status'=>1,'products'=>$productArray,'sql'=>$sql); 156 } 157 158 public static function getSubCategories($pid,$recursive=true){ 159 global $db; 160 $pid = (int)$pid; 161 $sql = "SELECT id FROM `ju_categories` as cate WHERE cate.parent=".$pid; 162 $result = mysql_query($sql,$db); 163 $subCategories = array($pid); //加入當前分類 164 if($recursive){ 165 while($row = mysql_fetch_row($result)){ 166 $subCategories = array_merge($subCategories,self::getSubCategories($row[0])); 167 } 168 } 169 return $subCategories; 170 } 171 } 172 173 //End_php
數據庫連接文件(db.php)源碼:
View Code
1 <?php 2 static $connect = null; 3 if(!isset($connect)){ 4 $connect = mysql_connect("localhost","Zjmainstay","") or die('無法連接數據庫!'); 5 mysql_select_db("test",$connect) or die('無法連接到指定數據庫!'); 6 mysql_query("SET NAMES UTF8",$connect); 7 8 $db = $conn = $connect; 9 } 10 11 //End_php
重點:多屬性搜索方法
1 public static function searchProductByAttribute($searchString,$catid=0){ 2 global $db; 3 if(empty($searchString)) return array('status'=>0,'msg'=>'搜索條件不能為空!'); 4 5 if(empty($catid)) $where = array(); 6 else $where = array("p.catid IN(".implode(',',self::getSubCategories((int)$catid)).")"); //WHERE子句數組,分類搜索(getSubCategories方法默認含子分類) 7 $ands = explode('|',$searchString); //分離不同層級的屬性,如品牌與價格范圍 8 foreach($ands as $and){ 9 $andString = ""; 10 $ors = explode(',',$and); //分離同一層級的多個屬性,如品牌中的"HP","華碩","聯想"等 11 foreach($ors as $or){ 12 $andString .= "LOCATE(',{$or},',pas.attribute_ids) OR "; //對每個屬性進行LOCATE定位,定位目標為各個產品所有屬性組成的屬性串,格式為:,,26,33,3,21,, 13 } 14 $andString = '('.substr($andString,0,strlen($andString)-4).')'; //-4去掉末尾“ OR ” 15 $where[] = $andString; //加入WHERE子句數組中 16 } 17 //使用group_concat(attribute_id)拼接每個產品的所有屬性,用於WHERE子句進行屬性LOCATE搜索 18 $sql = " 19 SELECT p.id FROM `ju_products` as p 20 INNER JOIN ( 21 SELECT product_id,concat(',,',group_concat(attribute_id),',,') as attribute_ids FROM `ju_product_attributes` GROUP BY product_id 22 ) as pas ON p.id=pas.product_id 23 WHERE ".implode(' AND ',$where)." //使用AND拼接WHERE子句數組 24 group by p.id 25 "; 26 $result = mysql_query($sql,$db); 27 $productArray = array(); 28 while($row = mysql_fetch_assoc($result)){ 29 array_push($productArray,$row['id']); //將查得產品id加入$productArray數組中,響應請求。 30 } 31 return array('status'=>1,'products'=>$productArray,'sql'=>$sql); 32 }
本文到此結束,謝謝大家耐心閱讀!
