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实现登陆验证码(类似条行码状)
Oct 09 PHP
global.php
Dec 09 PHP
一个捕获函数输出的函数
Feb 14 PHP
基于php-fpm的配置详解
Jun 03 PHP
基于php中使用excel的简单介绍
Aug 02 PHP
php面向对象中static静态属性和静态方法的调用
Feb 08 PHP
解析WordPress中控制用户登陆和判断用户登陆的PHP函数
Mar 01 PHP
CI框架(ajax分页,全选,反选,不选,批量删除)完整代码详解
Nov 01 PHP
Zend Framework分发器用法示例
Dec 11 PHP
PHP实现表单提交时去除斜杠的方法
Dec 26 PHP
PHP单元测试配置与使用方法详解
Dec 27 PHP
PHP之header函数详解
Mar 02 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
DC动画电影《黑暗正义联盟》曝预告 5月5日上线数字平台
2020/04/09 欧美动漫
一个PHP模板,主要想体现一下思路
2006/12/25 PHP
推荐一款PHP+jQuery制作的列表分页的功能模块
2014/10/14 PHP
jQuery 验证插件 Web前端设计模式(asp.net)
2010/10/17 Javascript
jquery随意添加移除html的实现代码
2011/06/21 Javascript
js 弹出新页面避免被浏览器、ad拦截的一种新方法
2014/04/30 Javascript
详解Angularjs中的依赖注入
2016/03/11 Javascript
基于JS实现textarea中获取动态剩余字数的方法
2016/05/25 Javascript
jQuery控制文本框只能输入数字和字母及使用方法
2016/05/26 Javascript
javascript简单进制转换实现方法
2016/11/24 Javascript
Javascript中关于Array.filter()的妙用详解
2016/12/04 Javascript
jQuery.Validate表单验证插件的使用示例详解
2017/01/04 Javascript
react-redux中connect()方法详细解析
2017/05/27 Javascript
AngularJS点击添加样式、点击变色设置的实例代码
2017/07/27 Javascript
微信小程序多列选择器range-key使用详解
2020/03/30 Javascript
详解require.js配置路径的用法和css的引入
2017/09/06 Javascript
angular2系列之路由转场动画的示例代码
2017/11/09 Javascript
微信小程序tabBar模板用法实例分析【附demo源码下载】
2017/11/28 Javascript
Vue中自定义全局组件的实现方法
2017/12/08 Javascript
详解Vue.js项目API、Router配置拆分实践
2018/03/16 Javascript
Mac下安装vue
2018/04/11 Javascript
React实现轮播效果
2020/08/25 Javascript
Python科学计算环境推荐——Anaconda
2014/06/30 Python
详解Python中的循环语句的用法
2015/04/09 Python
Python学习pygal绘制线图代码分享
2017/12/09 Python
Python根据文件名批量转移图片的方法
2018/10/21 Python
pygame实现雷电游戏雏形开发
2018/11/20 Python
python中使用 xlwt 操作excel的常见方法与问题
2019/01/13 Python
pymongo中聚合查询的使用方法
2019/03/22 Python
python paramiko远程服务器终端操作过程解析
2019/12/14 Python
python函数不定长参数使用方法解析
2019/12/14 Python
岗位职责定义及内容
2013/11/08 职场文书
节约用电标语
2014/06/17 职场文书
教师听课评语大全
2014/12/31 职场文书
美术教师个人总结
2015/02/06 职场文书
2015年前台文员工作总结
2015/05/18 职场文书