In D6 you could easily render a block by doing this:

  $block = module_invoke($module, 'block' ,$delta);
  $output = theme('block', $block);

In D7, module_invoke($module, 'block' ,$delta); returns a non-renderable array and there is no easy way to convert that array into something that drupal_render() can handle correctly. The only way to do this is to take the result of the module_invoke(...) and manually rebuild that array into a renderable array using bits and pieces of code form _block_render_blocks() The closest I have come to getting this to work is:

    $array = module_invoke('views', 'block_view', $display);
    // == array('content' => '...', 'subject' => '...')
    $block['content'] =$array['content']; 
    $block['#block'] = new StdClass();
    $block['#block']->subject = array('subject');
    $block['#block']->region = '';
    $block['#block']->module = 'views';
    $block['#block']->delta =  $display;
    $block['#theme_wrappers'][] = 'block';
    $output .= drupal_render($block);

...although even this still doesn't work (trying to figure out why.

All this said, I might be just be missing something because I'm still learning about renderable arrays so please feel free to set me straight and downgrade/close this issue, but if there is no good way to do this, then it is a regression from D6 and therefore critical.

Comments

Damien Tournoud’s picture

Category: bug » support
Priority: Critical » Major

I'm pretty sure you will figure it out :)

bleen’s picture

Category: support » bug

Damein ... I've been digging through this since yesterday afternoon, and I'm convinced there is no way to actually render a block without completely building a renderable array from scratch. If that's true, wouldn't this be considered a regression and therefore a bug?

If you have a suggestion as to what I may be missing, I'm all ears...

Damien Tournoud’s picture

Category: bug » support

@bleen18: the block module provides no API whatsoever to render a single block. You are on your own here. That said, it shouldn't be that hard.

$block = block_load($module, $delta);
$output = drupal_render(_block_get_renderable_array(_block_render_blocks(array($blocks))));
StevenPatz’s picture

Category: support » bug
Priority: Major » Normal
bleen’s picture

Category: bug » support

Wow ... that's a bit round-about, but it certainly works. Thanks Damien

Nikdilis’s picture

Does this require an update of the documentation in the module_invoke api for D7?
http://api.drupal.org/api/drupal/includes--module.inc/function/module_in...

Nikdilis’s picture

#3:
Little typo:
This one works and renders block 5:

$block = block_load('block', '5');
$output = drupal_render(_block_get_renderable_array(_block_render_blocks(array($block))));
print $output;
j0rd’s picture

It would be nice to get a single function to do this common task in Drupal.

Views for one for rendering views, why not block module?

jeffrey.dalton’s picture

Is it safe to use _block_get_renderable_array() and _block_render_blocks() ?

Aren't these private functions? In researching this issue I have seen countless threads of people struggling with this change in D7...

  • either having to use the module_invoke method which gets the guts of a block but doesn't actually render as a block with contextual links and block theme wrapper markup.
  • or trying to hand build a renderable array for each block (cumbersome at best)
  • or using the method in this thread that relies on private functions?

Guidance on this is much appreciated.

bdurbin’s picture

Here's a stab at a simple helper function, based on the comments above, that would be nice to have in the D7 blocks module. We're finding something similar to be useful in a current D7 project:

/**
 * Gets the rendered or render-ready contents of a block.
 *
 * @param string $module
 *   The machine name of the module that provides the block.
 * @param mixed $delta
 *   The delta of the desired block.
 * @param bool $render
 *   Whether or not to run the block through render(). Defaults to TRUE.
 *   Set this argument to FALSE if you wish to modify the render-ready block 
 *   data before passing to render().
 * @return mixed
 *   If $render is TRUE, the string representing the rendered block contents.
 *   If $render is FALSE, the render-ready block data array.
 * @see http://drupal.org/node/957038
 */
function block_embed_block($module, $delta, $render = TRUE) {
  $block = ($render) ? '' : array();
  
  $block_info = block_load($module, $delta);
  if ($block_info->bid) {
    $block = _block_get_renderable_array(_block_render_blocks(array($block_info)));    
    if ($render) {
      $block = render($block);
    }    
  }

  return $block;
}
j0rd’s picture

