<?php
/**
 * 用户评论的服务层实现
 * 
 * 主要实现删除，插入等业务
 * @author xiaoxia.xuxx
 */
!defined('M_P') && exit('Forbidden');

/**
 * 用户评论服务层
 * @author xiaoxia.xuxx
 */
class PW_CommentService {
	
	/**
	 * 评论内容表DAO
	 * @access private
	 * @var PW_Dianpu_CommentContentsDB
	 */
	var $_commentContentsDB;
    
	/**
	 * 构造函数（兼容PHP4）
	 */
	function PW_CommentService() {
		$this->__construct();
	}
    
	/**
	 * 构造函数（兼容PHP5）
	 */
	function __construct() {
		$this->_commentContentsDB = $this->_getCommentcontentsDB();
	}
	
	/**
	 * 添加评论
	 * 
	 * 先插入主表
	 * rootid或者是parentid决定是否是一级评论如果有值 则不是根级
	 * 
	 * @param array $data array('字段' => '值') 新评论的信息 
	 * @return mixed boolean 返回插入的结果  | array(false, 'message')返回过滤的结果
	 */
	function addComment($data) {
		if (!$data) return false;
		$data = $this->_filterInsertParam($data);
		if($data[0] === false) return $data;
		$intoContents = $this->_filterInsertData($data, 'contents');
		if (!$intoContents) return false;
		$data['commentid'] = $this->_commentContentsDB->insert($intoContents);
		if (!$data['commentid']) return false;
		if ($data['rootid'] && intval($data['rootid']) != 0) {
			return $this->_addCommentReply($data);
		} else {
			return $this->_addRootComment($data);
		}
	}
	
	/**
	 * 删除一条评论(针对三级的情况)
	 * 
	 * 思路：先去二级评论表查找该条数据，
	 * 如果没有记录，表明该被删的评论是属于根级数据表，直接调用私有函数_deleteByRootids来级联删除其下所有评论及本身的评论,并返回影响的行数
	 * 如果有记录，表明该被删除的评论是属于二级评论或是三级评论，调用私有函数_deleteByParentids来删除该评论下的子评论
	 * 
	 * @param integer $id 需要删除的评论ID
	 * @param integer $objectid 需要删除的该条评论的所在的店铺(或对象)ID
	 * @return mixed boolean (false:失败) | integer 返回删除影响的行数
	 */
	function deleteComment($id, $objectid) {
		if (!$id || !$objectid) return false;
		$commentsDB2 = $this->_getCommentReplysDB();
		if (!$commentsDB2->getRootidByCommentid($id, $objectid)) {
			return $this->_deleteByRootids(array($id), $objectid);
		} else {
			return $this->_deleteByParentids(array($id), $objectid);
		}
	}
	
	/**
	 * 站长删除一条评论(针对三级的情况)
	 * 
	 * @param integer $id 需要删除的评论ID
	 * @return mixed boolean (false:失败) | integer 返回删除影响的行数
	 */
	function delCommentForAdmin($id) {
		if (!$id) return false;
		$commentsDB2 = $this->_getCommentReplysDB();
		if (!$commentsDB2->getRootidByCommentid($id)) {
			return $this->_deleteByRootidsForAdmin(array($id));
		} else {
			return $this->_deleteByParentidsForAdmin(array($id));
		}
	}
	
