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
Apache设置虚拟WEB
Oct 09 PHP
亲密接触PHP之PHP语法学习笔记1
Dec 17 PHP
PHP的开发框架的现状和展望
Mar 16 PHP
php 数组二分法查找函数代码
Feb 16 PHP
利用Memcached在php下实现session机制 替换PHP的原生session支持
Aug 21 PHP
php判断GIF图片是否为动画的方法
Sep 04 PHP
php中fsockopen用法实例
Jan 05 PHP
学习php设计模式 php实现享元模式(flyweight)
Dec 07 PHP
php微信公众账号开发之前五个坑(一)
Sep 18 PHP
yii通过小物件生成view的方法
Oct 08 PHP
PC端微信扫码支付成功之后自动跳转php版代码
Jul 07 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 无线电
给海燕B411配件机起死回生配上件
2021/03/02 无线电
PHP的类 功能齐全的发送邮件类
2006/10/09 PHP
PHP+javascript液晶时钟
2006/10/09 PHP
Laravel框架学习笔记(二)项目实战之模型(Models)
2014/10/15 PHP
codeigniter中view通过循环显示数组数据的方法
2015/03/20 PHP
详解PHP序列化和反序列化原理
2018/01/15 PHP
php实现单笔转账到支付宝功能
2018/10/09 PHP
用ADODB.Stream转换
2007/01/22 Javascript
JavaScript闭包实例详解
2016/06/03 Javascript
form表单序列化详解(推荐)
2017/08/15 Javascript
vue路由懒加载的实现方法
2018/03/12 Javascript
小程序组件之仿微信通讯录的实现代码
2018/09/12 Javascript
js中Array对象的常用遍历方法详解
2019/01/17 Javascript
在Vue项目中使用Typescript的实现
2019/12/19 Javascript
vue 解决兄弟组件、跨组件深层次的通信操作
2020/07/27 Javascript
跨平台python异步回调机制实现和使用方法
2013/11/26 Python
Python中import导入上一级目录模块及循环import问题的解决
2016/06/04 Python
Python中sort和sorted函数代码解析
2018/01/25 Python
pandas分别写入excel的不同sheet方法
2018/12/11 Python
Python的UTC时间转换讲解
2019/02/26 Python
python实现最大优先队列
2019/08/29 Python
python网络爬虫 Scrapy中selenium用法详解
2019/09/28 Python
Python中six模块基础用法
2019/12/08 Python
python用opencv完成图像分割并进行目标物的提取
2020/05/25 Python
Django REST Swagger实现指定api参数
2020/07/07 Python
django创建css文件夹的具体方法
2020/07/31 Python
python基于pexpect库自动获取日志信息
2021/02/01 Python
委托公证书
2014/04/08 职场文书
大学学风建设方案
2014/05/04 职场文书
弘扬雷锋精神演讲稿
2014/05/10 职场文书
如何写贫困证明申请书
2014/10/29 职场文书
2014年移动公司工作总结
2014/12/08 职场文书
农村房屋租赁合同(范本)
2019/07/23 职场文书
导游词之海南天涯海角
2019/12/05 职场文书
引用计数法和root搜索算法以及JVM中判定对象需要回收的方法
2022/04/19 Java/Android