I'd like this integrated in to the block.module as well. Would make a lot of peoples lives easier.

bleen’s picture

Version: 7.x-dev » 8.x-dev
Issue tags: +Needs backport to D7

bdurbin: can you submit your function as a patch? We need to fix this for D8 before dries/webchick would consider adding it to D7

chrisjlock’s picture

Use print render() to render a block from module_invoke().

for example:

<?php 
$block = module_invoke('views', 'block_view', 'rotation_image-block');
print render($block);
?>
bleen’s picture

clock: its been a while since my original post, but as I recall your suggestion didnt work ... hence the problem

chrisjlock’s picture

Possible this has been fixed through a related issue then, as this code is currently working on 7.2

Nikdilis’s picture

@clock:
your solution doesn't seem to work for me. Not even in Drupal 7.4

pfrenssen’s picture

This seems to work fine in D7 and does not call any private functions.

function MODULE_render_block($module, $delta) {
  $block = module_invoke($module, 'block_view', $delta);
  $block['module'] = $module;
  $block['delta'] = $delta;
  $vars = array('elements' => array('#block' => (object) $block, '#children' => render($block['content'])));
  return theme('block', $vars);
}
Nikdilis’s picture

//to show Block 26 in Drupal 7
$block = module_invoke('block', 'block_view', '26');
print $block['content'];

See also: http://www.werockyourweb.com/drupal-block-in-node

I think this issue can be closed now!

Nikdilis’s picture

Version: 8.x-dev » 7.x-dev
Category: support » bug
Status: Active » Closed (fixed)
Nikdilis’s picture

see also: http://drupal.org/node/360605 in case that 'strict warning'-errors are shown when applying #18 (are only shown when in Configuration / Logging and errors the option 'all messages' is selected)

bleen’s picture

Version: 7.x-dev » 8.x-dev
Status: Closed (fixed) » Active

Nikdilis: #18 does not work. It returns a $block array with no content in 'subject' or 'content'. As well as throwing this error:

Notice: Trying to get property of non-object in block_block_view() (line 245 of /..../modules/block/block.module).
Nikdilis’s picture

After further testing in Drupal 7.5 I agree that there is no (reasonable) way to programmatically render a block with module_invoke. See also: http://drupal.org/node/126070 - module_invoke('block', 'view', ...) doesn't return subject. This issue is from March 8, 2007!!!

However, in Drupal 7.5 the following works:


//to show Block '15' (Custom Block) without title:
$block = module_invoke('block', 'block_view', '15');
//to show the title: ???????
//   See: http://drupal.org/node/126070 - module_invoke('block', 'view', ...) doesn't return subject
//to show the content of the block - no render!:
print $block['content'];

//--------------------------------------------------------------------------

//to show Block 'recent blog posts' - see also: http://drupal.org/node/957038#comment-4595150
$block = module_invoke('blog', 'block_view', 'recent');
//to show the title of the block:
print $block['subject'];
//to show the content of the block - render must be used:
print render($block['content']);

David_Rothstein’s picture

Something along the lines of #10 looks good (at least as a first step for D8), and is a good candidate to backport to D7.

On a quick glance, it seems like it takes care of all the things that need to be (title vs subject, making sure that drupal_alter() gets called) in order to render the block properly.