	/**
	 * 删除多条评论(针对三级的情况)
	 * 
	 * 思路：
	 * 先去调用私有函数_filterDeleteRootID来过滤被删除的评论列表中的所有根级评论ID
	 * 如果有根级评论，调用私有函数_deleteByRootids来级联删除其下所有评论及本身的评论,并返回影响的行数
	 * 用php内置函数array_diff来将原始的ID列表和被查询出来的根级评论的列表做一个差集，该差集中集合了所有要被删除的二级或是三级评论
	 * 如果没有根级评论，该差集将是原始的评论ID列表，
	 * 如果差集元素为0，则说明该被删除的ID列表都是根级评论，则直接返回
	 * 如果差集元素不为0，调用私有函数_deleteByParentids来删除该评论下的子评论
	 * 
	 * @param array $ids 需要删除的评论ID
	 * @param integer $dianpuid  需要删除的评论所在的店铺(或者objectid)ID
	 * @return mixed boolean (false:失败) | integer  返回受影响的行数
	 */
	function deleteComments($ids, $objectid) {
		if (!S::isArray($ids) || !$objectid) return false;
		$delNum = 0;
		$rootIDs = $this->_filterDeleteRootID($ids);
		(count($rootIDs) != 0 ) && $delNum = $this->_deleteByRootids($rootIDs, $objectid);
		$secondIDs = array_diff($ids, $rootIDs);
		if (count($secondIDs) != 0) {
			$delNum += $this->_deleteByParentids($secondIDs, $objectid);
		}
		return $delNum;
	}
	/**
	 * 站长删除多条评论(针对三级的情况)
	 * 
	 * @param array $ids  需要删除的评论ID列表
	 * @return mixed boolean (false:失败) | integer  返回受影响的行数
	 */
	function delCommentsForAdmin($ids) {
		if (!S::isArray($ids)) return false;
		$delNum = 0;
		$rootIDs = $this->_filterDeleteRootID($ids);
		(count($rootIDs) != 0 ) && $delNum = $this->_deleteByRootidsForAdmin($rootIDs);
		$secondIDs = array_diff($ids, $rootIDs);		
		if (count($secondIDs) != 0) {
			$delNum += $this->_deleteByParentidsForAdmin($secondIDs);
		}	
		return $delNum;
	}
	
	/**
	 * 因为商店或是商品的删除而需要级联删除评论 （给商品和店铺管理提供接口）
	 * 
	 * @param integer $objectid 店铺ID
	 * @param integer $typeid 商品ID
	 * @return mixed boolean (false:失败) | integer 返回受影响的行数
	 */
	function deleteCommentByObject($objectid, $typeid = 0) {
		if (!$objectid) return false;
		$commentDB1 = $this->_getCommentsDB();
		$rootID = $commentDB1->getRootidByObject($objectid, $typeid);
		$rootidList = $this->_filterArray($rootID, 'commentid');
		$commentDB1->deletes($rootidList);
		$commentDB2 = $this->_getCommentReplysDB();
		$commentDB2->deletesByRootID($rootidList);
		return $this->_commentContentsDB->deleteByObject($objectid, $typeid);
	}		
	
	/**
	 * 根据条件搜索评论(后台搜索 包括【站长后台和商家后台】)
	 * 
	 * @param array $params 搜索条件
	 * @param integer $start 开始查询的记录
	 * @param integer $perpage 总共返回的记录条数
	 * @return array array(array('字段' => '值'),...) 返回查询到的结果
	 */
	function getSearchCommentList($params, $start = 0, $perpage = 20) {
		$_tempList = $this->_commentContentsDB->search($params, $start, $perpage);
		return $this->_buildResultList($_tempList);
	}	

	/**
	 * 前台显示获取第一级评论信息列表
	 * 
	 * @param integer $objectid  店铺ID
	 * @param integer $typeid  商品ID  如果为0，则只取店铺的评论，如果不为0，则取该店铺中该商品的评论
	 * @param integer $start 分页开始的位置
	 * @param integer $perpage 每页显示的条数
	 * @return mixed boolean (false:失败) | array array(array('字段' => '值'),...) 返回评论列表
	 */
	function getFrontCommentList($objectid, $typeid = 0, $start = 0, $perpage = 20) {
		if (!$objectid) return array();
		$frontRootidList = $this->_commentContentsDB->getRootidByObject($objectid, $typeid, $start, $perpage);
		$IDList = $this->_filterArray($frontRootidList, 'commentid');
		$_tempList = $this->_commentContentsDB->getFrontCommentsInfoList($IDList);
		return $this->_buildResultList($_tempList);
		
	}
	
