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中文汉字验证码
Apr 08 PHP
php printf输出格式使用说明
Dec 05 PHP
PHP5中虚函数的实现方法分享
Apr 20 PHP
PHP检测移动设备类mobile detection使用实例
Apr 14 PHP
Yii框架中memcache用法实例
Dec 03 PHP
解决PHP里大量数据循环时内存耗尽的方法
Oct 10 PHP
深入理解PHP内核(二)之SAPI探究
Nov 10 PHP
PHP使用Pthread实现的多线程操作实例
Nov 14 PHP
WordPress开发中的get_post_custom()函数使用解析
Jan 04 PHP
PHP 实现公历日期与农历日期的互转换
Sep 13 PHP
PHP创建XML接口示例
Jul 04 PHP
php实现获取近几日、月时间示例
Jul 06 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 数组二分法查找函数代码
2010/02/16 PHP
有关PHP性能优化的介绍
2013/06/20 PHP
PHP实现简单实用的验证码类
2015/07/29 PHP
基于命令行执行带参数的php脚本并取得参数的方法
2016/01/25 PHP
PHP二分查找算法示例【递归与非递归方法】
2016/09/29 PHP
如何确保JavaScript的执行顺序 之jQuery.html并非万能钥匙
2011/03/03 Javascript
js将控件隐藏的方法及display属性介绍
2013/07/04 Javascript
一个仿糯米弹框效果demo
2014/07/22 Javascript
js给网页加上背景音乐及选择音效的方法
2015/03/03 Javascript
js行号显示的文本框实现效果(兼容多种浏览器 )
2015/10/23 Javascript
jQuery Easyui datagrid editor为combobox时指定数据源实例
2016/12/19 Javascript
简单好用的nodejs 爬虫框架分享
2017/03/26 NodeJs
ES6新特性八:async函数用法实例详解
2017/04/21 Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
2017/07/31 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
vue2手机APP项目添加开屏广告或者闪屏广告
2017/11/28 Javascript
10行原生JS实现文字无缝滚动(超简单)
2018/01/02 Javascript
webpack将js打包后的map文件详解
2018/02/22 Javascript
JS实现HTML页面中动态显示当前时间完整示例
2018/07/30 Javascript
详解在不使用ssr的情况下解决Vue单页面SEO问题
2018/11/08 Javascript
setTimeout与setInterval的区别浅析
2019/03/23 Javascript
[31:33]2014 DOTA2国际邀请赛中国区预选赛 TongFu VS DT 第一场
2014/05/23 DOTA
[59:07]海涛为你详解DOTA2新版本“贤哲秘契”
2014/11/22 DOTA
[23:18]Spirit vs Liquid Supermajor小组赛A组 BO3 第二场 6.2
2018/06/03 DOTA
Windows系统下多版本pip的共存问题详解
2017/10/10 Python
Python绘制3D图形
2018/05/03 Python
django模板加载静态文件的方法步骤
2019/03/01 Python
详解Python并发编程之创建多线程的几种方法
2019/08/23 Python
python opencv角点检测连线功能的实现代码
2020/11/24 Python
PyQt5通过信号实现MVC的示例
2021/02/06 Python
CSS3实现可翻转的hover效果
2018/05/23 HTML / CSS
复核员上岗演讲稿
2014/01/05 职场文书
4s店销售经理岗位职责
2014/07/19 职场文书
离婚答辩状范文
2015/05/22 职场文书
Django+Celery实现定时任务的示例
2021/06/23 Python
python中subplot大小的设置步骤
2021/06/28 Python