php无限级评论嵌套实现代码


Posted in PHP onApril 18, 2018

我在设计BB的过程中,也一直在思考是否可以不通过递归来实现无限级分类的结构展现和父子结构查找,因为如果不对这里的算法进行优化后果可能是致命的!试想一下,一篇文章如果评论数为300,按正常的递归算法,至少就得查询数据库301次,而且还是在没有任何嵌套的情况下,如果有过一两级嵌套或者评论数过1000,那数据库不是直接宕掉?
而实际上,PHP强大的数组处理能力已经能帮助我们快速方便的解决这个问题。下图为一个无限级分类的

数据库结构:

IDparentID newsID commts
108文章ID为8的评论
21 8对ID为1的评论的回复
328对ID为2的评论的回复

要在前台嵌套式的展现文章编号8的评论,其实我们只用查询一次数据库,即“SELECT * FROM TABLE WHERE newsID=8”,而把后期的递归工作交给强大的PHP数组来完成。这里可能涉及的问题就是数组的结构关系的重组,即将所有停留在一级分类上的评论全部放到自己的parentID下,形成children项。
下面将BBComment类中这块的代码粘贴出来,希望与大家分享下我的思路,也希望大家能够提出更好更有效率的算法。

方法一

/** 
 * 按ID条件从评论数组中递归查找 
 * 
 */ 
function getCommentsFromAryById($commtAry, $id) 
{ 
 if ( !is_array($commtAry) ) return FALSE; 
 foreach($commtAry as $key=>$value) { 
  if ( $value['id'] == $id ) return $value; 
  if ( isset($value['children']) && is_array($children) ) $this->getCommentsFormAryById($value['children'], $id); 
 } 
} 
/** 
 * 追加 子评论 到 主评论 中,并形成children子项 
 * 
 * @param array $commtAry 原评论数据引用 
 * @param int $parentId 主评论ID 
 * @param array $childrenAry 子评论的值 
 */ 
function addChildenToCommentsAry($commtAry, $parentId, $childrenAry) 
{ 
 if ( !is_array($commtAry) ) return FALSE; 
 
 foreach($commtAry as $key=>$value) { 
  if ( $value['id'] == $parentId ) { 
   $commtAry[$key]['children'][] = $childrenAry; 
   return TRUE; 
  } 
  if ( isset($value['children']) ) $this->addChildenToCommentsAry($commtAry[$key]['children'], $parentId, $childrenAry); 
 } 
} 
 $result = $this->BBDM->select($table, $column, $condition, 0, 1000); 
 
 /* 开始进行嵌套评论结构重组 */ 
 array_shift($result); 
 $count = count($result); 
 $i  = 0; 
 while( $i<$count ) { 
  if ( '0' != $result[$i]['parentId'] ) { 
   $this->addChildenToCommentsAry($result, $result[$i]['parentId'], $result[$i]); 
   unset($result[$i]); 
  } 
  $i++; 
 } 
 $result = array_values($result); 
 /* 重组结束 */

实现方法二

核心代码摘自WordPress

<?php
$comments = array (
  array (
    'id' => '3',
    'parent' => '0'
  ),
  array (
    'id' => '9',
    'parent' => '0'
  ),
  array (
    'id' => '1',
    'parent' => '3'
  ),
  array (
    'id' => '2',
    'parent' => '3'
  ),
  array (
    'id' => '5',
    'parent' => '1'
  ),
  array (
    'id' => '7',
    'parent' => '1'
  )
);
function html5_comment($comment) {
  echo '<li>';
  echo 'id:', $comment['id'], ' parent:', $comment['parent'];
}
function start_el(& $output, $comment) {
  ob_start();
  html5_comment($comment);
  $output .= ob_get_clean();
}
function end_el(& $output) {
  $output .= "</li><!-- #comment-## -->\n";
}
function start_lvl(& $output) {
  $output .= '<ol class="children">' . "\n";
}
function end_lvl(& $output) {
  $output .= "</ol><!-- .children -->\n";
}
function display_element($e, & $children_elements, $max_depth, $depth, & $output) {
  $id = $e['id'];
  start_el($output, $e); //当前评论的开始代码
  if ($max_depth > $depth +1 && isset ($children_elements[$id])) { //如果没超过最大层,并且存在子元素数组
    foreach ($children_elements[$id] as $child) {
      if (!isset ($newlevel)) { //第一次循环没设置变量$newlevel,所以把$newlevel设为true,并且开始子元素的开始代码;第二次及之后的循环,已经设置了$newlevel,就不会再添加子元素的开始代码。因为同一批循环时兄弟元素,所以只需要一个子元素开始代码,循环内容为并列关系。
        $newlevel = true;
        start_lvl($output);
      }
      display_element_template($child, $children_elements, $max_depth, $depth +1, $output); //$child作为参数,继续去寻找下级元素
    }
    unset ($children_elements[$id]); //用完释放变量,以后就不会重复判断该值了,递归后继续判断剩下的子元素
  }
  if (isset ($newlevel) && $newlevel) { //如果前面找到了子元素,这里就要执行子元素的结束代码
    end_lvl($output);
  }
  end_el($output); //当前评论的结束代码
}
function display_element_template($e, & $children_elements, $max_depth, $depth, & $output) {
  $id = $e['id'];
  display_element($e, $children_elements, $max_depth, $depth, $output);
  if ($max_depth <= $depth +1 && isset ($children_elements[$id])) { //如果超出最大层级,并且子元素存在的话,以$child为参数继续往下找
    foreach ($children_elements[$id] as $child) {
      display_element_template($child, $children_elements, $max_depth, $depth, $output);
    }
    unset ($children_elements[$id]); //用完释放变量
  }
}
function comments_list($comments) {
  $top_level_elements = array ();
  $children_elements = array ();
  foreach ($comments as $e) {
    if (0 == $e['parent']) {
      $top_level_elements[] = $e;
    } else {
      $children_elements[$e['parent']][] = $e;
    }
  }
  $output = '';
  foreach ($top_level_elements as $e) {
    display_element_template($e, $children_elements, 2, 0, $output);
  }
  //var_dump($children_elements);//由于每次用完$children_elements后都会释放变量,所以到最后$children_elements为空数组
  return $output;
}
echo '<ol class="comment-list">', comments_list($comments), '</ol>';