	/**
	 * 根据店铺ID或是商品ID获得该类的所有根级评论的条数
	 * 
	 * @param integer $objectid 店铺（对象）ID
	 * @param integer $typeid 商品ID
	 * @return integer 返回该店铺（或者该店铺下的该商品）的评论条数
	 */
	function getFrontCount($objectid, $typeid = 0) {
		if (!$objectid) return 0;
		return $this->_commentContentsDB->getFrontCount($objectid, $typeid);
	}
	
	/**
	 * 根据评论的ID列表，获取该条评论的子级评论详细信息表根据时间正序来
	 * 
	 * @param array $rootidList 根级评论的ID列表
	 * @return array array('rootid' =>array(array('字段'=>'值'),...),...)返回以根级ID为节点的子级评论数据集
	 */
	function getChildCommentList($rootidList){
		if (!S::isArray($rootidList)) return array();
		$_commentList = $this->_commentContentsDB->getChildCommentInfoList($rootidList);
		return $this->_buildCommentList($_commentList, 'rootid');
	}
	
	/**
	 * 根据对象ID和商品ID获得该属性下所有的店主回复(根据时间正序排序)
	 * 
	 * @param integer $objectid 店铺(或对象）ID
	 * @param integer $typeid 商品ID
	 * @return array array('parentid' =>array(array('字段'=>'值'),...),...)返回已父节点的数剧集
	 */
	function getReplyCommentList($objectid, $typeid = 0) {
		if (!$objectid) return array();
		$_commentList = $this->_commentContentsDB->getReplyCommentList($objectid, $typeid);
		return $this->_buildCommentList($_commentList, 'parentid');
	}
	
	/**
	 * 根据输入的参数列表返回记录总数
	 * 
	 *@param array $params  需要筛选的条件列表   array('content' => '评论内容',  
	 *												'username' => '评论发表者',  
	 *           									'startdate' => '查询评论的开始时间 ', 
	 *           									'enddate' => '查询评论的结束时间' ,
	 *          									'isanswer' = > '是否是回复，如果是1则是回复',
	 *          									'state' => '该条评论是否已经被回复，1为已经被回复',
	 *           									'objectid' => '筛选评论列表的店铺（或是对象）的ID',
	 *           									'typeid' => '筛选评论列表的商品ID',
	 *     										)
	 * @return integer 返回符合筛选结果的条数
	 */
	function countTotals($params) {
		return $this->_commentContentsDB->count($params);
	}
	
	/**
	 * 根据输入的第二个参数来构造结果集列表,返回的结果集中将以第二个参数为节点形成的数组(店铺和商品的评论展示)
	 * 
	 * @access private
	 * @param array $commentList array(array('字段','值'),...)评论信息列表
	 * @param integer $parentid 父级评论的ID
	 * @return array array('id' =>array(array('字段'=>'值'),...),...)返回聚合之后的数组信息
	 */
	function _buildCommentList($commentList, $parentid) {
		$_temp = array();
		$parentid = (!in_array($parentid, array('rootid', 'parentid'))) ? 'rootid' : trim($parentid);
		foreach ($commentList as $key => $value) {
			$_temp[$value[$parentid]][] = $this->_buildResultValue($value);
		}
		return $_temp;
	}
	
	/**
	 * 构造结果集列表函数 用于生成一维的列表(前后台管理页面搜索)
	 * 
	 * @access private
	 * @param array $commentList array(array('字段','值'),...) 评论的信息列表
	 * @return array array(array('字段' => '值'),...) 返回组装成的结果集
	 */
	function _buildResultList($commentList) {
		$_temp = array();
		foreach ($commentList as $key => $value) {
			$_temp[$key] = $this->_buildResultValue($value);
		}
		return $_temp;
	}
	
