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 相关文章推荐
Windows下的PHP5.0详解
Nov 18 PHP
smarty模板中使用get、post、request、cookies、session变量的方法
Apr 24 PHP
PHP微框架Dispatch简介
Jun 12 PHP
让CodeIgniter的ellipsize()支持中文截断的方法
Jun 12 PHP
PHP多个文件上传到服务器实例
Oct 29 PHP
初识通用数据库操作类――前端easyui-datagrid,form(php)
Jul 31 PHP
PHP MYSQL实现登陆和模糊查询两大功能
Feb 05 PHP
非集成环境的php运行环境(Apache配置、Mysql)搭建安装图文教程
Apr 12 PHP
Joomla语言翻译类Jtext用法分析
May 05 PHP
php获取POST数据的三种方法实例详解
Dec 20 PHP
laravel通过a标签从视图向控制器实现传值
Oct 15 PHP
Yii Framework框架开发微信公众平台示例
Apr 26 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 array的学习笔记
2012/05/10 PHP
PHP对文件夹递归执行chmod命令的方法
2015/06/19 PHP
PHP实现的堆排序算法详解
2017/08/17 PHP
PHP中使用mpdf 导出PDF文件的实现方法
2018/10/22 PHP
js 幻灯片的实现
2011/12/06 Javascript
javascript相关事件的几个概念
2015/05/21 Javascript
jQuery插件EnPlaceholder实现输入框提示文字
2015/06/05 Javascript
JAVASCRIPT代码编写俄罗斯方块网页版
2015/11/26 Javascript
灵活使用数组制作图片切换js实现
2016/07/28 Javascript
解析ajaxFileUpload 异步上传文件简单使用
2016/12/30 Javascript
深入理解ES6的迭代器与生成器
2017/08/19 Javascript
JS中实现隐藏部分姓名或者电话号码的代码
2018/07/17 Javascript
BootStrap中的模态框(modal,弹出层)功能示例代码
2018/11/02 Javascript
vue实现微信浏览器左上角返回按钮拦截功能
2020/01/18 Javascript
python中使用enumerate函数遍历元素实例
2014/06/16 Python
简单介绍Python中的RSS处理
2015/04/13 Python
探究Python的Tornado框架对子域名和泛域名的支持
2015/05/02 Python
小议Python中自定义函数的可变参数的使用及注意点
2016/06/21 Python
python爬虫入门教程--正则表达式完全指南(五)
2017/05/25 Python
基于python时间处理方法(详解)
2017/08/14 Python
Python连接phoenix的方法示例
2017/09/29 Python
Python获取当前函数名称方法实例分享
2018/01/18 Python
python取代netcat过程分析
2018/02/10 Python
Python网页正文转换语音文件的操作方法
2018/12/09 Python
Python hashlib模块实例使用详解
2019/12/24 Python
Linux安装Python3如何和系统自带的Python2并存
2020/07/23 Python
安装pyecharts1.8.0版本后导入pyecharts模块绘图时报错: “所有图表类型将在 v1.9.0 版本开始强制使用 ChartItem 进行数据项配置 ”的解决方法
2020/08/18 Python
python 利用jieba.analyse进行 关键词提取
2020/12/17 Python
购买中国最好的电子产品:Geekbuying
2018/03/13 全球购物
英国顶级足球鞋的领先零售商:Lovell Soccer
2019/08/27 全球购物
薇姿法国官网:Vichy法国
2021/01/28 全球购物
Exception类的常用方法
2012/06/16 面试题
活动邀请函范文
2014/01/19 职场文书
党的群众路线个人对照检查材料
2014/09/23 职场文书
优秀班主任工作总结2015
2015/05/25 职场文书
教师听课学习心得体会
2016/01/15 职场文书