这篇文章就介绍到这了,其实大家多参考一些开源的cms也可以看到很多不错的代码,希望大家以后多多支持三水点靠木

PHP 相关文章推荐
PHP 文件扩展名 获取函数
Jun 03 PHP
火车头discuz6.1 完美采集的php接口文件
Sep 13 PHP
PHP中开发XML应用程序之基础篇 添加节点 删除节点 查询节点 查询节
Jul 09 PHP
浅析虚拟主机服务器php fsockopen函数被禁用的解决办法
Aug 07 PHP
php调用新浪短链接API的方法
Nov 08 PHP
PHP查找数值数组中不重复最大和最小的10个数的方法
Apr 20 PHP
PHP实现恶意DDOS攻击避免带宽占用问题方法
May 27 PHP
PHP汉字转换拼音的函数代码
Dec 30 PHP
Symfony2 session用法实例分析
Feb 04 PHP
php图片添加文字水印实现代码
Mar 15 PHP
简单的自定义php模板引擎
Aug 26 PHP
thinkphp框架表单数组实现图片批量上传功能示例
Apr 04 PHP
PHP实现负载均衡下的session共用功能
Apr 17 #PHP
PHP代码重构方法漫谈
Apr 17 #PHP
php微信公众号开发之现金红包
Apr 16 #PHP
PHP闭包定义与使用简单示例
Apr 13 #PHP
PHP简单实现正则匹配省市区的方法
Apr 13 #PHP
PHP编程实现的TCP服务端和客户端功能示例
Apr 13 #PHP
php框架CodeIgniter使用redis的方法分析
Apr 13 #PHP
You might like
?算你??的 PHP 程式大小
2006/12/06 PHP
jquery.lazyload  实现图片延迟加载jquery插件
2010/02/06 Javascript
浅析return false的正确使用
2013/11/04 Javascript
jquery实现鼠标点击后展开列表内容的导航栏效果
2015/09/14 Javascript
JS打印组合功能
2016/08/04 Javascript
8 行 Node.js 代码实现代理服务器
2016/12/05 Javascript
JS实现物体带缓冲的间歇运动效果示例
2016/12/22 Javascript
Angularjs单选框相关的示例代码
2017/08/17 Javascript
jQuery实现动态控制页面元素的方法分析
2017/12/20 jQuery
关于redux-saga中take使用方法详解
2018/02/27 Javascript
基于Vue自定义指令实现按钮级权限控制思路详解
2018/05/23 Javascript
Node错误处理笔记之挖坑系列教程
2018/06/05 Javascript
从vue源码看props的用法
2019/01/09 Javascript
element-ui 中使用upload多文件上传只请求一次接口
2019/07/19 Javascript
ES6 Promise对象概念及用法实例详解
2019/10/15 Javascript
[03:04]DOTA2英雄基础教程 影魔
2013/12/11 DOTA
Python 和 JS 有哪些相同之处
2017/11/23 Python
Python subprocess模块详细解读
2018/01/29 Python
Python中is和==的区别详解
2018/11/15 Python
python 画3维轨迹图并进行比较的实例
2019/12/06 Python
浅谈Django中的QueryDict元素为数组的坑
2020/03/31 Python
python+excel接口自动化获取token并作为请求参数进行传参操作
2020/11/10 Python
CSS中越界问题的经典解决方案【推荐】
2016/04/19 HTML / CSS
详解canvas多边形(蜘蛛图)的画法示例
2018/01/29 HTML / CSS
美国知名玩具品牌:Melissa & Doug
2016/08/16 全球购物
英国知名小木屋定制网站:Tiger Sheds
2020/03/06 全球购物
几个MySql的面试题
2013/04/22 面试题
大学生农村教师实习自我鉴定
2013/09/21 职场文书
机械设计制造专业个人求职信
2013/09/25 职场文书
商务英语专业应届毕业生求职信
2013/10/28 职场文书
党的群众路线教育实践活动通讯稿
2014/09/10 职场文书
秋季校运会广播稿100字
2014/09/18 职场文书
培训督导岗位职责
2015/04/10 职场文书
OpenCV-Python实现轮廓拟合
2021/06/08 Python
Python预测分词的实现
2021/06/18 Python
IDEA中sout快捷键无效问题的解决方法
2022/07/23 Java/Android