	/**
	 * 组装过滤每个输出字段值
	 * 
	 * @access private 
	 * @param array $comment array('字段' => '值') 评论详细信息列表
	 * @return array array('字段' => '值') 输出过滤组装后的数据 
	 */
	function _buildResultValue($comment) {
		require_once(R_P . 'require/bbscode.php');//装载函数 showface
	    if (!S::isArray($comment)) return array();
	    $comment['postdate'] = $comment['postdate'] ? get_date($comment['postdate']) : '-';
	    $frontURL = 'index.php?m=dianpu&id=' . $comment['objectid'] . '&productid=' . $comment['typeid'];
	    $frontURL .= ($comment['typeid'] != 0) ? "&c=product&a=detail" : "&c=info";
	    $comment['frontURL'] = $frontURL;
	    ($comment['icon']) ? list($comment['faceurl']) = showfacedesign($comment['icon'], 1, 'm') : $comment['faceurl'] = "images/face/none.gif";
	    $comment['shortcontent'] = substr_cut($comment['content'], 45, true);
	    ($comment['shortcontent'] == "") && $comment['shortcontent'] = substr_cut($comment['title'], 20);
	    (strpos($comment['content'],'[s:') !== false) && $comment['content'] = showface($comment['content']);
	   	return $comment;
	}
	
	/**
	 * 从ID列表中过滤出所有根级ID(用于删除多条评论的时候)
	 * 
	 * @access private
	 * @param array $ids 需要过滤的所有评论ID列表
	 * @return array 返回输入参数中所有根级的ID列表
	 */
	function _filterDeleteRootID($ids) {
		$commentsDB1 = $this->_getCommentsDB();		
		$rootIDs = $commentsDB1->filterDeleteRootID($ids);
		return $this->_filterArray($rootIDs, 'commentid');
	}
	
	/**
	 * 前台辅助函数《删除某父级评论及其子评论》
	 * 
	 * 思路：
	 * 通过父级评论先获得所有该父级评论的子评论ID列表，
	 * 将父级评论及子评论的ID进行合并，得到删除的ID列表，进行删除
	 * 
	 * @access private
	 * @param array $commentids 评论ID
	 * @param integer $objectid 店铺（或对象）ID
	 * @return integer 返回影响的行数
	 */
	function _deleteByParentids($commentids, $objectid) {
		if (!S::isArray($commentids) || !$objectid) return false;
		$commentsDB1 = $this->_getCommentsDB();
		$commentsDB2 = $this->_getCommentReplysDB();
		$childCommentIDList = $commentsDB2->getCommentidByParentid($commentids);
		$childIDs = $this->_filterArray($childCommentIDList, 'commentid');
		$delIDs = array_merge($commentids, $childIDs);
		$commentsDB2->deletes($delIDs, $objectid);
		return $this->_commentContentsDB->deletes($delIDs, $objectid);
	}
	
	/**
	 * 站长后台根据父级评论ID删除所有子级及该父级的评论
	 * 
	 * @access private
	 * @param array $commentids 父级评论ID列表
	 * @return integer 返回影响的行数
	 */
	function _deleteByParentidsForAdmin($commentids) {
		if (!S::isArray($commentids)) return false;
		$commentsDB1 = $this->_getCommentsDB();
		$commentsDB2 = $this->_getCommentReplysDB();
		$childCommentIDList = $commentsDB2->getCommentidByParentid($commentids);
		$childIDs = $this->_filterArray($childCommentIDList, 'commentid');
		$delIDs = array_merge($commentids, $childIDs);
		$commentsDB2->delsForAdmin($delIDs);
		return $this->_commentContentsDB->delsForAdmin($delIDs);
	}
	
