And, you know, just for the heck of it, let’s take it a bit further and use objects for comments instead of plain old arrays. Then we can let those render themselves. Create a nice Composite pattern.
So let’s a define a comment:
<?php
class Comment
{
/**
* @var string
*/
public $name;
/**
* @var string
*/
public $comment;
/**
* @var Comment[]
*/
public $replies;
public function __construct($name, $comment, array $replies)
{
$this->name = $name;
$this->comment = $comment;
$this->replies = $replies;
}
public function render()
{
echo $this->name . "<br>";
echo $this->comment;
echo "<br><br>";
foreach ($this->replies as $reply) {
$reply->render();
}
}
}
Okay, so that’s cool, let’s see how that looks with the rest of the code:
function fetch_comments($parent, $dbconn) {
$stmt = $dbconn->prepare('SELECT id, r_to, name, comment FROM comments WHERE r_to = ?');
$stmt->bind_param('s', $parent);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($id, $r_to, $name, $comment);
$comments = [];
while ($stmt->fetch()) {
$comments[] = new Comment(
$name,
$comment,
fetch_comments($id, $dbconn)
);
}
return $comments;
}
foreach (fetch_comments('0', $mysqli) as $comment) {
$comment->render();
}
So the render_comments
function is just a simple foreach, and the recursion has moved to the Comment
class, so let’s get rid of that function and just put it inline.
One thing annoys me still, is that I have to manually loop over those posts, let’s introduce a CommentCollection
to take care of that:
class Comment
{
/**
* @var string
*/
public $name;
/**
* @var string
*/
public $comment;
/**
* @var CommentCollection
*/
public $replies;
public function __construct($name, $comment, CommentCollection $replies)
{
$this->name = $name;
$this->comment = $comment;
$this->replies = $replies;
}
public function render()
{
echo $this->name . "<br>";
echo $this->comment;
echo "<br><br>";
$this->replies->render();
}
}
class CommentCollection
{
/**
* @var Commment[]
*/
public $comments;
public function __construct(array $comments)
{
$this->comments = $comments;
}
public function add(Comment $comment)
{
$this->comments[] = $comment;
}
public function render()
{
foreach ($this->comments as $comment) {
$comment->render();
}
}
}
function fetch_comments($parent, $dbconn) {
$stmt = $dbconn->prepare('SELECT id, r_to, name, comment FROM comments WHERE r_to = ?');
$stmt->bind_param('s', $parent);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($id, $r_to, $name, $comment);
$comments = new CommentCollection();
while ($stmt->fetch()) {
$comments->add(
new Comment(
$name,
$comment,
fetch_comments($id, $dbconn)
)
);
}
return $comments;
}
fetch_comments('0', $mysqli)->render();
Okay, so that’s the rendering taken care of. Last thing we need now, with all these objects, is create an object for the fetching. Let’s call it a CommentRepository
:
class Comment
{
/**
* @var string
*/
public $name;
/**
* @var string
*/
public $comment;
/**
* @var CommentCollection
*/
public $replies;
public function __construct($name, $comment, CommentCollection $replies)
{
$this->name = $name;
$this->comment = $comment;
$this->replies = $replies;
}
public function render()
{
echo $this->name . "<br>";
echo $this->comment;
echo "<br><br>";
$this->replies->render();
}
}
class CommentCollection
{
/**
* @var Commment[]
*/
public $comments;
public function __construct(array $comments)
{
$this->comments = $comments;
}
public function add(Comment $comment)
{
$this->comments[] = $comment;
}
public function render()
{
foreach ($this->comments as $comment) {
$comment->render();
}
}
}
class CommentRepository
{
/**
* @var mysqli
*/
private $dbConn;
public function __construct(mysqli $dbConn)
{
$this->dbConn = $dbConn;
}
function findComments($parent)
{
$stmt = $this->dbconn->prepare('SELECT id, r_to, name, comment FROM comments WHERE r_to = ?');
$stmt->bind_param('s', $parent);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($id, $r_to, $name, $comment);
$comments = new CommentCollection();
while ($stmt->fetch()) {
$comments->add(
new Comment(
$name,
$comment,
$this->findComments($id)
)
);
}
return $comments;
}
}
(new CommentRepository($mysqli))->findComments('0')->render();
And there you have it, an OOP solution to the problem 
It does exactly the same thing, but this is a lot more resilient to change, because not everything is tightly coupled together anymore. Want to change how comments are fetched from the database? Change the repository. Want to change how they’re rendered? Change the Comment. etc.
In an ideal world the comments would not even render themselves, that would be done by a CommentRenderer
, but that might be taking things too far for this example 