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 17 PHP
ThinkPHP 表单自动验证运用示例
Oct 13 PHP
php5.3提示Function ereg() is deprecated Error问题解决方法
Nov 12 PHP
php jsonp单引号转义
Nov 23 PHP
THINKPHP内容分页代码分享
Jan 14 PHP
CentOS下PHP安装Oracle扩展
Feb 15 PHP
php生成固定长度纯数字编码的方法
Jul 09 PHP
PHP实现递归复制整个文件夹的类实例
Aug 03 PHP
替换php字符串中的单引号为双引号的方法
Feb 16 PHP
PHP异步进程助手async-helper
Feb 05 PHP
详解PHP实现支付宝小程序用户授权的工具类
Dec 25 PHP
PHP获取远程http或ftp文件的md5值的方法
Apr 15 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
长波知识介绍
2021/03/01 无线电
JavaScript多线程的实现方法
2007/05/08 Javascript
发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser
2007/11/30 Javascript
Javascript常用运算符(Operators)-javascript基础教程
2007/12/14 Javascript
JQuery1.8 判断元素是否绑定事件的方法
2014/07/10 Javascript
js实现class样式的修改、添加及删除的方法
2015/01/20 Javascript
JavaScript如何动态创建table表格
2020/08/02 Javascript
简单实现Bootstrap标签页
2020/08/09 Javascript
select下拉框插件jquery.editable-select详解
2017/01/22 Javascript
jQuery的三种bind/One/Live/On事件绑定使用方法
2017/02/23 Javascript
vue 如何添加全局函数或全局变量以及单页面的title设置总结
2017/06/01 Javascript
Bootstrap Table 在指定列中添加下拉框控件并获取所选值
2017/07/31 Javascript
详解vue-cli3使用
2018/08/14 Javascript
微信小程序实现即时通信聊天功能的实例代码
2018/08/17 Javascript
详解小程序输入框闪烁及重影BUG解决方案
2018/08/31 Javascript
基于VUE实现的九宫格抽奖功能
2018/09/30 Javascript
jQuery使用bind动态绑定事件无效的处理方法
2018/12/11 jQuery
微信小程序使用二次贝塞尔曲线画波浪
2018/12/25 Javascript
js+cavans实现图片滑块验证
2020/09/29 Javascript
js 压缩图片的示例(只缩小体积,不更改图片尺寸)
2020/10/21 Javascript
[04:27]DOTA2官方论坛水友赛集锦
2013/09/16 DOTA
Python实现简单的可逆加密程序实例
2015/03/05 Python
Python编程之变量赋值操作实例分析
2017/07/24 Python
python中使用psutil查看内存占用的情况
2018/06/11 Python
python爬虫之urllib3的使用示例
2018/07/09 Python
对python模块中多个类的用法详解
2019/01/10 Python
css3实现一个div设置多张背景图片及background-image属性实例演示
2017/08/10 HTML / CSS
利用三角函数在canvas上画虚线的方法
2018/01/11 HTML / CSS
倩碧英国官网:Clinique英国
2018/08/10 全球购物
Yves Rocher捷克官方网站:植物化妆品的创造者
2019/07/31 全球购物
交通事故检查书范文
2014/01/30 职场文书
小学生安全保证书
2014/02/01 职场文书
法律专业学生的自我评价
2014/02/07 职场文书
工程项目建议书范文
2014/03/12 职场文书
2014光棍节单身联谊活动策划书
2014/10/10 职场文书
民事起诉书范本
2015/05/19 职场文书