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 相关文章推荐
JAVA/JSP学习系列之六
Oct 09 PHP
php读取msn上的用户信息类
Dec 05 PHP
PHP 防注入函数(格式化数据)
Aug 08 PHP
php查找字符串出现次数的方法
Dec 01 PHP
PHP实现在线阅读PDF文件的方法
Jun 17 PHP
PHP读书笔记整理_结构语句详解
Jul 01 PHP
浅谈PHP正则中的捕获组与非捕获组
Jul 18 PHP
微信 getAccessToken方法详解及实例
Nov 23 PHP
php取出数组单个值的方法
Mar 12 PHP
PHP的cookie与session原理及用法详解
Sep 27 PHP
laravel实现一个上传图片的接口,并建立软链接,访问图片的方法
Oct 12 PHP
如何在Laravel5.8中正确地应用Repository设计模式
Nov 26 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
杏林同学录(二)
2006/10/09 PHP
浅析PHP中的UNICODE 编码与解码
2013/06/29 PHP
PHP合并discuz用户脚本的方法
2015/08/04 PHP
微信支付的开发流程详解
2016/09/13 PHP
PHP在linux上执行外部命令的方法
2017/02/06 PHP
防止动态加载JavaScript引起的内存泄漏问题
2009/10/08 Javascript
如何确保JavaScript的执行顺序 之jQuery.html并非万能钥匙
2011/03/03 Javascript
JS鼠标滑过图片时切换图片实现思路
2013/09/12 Javascript
用Jquery选择器计算table中的某一列某一行的合计
2014/08/13 Javascript
JavaScript 事件对象介绍
2015/04/13 Javascript
js实现动态加载脚本的方法实例汇总
2015/11/02 Javascript
javascript制作照片墙及制作过程中出现的问题
2016/04/04 Javascript
浅谈使用React.setState需要注意的三点
2017/12/18 Javascript
利用Vue2.x开发实现JSON树的方法
2018/01/04 Javascript
vue单个组件实现无限层级多选菜单功能
2018/04/10 Javascript
Vue中的v-for循环key属性注意事项小结
2018/08/12 Javascript
VUE中V-IF条件判断改变元素的样式操作
2020/08/09 Javascript
[00:43]TI7不朽珍藏III——幽鬼不朽展示
2017/07/15 DOTA
Python fileinput模块使用实例
2015/06/03 Python
python使用mysql数据库示例代码
2017/05/21 Python
python正则表达式去除两个特殊字符间的内容方法
2018/12/24 Python
python3 deque 双向队列创建与使用方法分析
2020/03/24 Python
Python爬虫实例——scrapy框架爬取拉勾网招聘信息
2020/07/14 Python
pytho matplotlib工具栏源码探析一之禁用工具栏、默认工具栏和工具栏管理器三种模式的差异
2021/02/25 Python
CSS3弹性布局内容对齐(justify-content)属性使用详解
2017/07/31 HTML / CSS
移动端html5模拟长按事件的实现方法
2018/09/30 HTML / CSS
UML设计模式笔试题
2014/06/07 面试题
幼师自荐信范文
2013/10/06 职场文书
小学生班会演讲稿
2014/01/09 职场文书
八一演出活动方案
2014/02/03 职场文书
电大会计学自我鉴定
2014/02/06 职场文书
领导干部作风整顿个人剖析材料
2014/10/11 职场文书
销售经理助理岗位职责
2015/04/13 职场文书
会计稽核岗位职责
2015/04/13 职场文书
Logback 使用TurboFilter实现日志级别等内容的动态修改操作
2021/08/30 Java/Android
Python接口自动化之文件上传/下载接口详解
2022/04/05 Python