	/**
	 * 辅助函数--删除根级评论及其所有子评论
	 * 
	 * 思路：
	 * 根据传入的根类评论ID获得所有其他下的子类评论ID，并将该子类ID和根类ID进行合并得到所有需要被删除的评论ID列表
	 * 分别调用deletes 等对应的删除函数删除评论
	 * 
	 * @access private
	 * @param array $rootIDs 根级评论ID
	 * @param integer $objectid 店铺（或对象）ID
	 * @return integer 返回受影响的行数
	 */
	function _deleteByRootids($rootIDs, $objectid) {
		if (!S::isArray($rootIDs) || !$objectid) return false;
		$commentsDB1 = $this->_getCommentsDB();
		$commentsDB2 = $this->_getCommentReplysDB();
		$commentsID = $commentsDB2->getCommentidByRootid($rootIDs);
		$childCommentID = $this->_filterArray($commentsID, 'commentid');
		$commentsDB2->deletesByRootID($rootIDs, $objectid);	
		$commentsDB1->deletes($rootIDs, $objectid);		
		$commentID = array_merge($rootIDs, $childCommentID);
		return $this->_commentContentsDB->deletes($commentID, $objectid);
	}
	
	/**
	 * 后台辅助函数··功能同上
	 * 
	 * @access private
	 * @param array $rootIDs 根级评论ID列表
	 * @return integer 返回受影响的行数
	 */
	function _deleteByRootidsForAdmin($rootIDs) {
		if (!S::isArray($rootIDs)) return false;
		$commentsDB1 = $this->_getCommentsDB();		
		$commentsDB2 = $this->_getCommentReplysDB();
		$commentsID = $commentsDB2->getCommentidByRootid($rootIDs);
		$childCommentID = $this->_filterArray($commentsID, 'commentid');
		$commentsDB2->deletesByRootIDForAdmin($rootIDs);	
		$commentsDB1->delsForAdmin($rootIDs);	
		$commentID = array_merge($rootIDs, $childCommentID);
		return $this->_commentContentsDB->delsForAdmin($commentID);
	}
	
	/**
	 * 根据输入的参数重组数组
	 * 
	 * @access private
	 * @param array $params   待重组的数组
	 * @param string $key     需要重组输出的数组的键值（如果是数字索引键，则为0)
	 * @param array array('','',) 返回重组后的一维数组，并且以数字键索引
	 */
	function _filterArray($params, $key = 0) {
		if (!$params) return array();
		if (!S::isArray($params)) return array();
		$clear = array();
		foreach($params as $value) {
			$clear[] = $value[$key];
		}
		return $clear;
	}
	
	/**
	 * 对插入数据表的数据进行合法字段过滤
	 *  
	 * @access private
	 * @param array $fieldData 插入的数据集
	 * @param string $table array('contents', 'reply1','reply2') 过滤的数据表
	 * @return mixed boolean (false:失败) | array 返回过滤后的合法数据集合
	 */
	function _filterInsertData($fieldData, $table = 'contents') {
		if (!S::isArray($fieldData) || !S::inArray($table, array('contents', 'reply1', 'reply2'))) return false;
		$allowField = array();
		$allowField['contents'] = array('commentid', 'title', 'content', 'objectid', 'typeid', 'uid', 'username', 'postdate', 'isanswer', 'state');
		$allowField['reply1'] = array('commentid', 'objectid', 'typeid', 'postdate');
		$allowField['reply2'] = array('commentid', 'parentid', 'rootid', 'postdate', 'objectid');
		$clearData = array();
		foreach ($fieldData as $key => $value) {
			if (in_array($key, $allowField[$table])) $clearData[$key] = $value;
		}
		return $clearData;		
	}
	
