我目前正在使用PHP进行评论系统,我正在使用'父ID'解决方案将一个回复连接到另一个。问题是我还没弄清楚如何投射这些'父ID'将存储在mysql中的数据连接到PHP数组并将它们渲染出去。我一直在寻找迭代解决方案,但没有发现任何问题。我的数据库结构如下: Parent_id 0表示顶级评论。
comment_id content parent_id
1 xxx 0
2 xxx 0
3 xxx 1
4 xxx 3
5 xxx 4
6 xxx 3
... ... ...
这是我所做的,我在数组中获取了所有注释,数组看起来像这样:
$comment_list = array(0=>array('comment_id'=>1,'content'=>'xxx','parent_id'=>0),
0=>array('comment_id'=>2,'content'=>'xxx','parent_id'=>0),
0=>array('comment_id'=>3,'content'=>'xxx','parent_id'=>1),
0=>array('comment_id'=>4,'content'=>'xxx','parent_id'=>3),
...
)
我需要在parent_id 1附加注释以使用comment_id 1进行注释,依此类推,深度应该是无限的,并且工作几个小时仍然无法找到正确迭代的方法,有人可以给我一些指示如何这样做?我知道一个解决方案,但是每次迭代都会对数据库提出新的请求,所以我更喜欢一次使用PHP数组,谢谢!
答案 0 :(得分:1)
当遇到像这样的复杂结构时,有时最好创建面向对象的解决方案,然后使用这些对象来创建所需的数组。
例如,根据您的上述内容,我可能会定义以下类:
class Comment{
protected $id;
protected $children;
protected $content;
public function __construct( $id, $content ){
$this->id = $id;
$this->content = $content;
$this->children = array();
}
public function addChild( $child ){
$this->children[] = $child;
}
}
现在,我们使用此对象将数据库传输到工作内存中,如下所示:
$workingMemory = array(); //a place to store our objects
$unprocessedRows = array(); //a place to store unprocessed records
// here, add some code to fill $unproccessedRows with your database records
do{
$row = $unprocessedRows; //transfer unprocessed rows to a working array
$unprocessedRows = array(); //clear unprocessed rows to receive any rows that we need to process out of order.
foreach( $row as $record ){
$id = $record[0]; //assign your database value for comment id here.
$content = $record[1]; //assign your database value for content here.
$parentId = $record[2]; //assign your database value for parent id here
$comment = new Comment( $id, $content );
//for this example, we will refer to unlinked comments as
//having a parentId === null.
if( $parentId === null ){
//this is just a comment and does not need to be linked to anything, add it to working memory indexed by it's id.
$workingMemory[ $id ] = $comment;
}else if( isset( $workingMemory[ $parentId ] ) ){
//if we are in this code block, then we processed the parent earlier.
$parentComment = $workingMemory[ $parentId ];
$parentComment->addChild( $comment );
$workingMemory[ $id] = $comment;
}else{
//if we are in this code block, the parent has not yet been processed. Store the row for processing again later.
$unprocessedRows[] = $record;
}
}
}while( count( $unprocessedRows ) > 0 );
一旦所有unprocessedRows完成,您现在可以完全存储在变量$ workingMemory中表示您的注释,并且此数组的每个单元格都是一个Comment对象,它具有$ id,$ content和所有链接孩子们的评论。
我们现在可以遍历这个数组并生成我们想要的任何数据数组或表。我们必须记住,我们存储数组的方式,我们可以直接从$ workingMemory数组直接访问任何注释。
如果我使用它来为网站生成HTML,我会遍历workingMemory数组并仅处理父注释。然后,每个过程将遍历子项。从父母而不是孩子开始,我们保证我们不会两次处理相同的评论。
我会改变我的评论课,以使这更容易:
class Comment{
protected $id;
protected $children;
protected $content;
protected $isRoot;
public function __construct( $id, $content ){
$this->id = $id;
$this->content = $content;
$this->children = array();
$this->isRoot = true;
}
public function addChild( $child ){
$child->isRoot = false;
$this->children[] = $child;
}
public function getChildren(){ return $this->children; }
public function getId(){ return $this->id; }
public function getContent(){ return $this->content; }
}
完成此更改后,我可以按如下方式创建HTML:
function outputCommentToHTML( $aComment, $commentLevel = 0 ){
//I am using commentLevel here to set a special class, which I would use to indent the sub comments.
echo "<span class'comment {$commentLevel}' id='".($aComment->getId())."'>".($aComment->getContent())."</span>";
$children = $aComment->getChildren();
foreach( $children as $child ){
outputCommentToHTML( $child, $commentLevel + 1 );
}
}
foreach( $workingMemory as $aComment ){
if( $aComment->isRoot === true ){
outputCommentToHTML( $aComment );
}
}
这会将数据库列转换为您需要的格式。例如,如果我们有以下数据:
comment_id content parent_id
1 xxx 0
2 xxx 0
3 xxx 1
4 xxx 3
5 xxx 4
6 xxx 3
... ... ...
它将以HTML输出:
Comment_1
Comment_3
Comment_4
Comment_5
Comment_6
Comment_2
这是在函数中递归完成的,该函数在移动到Comment 2之前完全处理Comment_1。它还在移动到Comment 2之前完全处理Comment_3,这是注释2之前注释4,5和6的全部输出。
上面的例子对你有用,但是如果它是我的个人项目,我不会混合使用线性和面向对象的代码,所以我会创建一个代码工厂来将Comments转换为HTML。工厂从源对象生成数据字符串。您可以创建一个充当HTML工厂的对象,另一个充当SQL生成器的工厂,通过具有此类解决方案的图层对象,您可以创建一个完全面向对象的解决方案,这对于平均值更容易理解读者,有时甚至是非编码人员来制作这样的东西:
//these definition files get hidden and tucked away for future use
//you use include, include_once, require, or require_once to load them
class CommentFactory{
/**** other Code *****/
public function createCommentArrayFromDatabaseRecords( $records ){
/*** add the data conversion here that we discussed above ****/
return $workingMemory;
}
}
class HTMLFactory{
public function makeCommentTableFromCommentArray( $array ){
$htmlString = "";
foreach( $array as $comment ){
if( $comment->isRoot ){
$htmlString .= $this->getHTMLStringForComment( $comment );
}
}
return $htmlString;
}
private function getHTMLStringForComment( $comment, $level=0 ){
/*** turn your comment and all it's children into HTML here (recursively) ****/
return $html;
}
}
正确完成后,它可以清理您的活动代码文件,使其读起来就像这样的指令列表:
//let database be a mysqli or other database connection
//let the query function be whatever method works for your database
// of choice.
//let the $fetch_comment_sql variable hold your SQL string to fetch the
// comments
$records = $database->query( $fetch_comment_sql )
$comFactory = new CommentFactory();
$commentArray = $comFactory->createCommentArrayFromDatabaseRecords( $records );
$htmlFactory = new HTMLFactory();
$htmlResult = $htmlFactory->makeCommentTableFromCommentArray( $commentArray );
echo $htmlResult;