之前写了一个抓取程序,通过定时任务抓取资源并保存至数据库,之后通过输出RSS实现资源的自动下载。但是因为资源更新太快来不及下载,本地列表经常累计了50-60个待下载任务,导致服务器下载功能被完全阻塞。所以最近一直在想是否可以在原有的基础上增加一个标记功能,这样就只需要在Review的时候标记资源,然后只下载标记好的资源即可。
设想的标记功能应该有如下特点:不需要刷新页面、实时与数据库同步。
为了实现上述功能,需要对数据库、页面以及后端处理做一些改进。
数据库Structure
为了增加星标功能,需要在原有的数据结构末尾增加一列star,类型为tinyint(1),用于存储星标状态,默认为0,标记后为1。
前端DOM
为了实现不需要刷新页面,直接点击就可以标记的功能,在前端使用Ajax方法,交互内容设定为一个checkbox,当点击某个条目的checkbox后,获取该checkbox的value值,并传递给后端API与数据库进行同步,并反馈同步结果给页面。
为了在列表页第一次渲染时能够将星标状态输出至页面,除了增加checkbox的DOM节点外,还需要在页面输出时对项目的标记状态做判断,并视情况添加checked属性。
1 2 3 4 5 6 |
if($items[$i-1]['star'] == FALSE){ echo ' <input id="starCheck" type="checkbox" name="star" value="'.$items[$i-1]['part_number_1'].'-'.$items[$i-1]['part_number_2'].'" />';} else{ echo ' <input id="starCheck" type="checkbox" name="star" value="'.$items[$i-1]['part_number_1'].'-'.$items[$i-1]['part_number_2'].'" checked="checked" />';} |
由于之前的页面没有使用jQuery,所以需要在页面底部引入jQuery。为了之后修改的方便,Ajax实例也通过外部文件引入。
1 2 |
echo '<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>'; echo '<script type="text/javascript" src="get_torrentav_image_view.js"></script>'; |
外部Ajax实例,绑定点击事件至checkbox,并在发生点击事件后与后端API通信,更改数据库中的标记状态,并反馈结果至DOM
1 2 3 4 5 6 7 8 9 |
$(document).ready(function(){ $(":checkbox[name='star']").click(function(){ var itemPart = $(this).val(); var starStatus = $(this).prop("checked"); $.getJSON( "get_torrentav_image_view_ajax_star.php?itemPart="+itemPart, function( data ) { $( "span[alt="+itemPart+"]" ).append( "updated: "+data.star_status+""); }); }) }); |
这里有几个需要注意的地方
1.关于jQuery选择器 $(":checkbox[name='star']")
为了将click事件绑定至所有交互元素,这里使用了jQuery里的 :checkbox 选择器,这个选择器将搜索所有的checkbox,为了之后的兼容性考虑,在之前的DOM中,将标记checkbox的name设为了star,并在选择器中用 [name='star'] 来指定,这样后续再增加其他功能的checkbox的时候就不会搞混。
2.关于在jQuery选择器中使用变量 $( "span[alt="+itemPart+"]" )
jQuery选择器是可以使用变量的,比如点击了某个checkbox激活了Ajax事件,之后要给这个checkbox所在的项一个反馈,比如成功后在该项目旁边显示标记成功,那么就需要知道这个checkbox所在项目的DOM选择器,这时就可以将之前的checkbox传递过来的value值传递给选择器,直接就可以定位找到checkbox所在的项目节点。当然,项目属性里也要有和checkbox的value值相同的属性,这里用了alt。
3.关于checkbox状态 $(this).prop("checked")
jQuery中,如果要获得某个元素的属性,可以使用 .prop() 来获取。这里checkbox的状态,通过checked属性来定义。
4.关于反馈结果至DOM .append( ""+itemPart+"");
在与后端API通信后,反馈信息可以通过 .append() 添加至需要的位置。
5.关于jQuery调用JSON格式API的简单方法
jQuery有个简单的 $.getJSON 方法可以直接调用反馈为JSON的API,详见jquery.getjson
后端API
后端API的主要用途是与数据库进行通信,为了在数据库中更改标记状态,需要知道更改哪个条目,因为标记只有两种状态,所以无需知道标记的状态,只需要一个唯一的搜索关键词即可。
这里的关键词由之前的Ajax的Get操作传递,将传递一个组合关键词,在API中进行拆分并组合成SQL查询语句。
1 2 3 4 5 6 7 8 |
//analyse input if( isset($_GET["itemPart"])) $itemPart = $_GET["itemPart"]; else die(); $item_temp = explode("-",$itemPart); $part_number_1 = $item_temp[0]; $part_number_2 = $item_temp[1]; |
关于关键词的组合与拆分
关键词在Ajax提交阶段使用-分隔符组合,在API里使用 explode() 拆分
在拆分关键词之后,就可以组合SQL查询语句,查找对应条目,目的是查找该条目的标记状态
1 2 3 4 5 6 7 8 9 10 |
// search sql $results = $db_Selete->rawQueryOne ('select * from get_torrentav where part_number_1 LIKE ? AND part_number_2 LIKE ?', Array($part_number_1,$part_number_2)); if($results) $star_status = $results['star']; else die(); if($star_status) $star_status = 0; else $star_status = 1; |
关于rawQueryOne的使用
这是MysqliDb插件的一个自己组合SQL的方法,第一个参数是SQL语句字符串,其中变量用?占位,第二个参数是一个变量数组,用于替换第一个参数中的占位? 。这样就不会导致因为SQL里有变量而出现大量的单双引号。
另外因为由于关键词均来自数据库,所以反向查询必定是有结果的,所以如果数据库查询没有结果,说明该查询请求有问题,使用 die() 直接结束该进程,不反馈任何结果
在查找到该条目的信息后,下一步就是修改条目的标记信息,因为只有两种状态,所以只要将获得的标记状态取否即可。
1 2 3 4 5 6 7 |
// update sql $db_Update->where ('id', $results['id']); $data = Array ('star' => $star_status); if ($db_Update->update ('get_torrentav', $data)) $update_Status = 1; else $update_Status = -1; |
之后通过JSON输出反馈信息给JS,如果数据库操作成功,那么$update_Status的值为1,JS获得该值之后,就可以在页面上输出修改成功的信息
1 2 3 4 5 6 7 8 9 10 11 |
// structure json $ntc_return = array( "part_number_1" => "$part_number_1", "part_number_2" => "$part_number_2", "star_status" => "$star_status", "update_Status" => "$update_Status" ); // output json $outputArray = $ntc_return; echo $outputJSON = json_encode($outputArray); |
这里的数据库操作都使用了MysqliDb插件
遗留问题
页面与数据库同步问题
在数据库操作成功的情况下,页面的checkbox状态是与数据库同步的,但是在数据库操作失败的情况下,页面的checkbox选中状态可能就会与数据库有差异,这时候就出现页面与数据库不同步的问题。
这个问题的解决方法是在Ajax操作完成之后再进行一步判断,如果判断为数据库操作失败,那么应该通过 $(this).removeProp("checked"); 或者 $(this).prop('checked', true); 将checkbox重新设为之前的状态。
There are no comments yet