массивы — вложенные комментарии с использованием итерации в переполнении стека

В настоящее время я работаю над системой комментариев, использующей PHP, и использую решение «родительский идентификатор», чтобы соединить один ответ с другим. Проблема в том, что я не понял, как преобразовать эти сохраненные данные, связанные с «родительским идентификатором». в 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 раз и навсегда, спасибо!

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 и ссылки на все дочерние $ comments. ,

Теперь мы можем перебирать этот массив и создавать любые массивы данных или таблицы, которые мы хотим. Мы должны помнить, что при хранении массивов мы имеем прямой доступ к любому комментарию непосредственно из массива $ workingMemory.

Если бы я использовал это для генерации HTML-кода для веб-сайта, я бы перебрал массив workingMemory и обработал только родительские комментарии. Каждый процесс будет затем перебирать детей. Начиная с родителей, а не с детей, мы гарантируем, что мы не обрабатываем один и тот же комментарий дважды.

Я бы изменил свой класс Comment, чтобы сделать это проще:

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_1 перед переходом к комментарию 2. Она также полностью обрабатывает Comment_3 перед переходом к комментарию 2, то есть все комментарии 4, 5 и 6 выводятся до комментария 2.

Приведенный выше пример будет работать для вас, но если бы это был мой личный проект, я бы не смешивал линейный и объектно-ориентированный код, поэтому я бы создал фабрику кода для преобразования комментариев в 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;
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]