Created
January 30, 2011 21:07
-
-
Save MichelBartz/803251 to your computer and use it in GitHub Desktop.
Nested Comments using Redis. Rely on the Owlient Redis extension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* A Nested Comments manager using Redis only | |
* @author Michel Bartz <[email protected]> | |
* @date 01/28/2011 | |
*/ | |
class Comments | |
{ | |
private $_redis; | |
public function __construct() { | |
$this->_redis = new Redis(); | |
$this->_redis->connect("localhost", 6379); | |
} | |
/** | |
* Save a new comment | |
* @param int $itemId Item on which the comment is made | |
* @param String $comment The comment itself | |
* @param String $username The username of the comment author | |
* @param int $userId The user id of the comment author | |
* @param int $parentId The ID of the direct parent comment, 0 if none. | |
* @return int The comment ID | |
*/ | |
public function save($itemId, $comment, $username, $userId, $parentId = 0) { | |
//To force the re-fetching of the comment tree on the next getComments() call | |
$this->_redis->del("item:" . $itemId. " :parsedComments"); | |
$isMasterComment = false; | |
if(!$parentId) { | |
$isMasterComment = true; | |
$parentId = $itemId; | |
} else { | |
$this->_redis->hSet("comment:" . $parentId, "hasChildren", 1); | |
} | |
$data = array("id" => $this->_getCommentId(), | |
"parentId" => $parentId, | |
"user_id" => $userId, | |
"username" => $username, | |
"content" => $comment, | |
"itemId" => $itemId, | |
"hasChildren" => 0, | |
"time" => time()); | |
if($isMasterComment) { | |
$this->_redis->rPush("item:" . $itemId . ":comments", $data['id']); | |
} else { | |
$this->_redis->rPush("thread:" . $parentId, $data['id']); | |
} | |
$this->_redis->hMSet("comment:" . $data['id'], $data); | |
return $data['id']; | |
} | |
/** | |
* Returns all the comments attached to a given Item ID | |
* @param int $itemId The item id | |
* @return array The comments | |
*/ | |
public function getComments($itemId) { | |
$parsedComments = $this->_redis->get("item:" . $itemId. " :parsedComments"); | |
if(!$parsedComments) { | |
$data = $this->_multiFetch("item:" . $itemId . ":comments"); | |
$parsedComments = $this->_processComments($data); | |
$this->_redis->set("item:" . $itemId. " :parsedComments", serialize($parsedComments)); | |
} else { | |
$parsedComments = unserialize($parsedComments); | |
} | |
return $parsedComments; | |
} | |
/** | |
* Recursivly fetch the comment tree | |
* @param array $comments The first comment node | |
* @return array | |
*/ | |
private function _processComments($comments) { | |
$curr = array(); | |
foreach($comments as $comment) { | |
if($comment['hasChildren'] === "1") { | |
$data = $this->_multiFetch("thread:" . $comment['id']); | |
$curr[$comment['id']] = $this->_processComments($data); | |
} | |
$curr[$comment['parentId']][] = $comment; | |
} | |
return $curr; | |
} | |
/** | |
* Returns the comments according to a list of IDs stored in a Redis List | |
* @param String $keyName the Redis List key name | |
* @return array | |
*/ | |
private function _multiFetch($keyName) { | |
$commentList = $this->_redis->lRange($keyName, 0, -1); | |
$this->_redis->multi(); | |
foreach($commentList as $commentId) { | |
$this->_redis->hGetAll("comment:" . $commentId); | |
} | |
return $this->_redis->exec(); | |
} | |
/** | |
* Generates a unique ID for the comment | |
* return String | |
*/ | |
private function _getCommentId() { | |
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', | |
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), | |
mt_rand( 0, 0xffff ), | |
mt_rand( 0, 0x0fff ) | 0x4000, | |
mt_rand( 0, 0x3fff ) | 0x8000, | |
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) | |
); | |
//Could also be | |
//return $this->_redis->incr("comments:id:next"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
include "Comments.php"; | |
$comments = new Comments(); | |
$id = $comments->save(1, "Redis is awesome!", "Mikushi", 42); | |
$otherId = $comments->save(1, "i know!", "Asiendra", 43, $id); | |
$comments->save(1, "yeeha!", "Mikushi", 42, $otherId); | |
$comments->save(1, "mehehehe!", "Pythian", 44, $otherId); | |
$comments->save(1, "I'm jaleous!", "MongoDB", 45, $id); | |
$comments->save(1, "Me too!", "Memcached", 45, $id); | |
$comments->save(1, "Redis is the shit!", "Me", 46); | |
$comments->save(1, "Correction, Redis is The shit!", "OtherMe", 47); | |
$myComments = $comments->getComments(1); | |
printComments($myComments[1], 0, $myComments); | |
function printComments($thread, $margin, $container) { | |
foreach($thread as $comment) { | |
echo "<div style='border: 1px solid black; padding: 4px; margin-left: ".$margin."px'>" . $comment['username'] . " - " . $comment['content'] . "</div>"; | |
if($comment['hasChildren'] === "1") { | |
printComments($container[$comment['id']][$comment['id']], $margin+20, $container[$comment['id']]); | |
} | |
} | |
} |
I made a Ruby version based on this Gist. Here it is: https://gist.github.com/2766062
I made a C# version based on this Gist. Here it is: https://gist.github.com/2788831
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So, how would you go about deleting a node from the tree? (Especially if it has children).