Moving Comments into a Block - Drupal 7

[Note: It looks like there's a new module, as of January 2013, Node Comment Block, which uses the technique outlined below to move comments into a block.]

Most of the time, Drupal's convention of printing comments and the comment form inside the node template (node.tpl.php) is desirable, and doesn't cause any headaches.

However, I've had a few cases where I wanted to either put comments and the comment form in another place on the page, and in the most recent case, I asked around to see what people recommended for moving comments out of the normal rendering method. I found a few mentions of using Panels, and also noticed the Commentsblock module that does something like this using Views.

However, I just wanted to grab the normal comment information, and stick it directly into a block, and put that block somewhere else. I didn't want Views' overhead, or to have to re-theme and tweak things in Views, since I already have a firm grasp of comment rendering and form theming with the core comment display.

So, I set out to do something similar to this comment on drupal.org (which was also suggested by Jimajamma on Drupal Answers).

First, I had to hide the comments from the normal rendering pipeline in node.tpl.php, which involved using template_preprocess_node() to set 'comment' to 0, and a check in node.tpl.php to make sure $content['comments'] would only be rendered if $comment evaluated to TRUE:

<?php
function THEMENAME_preprocess_node(&$variables) {
 
// For note nodes, disable comments in the node template.
 
if ($variables['type'] == 'note') {
   
$variables['comment'] = 0;
  }
}
?>

Then, I simply built a block in my custom module, and used the magic of comment_node_page_additions() to render the comments and comment form, just as they would render under the node, except in my own, spiffy comment block:

<?php
/**
 * Implements hook_node_view().
 */
function MODULENAME_node_view($node, $view_mode) {
  global
$node_comments;

 
// Store node comments in global variable so we can put them in a block.
 
if ($node->type == 'note' && isset($node->content['comments'])) {
   
$node_comments = $node->content['comments'];
  }
}

/**
 * Implements hook_block_info().
 */
function MODULENAME_block_info() {
 
$blocks['note_comments'] = array(
   
'info' => t('Note Comments'),
   
'cache' => DRUPAL_NO_CACHE,
  );
  return
$blocks;
}

/**
 * Implements hook_block_view().
 */
function MODULENAME_block_view($delta = '') {
  global
$user;
  global
$node_comments;
 
$block = array();
  if (
$delta == 'note_comments') {
   
// Get the active menu object.
   
if ($node = menu_get_object()) {
     
// Make sure user is viewing a note.
     
if ($node->type == 'note') {
       
$block['content'] = '';
       
// Set the title of the block.
       
$block['subject'] = NULL;
       
// Render the comments and comment form (access checks, etc. are done
        // by comment_node_page_additions()).
       
$block['content'] .= drupal_render($node_comments);
      }
    }
  }
  return
$block;
}
?>

Then, after a quick trip to the Configure > Blocks page, where I assigned my block to a region, I had a slick comments block that I could render anywhere!

Comments

First, I had to hide the comments from the normal rendering pipeline in node.tpl.php, which involved using template_preprocess_node() to set 'comment' to 0, and a check in node.tpl.php to make sure $content['comments'] would only be rendered if $comment evaluated to TRUE:

If you overwrite the values stored in $content['comments'] to 0 you could also just unset it. This way you wouldn't need another check in your node.tpl.php.

Or did I miss something here?

Correct; but I could still do it a bit differently. I just like simple if/endif statements in the template files. I also like sticking to one node.tpl.php with a tiny bit extra logic, rather than three or four template files, if I'm just making a tiny one line tweak.

trying to do this but the pagination for comments in the block no longer works.
any help/advice?

Hmm... didn't think of this, because on the site I'm using this tip on, there are never more than maybe 50-60 comments, so the pager never shows up. I don't have time to look into this now, but I'm sure there's a workaround for comments-in-a-block.

I guess the node->type checking should be left for the block configuration — it already allows for that out of the box. Instead, I guess it would be useful to form_alter the form configuration to only display options for node types that are actually configured to display comments.

You'd have to do that separately. I think you can set in the node type edit page whether you want the comment form to be shown on the same page. Turn that option off, then maybe you can manually print the node comment form in another block. I don't have the relevant docs in front of me, but a drupal_get_form of the relevant form ID might work, as long as you pass along the right parameters.

fairly new to drupal and as cool as this sounds, when you say "I simply built a block in my custom module" i have no idea what you mean. where would one stick this code?

Hi, thanks for this - works really well. Unfortunately I also need to get the pagination working for comments in a block. Any further thoughts on the matter? I'll continue to investigate and post back if I find something that works...

I haven't thought about comments in a block, since the site on which I'm using this only has maybe 5-10 comments per post. Please post back with updates if you find any kind of solution!

Ok, figured it out.

comment_node_page_additions has already been called by comment_node_view by the time our hook_block_view is calling it. The first time it is called it returns the correct comments but the second time it is called, this time by us in hook_block_view, the wrong comments are returned (I'm not sure why).

I got round this by implementing hook_node_view (called before hook_block_view) and setting the comments attached to the node to a global variable that is then available to us in hook_block_view so we can pass that to drupal_render.

Example code would be:

function MODULENAME_node_view($node, $view_mode) {
global $the_comments;
$the_comments = $node->content['comments'];
}

function MODULENAME_block_view($delta = '') {
global $user;
$block = array();
if ($delta == 'note_comments') {
// Get the active menu object.
if ($node = menu_get_object()) {
// Make sure user is viewing a note.
if ($node->type == 'note') {
$block['content'] = '';
// Set the title of the block.
$block['subject'] = NULL;
global $the_comments;
$block['content'] = drupal_render($the_comments);
}
}
}
return $block;
}

Actually using:

$the_comments = isset($node->content['comments']) ? $node->content['comments'] : '';

in MODULENAME_node_view will stop any errors on pages that don't have comments...

Thanks! I've just updated the post with this info; had to do this on the site I mentioned earlier because interest exploded today, and we were getting hundreds of (legit) comments on a few posts. Yowza!

Have you submitted this as a module? It would be really helpful for many others for Drupal 7.

Thanks. I did try that module but it only seemed to show the comments already made - but not the form for submitting comments. Did I miss something?

I install successfully the module given above, I've assigned the block into somewhere else in my page but it's not rendering. I have no idea why. I am currently using bootstrap 3. Please help.

I am writing a module to provide the option to put comment on each paragraph. I have added a new field to the comment table, called "paragraph_number", so in the comment form there is a field which asks users to enter the number of the paragraph number that they want to put comment on. As you know in node.tpl.php , we use print render $content['comments'], to show the comments and comment form,but I want to only show the comment form, and then in my module do a db_select and sort the comments based on a the paragraph_number and then show the comments. Now, my problem is that how can I only show the comment form? I have asked this question in Stckexchange and somene has suggested the following: Implement hook_node_view() and check for $node->type == 'article' and $view_mode=='full' then unset the comments as follows

unset($node->content['comments']['comments']);

But I could not make it work.

Do you know how can I make it work, also do you have any suggestions. I really need to make it work, This is my master project.

Dear Jeff,
I really like your solution, but I am using Drupal 8 and don't really know how to adjust it.
Would you please suggest your solution in D8 applicable version.
Thanks!