But render() should be replaced with drupal_render().... or possibly removed from this function entirely? (There could be separate functions for getting the renderable array and rendering it rather than the $render parameter used here, or just assume that once you have a renderable array you don't even need another helper function at that point, because calling drupal_render() explicitly when you want to render something is pretty common).

Roensby’s picture

Here is something that might help others:
The suggestion in #3 is to use this function to render a block:

$block = block_load($module, $delta);
$replacement = drupal_render(_block_get_renderable_array(_block_render_blocks(array($block))));

However, when using it, I get a memory allocation error, caused by memory overflow.

In this comment (http://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_r...) it is explained that the use of drupal_render in a theming function will not work. Instead, use drupal_render_children.

In my particular situation, I render a block in hook_filter_info to substitute certain text strings with blocks. Replacing drupal_render with drupal_render_children fixes it.

benjy’s picture

In D8 this can be achieved with:

print drupal_render(entity_view(block_load($entity_id), $display_mode));

Where $entity_id is the machine name of the block and $display_mode is an available display like "full" or "teaser"

If we added any wrapper at all it would only be for the entity_view() and block_load() calls but i'm not sure if we want to add that or not? I don't mind either way.

benjy’s picture

Version: 8.x-dev » 7.x-dev
Issue summary: View changes
Issue tags: -Needs backport to D7

Previous comment shows how to do this in D7. It's not really something we can backport. There are a few suggestions above which would for D7.

sobi3ch’s picture

Version: 7.x-dev » 8.0.x-dev
Issue tags: +#block

...and after all this long months, do we have some solution at least for D8? Or this should go to 8.1?

sobi3ch’s picture

Would be great to have consistent functionality same as in nodes (http://drupal.stackexchange.com/a/53009/7010)
E.g.:

$block = block_load($module, $delta);
print drupal_render(block_view($block));
David_Rothstein’s picture

Version: 8.0.x-dev » 7.x-dev

#25 still seems to work fine for Drupal 8, except block_load() is now replaced with the lovely \Drupal\block\Entity\Block::load()...

donquixote’s picture

Solutions for Drupal 8 and Drupal 7 would look quite different anyway.
So should we split this up into one D7 issue and one D8 issue?

This one has been mostly about D7, so let's keep it that way.

EDIT: This is the same as @David_Rothstein already says one comment up.
Just saying IF someone thinks this still applies to D8 for whichever reason, it should be a distinct issue.

donquixote’s picture

@Nikdilis (#7):

$block = block_load('block', '5');
$output = drupal_render(_block_get_renderable_array(_block_render_blocks(array($block))));
print $output;

This won't work because the parameter to drupal_render() is by-reference.

Better:

$block = block_load('block', '5');
$block_element = _block_get_renderable_array(_block_render_blocks(array($block)));
$output = drupal_render($block_element);
print $output;

Still this causes some problems for me.
In template_preprocess_block() it is assumed that a block object has a ->region. In _block_render_blocks() it is assumed that it has a ->title. In i18n_block_translate_tab_access() it is assumed that it has an ->i18n_mode.
If the block does not exist, then block_load() will return a stub block, which only has ->module and ->delta.

Better, similar to @bdurbin (#10):
Note that I use if (!empty($block->bid)), instead of just if ($block->bid), to prevent a notice if the property does not exist.

$block = block_load('block', '5');
if (!empty($block->bid)) {
  $block_element = _block_get_renderable_array(_block_render_blocks(array($block)));
  $output = drupal_render($block_element);
  print $output;
}

Also note that block_load() fetches information from the database that is saved per theme, and that might not be saved at all for newly defined blocks. So even if hook_block_info() exposes a block, block_load() might return nothing.
(not sure exactly about this statement)

I am not even sure that we need anything from the block database table, when rendering a block programmatically.
E.g. block visibility per page probably should not apply to this case, where we are already in control in code.
I think all we really need is the information from hook_block_info(). The rest could be filled with placeholder values, instead of loading it from the database.

donquixote’s picture

@bdurbin (#10):

/**
 * [..]
 * @return mixed
 *   If $render is TRUE, the string representing the rendered block contents.
 *   If $render is FALSE, the render-ready block data array.
 * [..]
 */
function block_embed_block($module, $delta, $render = TRUE) {
  [..]

I don't like the flexible return type of this function. I think it is a bad practice that we should unlearn.
Better have separate functions per return type.

donquixote’s picture

@pfrenssen (#17):

  $block = module_invoke($module, 'block_view', $delta);
  $block['module'] = $module;
  $block['delta'] = $delta;
  $vars = array('elements' => array('#block' => (object) $block, '#children' => render($block['content'])));
  return theme('block', $vars);

Look interesting, but
- it skips the block cache (which might be intended or not).
- it perhaps skips some alter hooks that would otherwise be called.
- it will again cause a notice in template_preprocess_block() if ->region is missing. And perhaps in other places for other missing properties.

donquixote’s picture

Re #31:

Btw with block_load() the region name will be unpredictable because different database records exist per theme.
Therefore the template suggestions added in template_preprocess_block() will be unpredictable.

Lesson learned: This system is fragile!