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 adodb连接不同数据库
Mar 19 PHP
php启动时候提示PHP startup的解决方法
May 07 PHP
初识PHP
Sep 28 PHP
PHP防止注入攻击实例分析
Nov 03 PHP
php计算两个整数的最大公约数常用算法小结
Mar 05 PHP
提高php编程效率技巧
Aug 13 PHP
PHP Yii框架之表单验证规则大全
Nov 16 PHP
PHP递归实现文件夹的复制、删除、查看大小操作示例
Aug 11 PHP
php 使用expat方式解析xml文件操作示例
Nov 26 PHP
PHP实用小技巧之调用录像的方法
Dec 05 PHP
PHP架构及原理知识点详解
Dec 22 PHP
php中Swoole的热更新实现代码实例
Mar 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获取数组最大值下标的方法
2015/05/12 PHP
ThinkPHP2.x防范XSS跨站攻击的方法
2015/09/25 PHP
php时间计算相关问题小结
2016/05/09 PHP
php插件Xajax使用方法详解
2017/08/31 PHP
详解thinkphp5+swoole实现异步邮件群发(SMTP方式)
2017/10/13 PHP
CI框架附属类用法分析
2018/12/26 PHP
简单的两种Extjs formpanel加载数据的方式
2013/11/09 Javascript
24款热门实用的jQuery插件推荐
2014/12/24 Javascript
JavaScript中的console.profile()函数详细介绍
2014/12/29 Javascript
js实现千分符和保留几位小数的简单实例
2016/08/01 Javascript
简单实现js倒计时功能
2017/02/13 Javascript
javascript 面向对象function详解及实例代码
2017/02/28 Javascript
简单的JS控制button颜色随点击更改的实现方法
2017/04/17 Javascript
Vue数字输入框组件使用方法详解
2020/02/10 Javascript
Javascript中window.name属性详解
2020/11/19 Javascript
python实现bucket排序算法实例分析
2015/05/04 Python
轻量级的Web框架Flask 中模块化应用的实现
2017/09/11 Python
python实现逆序输出一个数字的示例讲解
2018/06/25 Python
Python补齐字符串长度的实例
2018/11/15 Python
Python unittest 简单实现参数化的方法
2018/11/30 Python
Python面向对象之类和实例用法分析
2019/06/08 Python
Python进程Multiprocessing模块原理解析
2020/02/28 Python
Python 代码调试技巧示例代码
2020/08/11 Python
老海军美国官网:Old Navy
2016/09/05 全球购物
西班牙香水和化妆品购物网站:Arenal Perfumerías
2019/03/01 全球购物
英智兴达软件测试笔试题
2016/10/12 面试题
大学生入党自我鉴定
2013/10/31 职场文书
简易离婚协议书(范本)
2014/10/25 职场文书
个人优缺点总结
2015/02/28 职场文书
教师节座谈会主持词
2015/07/03 职场文书
宾馆卫生管理制度
2015/08/06 职场文书
学校教代会开幕词
2016/03/04 职场文书
十二月早安励志心语大全
2019/12/03 职场文书
浅谈移动端中的视口(viewport)的具体使用
2021/04/13 HTML / CSS
【海涛解说】史上最给力比赛,挑战DOTA极限
2022/04/01 DOTA
Python Matplotlib绘制等高线图与渐变色扇形图
2022/04/14 Python