	/**
	 * 过滤插入的数据
	 * 
	 * @access private
	 * @param array $info 对插入的参数进行过滤
	 * @return array array('字段' => '值' )
	 */
	function _filterInsertParam($info) {
		$wordsfb = L::loadClass('FilterUtil', 'filter');//敏感词过滤		
		$clean = array();
		if (!$info['uid'])  return array(false, '非法操作'); 
		$clean['uid'] = $info['uid'];
		if (!$info['username'])  return array(false, '非法操作');
		$clean['username'] = trim($info['username']);
		if (!$info['content'] || $info['content'] == '')  return array(false, '请输入内容!');
		if (($banword = $wordsfb->comprise($info['content'], false)) !== false) return array(false, "警告：该评论内容含有非法词 <font color=red>" . $banword . "</font>！");
		$_temp = trim(preg_replace('/\[s:\w*\]|(&nbsp;)/', '', $info['content']));//过滤表情及html空格实体
		if(strlen(trim($_temp)) > 1000 ) return array(false, '输入的内容不能超过1000个字节！');
		$clean['content'] = trim($info['content']);
		if ($info['title'] && $wordsfb->comprise($info['title'], false) !== false) return array(false, "警告：该评论标题含有非法词 <font color=red>" . $banword . "</font>！");
		($info['title']) ? $clean['title'] = trim($info['title']) : $clean['title'] = $clean['username'] . ':';//登录用户名;
		if (!$info['objectid'])  return array(false, '非法操作');
		$clean['objectid'] = trim($info['objectid']);
		(!$info['typeid']) ? $clean['typeid'] = 0 : $clean['typeid'] = trim($info['typeid']);
		(!$info['parentid']) ? $clean['parentid'] = 0 : $clean['parentid'] = trim($info['parentid']);
		isset($info['rootid']) ? $clean['rootid'] = intval($info['rootid']) : $clean['rootid'] = 0;
		($clean['parentid'] != 0 && $clean['rootid'] == 0) && $clean['rootid'] = $clean['parentid'];
		($clean['rootid'] !=0 && $clean['parentid'] == 0) && $clean['parentid'] = $clean['rootid'];
		$clean['postdate'] = trim($info['postdate']);
		(!$info['isanswer'] || !in_array($info['isanswer'], array('0', '1'))) ? $clean['isanswer'] = 0 : $clean['isanswer'] = 1;
		return $clean;
	}

	/**
	 * 添加评论--二级评论信息表
	 * 
	 * （需要处理如果该评论是店主的回复的时候，修改该条评论的父级评论的状态）
	 * @access private 
	 * @param array $data 二级评论的信息添加
	 * @return boolean 返回插入处理的结果
	 */
	function _addCommentReply($data) {
		$tempIntoComments = $this->_filterInsertData($data, 'reply2');
		if (!$tempIntoComments) return false;
		$tempDB = $this->_getCommentReplysDB();
		$tempDB->insert($tempIntoComments);
		if ($data['isanswer'] == 1) {
			$this->_commentContentsDB->changeState($data['parentid']);
		}
		return true;
	}
	
	/**
	 * 添加评论--根级评论信息添加
	 * 
	 * @access private
	 * @param array $data  根级评论的信息添加
	 * @return boolean 返回插入处理的结果
	 */
	function _addRootComment($data) {
		$tempIntoComments = $this->_filterInsertData($data, 'reply1');
		if (!$tempIntoComments) return false;
		$tempDB = $this->_getCommentsDB();
		$tempDB->insert($tempIntoComments);	
		return true;
	}
	
	/**
	 * 获得PW_Dianpu_CommentContentsDB的类实例
	 * 
	 * @access private
	 * @return PW_Dianpu_CommentContentsDB 
	 */
	function _getCommentcontentsDB() {
		return DP::LoadDB('dianpu_commentcontents');
	}
	
	/**
	 * 获得PW_Dianpu_CommentsDB的类实例
	 * 
	 * @access private
	 * @return PW_Dianpu_CommentsDB 
	 */
	function _getCommentsDB() {
		return DP::LoadDB('dianpu_comments');
	}
	
	/**
	 * 获得PW_Dianpu_CommentReplysDB的类实例
	 * 
	 * @access private
	 * @return PW_Dianpu_CommentReplysDB 
	 */
	function _getCommentReplysDB() {
		return DP::LoadDB('dianpu_commentreplys');
	}	
}