Change record status: 
Project: 
Introduced in branch: 
8.x
Introduced in version: 
8.0.0
Description: 

Summary

In Drupal 7, definition and altering of blocks are done through block specific mechanisms. A block was defined by implementing hook_block_info() providing the available deltas and the corresponding administrative labels. hook_block_view() was implemented to return the relevant render array. The block then could be configured in the UI to appear once per theme in a specific region -- the disabled blocks are visible in the UI in the "disabled" region.

In Drupal 8, the logic for blocks is provided by so called plugins (annotated classes in a specific namespace), the annotation providing the administrative label. The concept of delta is gone, the plugin is identified by its plugin id. The defining module does not matter, the plugin id must be unique across all modules. The build method of the plugin class provides the relevant render array. The block does not appear at all until it is placed; however it can be placed any number of times, it can appear in a theme any number of times. The placement information (theme, region, weight) and any block specific configuration information is saved in a configuration entity. This is the same plugin/configuration entity dual mechanism that is used for actions, editors, migrations, views etc.

API changes

  • hook_block_info() has been removed. Block types are now defined by adding a block plugin.
  • hook_block_configure(), hook_block_save(), and hook_block_view() have been replaced with block plugin methods.
  • hook_block_info_alter() has been removed. The administrative label can be changed with the new hook_block_alter(). Most other properties (cache, visibility etc) can be changed on the configuration entity save or load: hook_block_presave, hook_block_insert, hook_block_update (or even hook_block_load for very dynamic cases).
  • hook_block_list_alter() has been removed. First, as with every entity, there is the entity access layer: hook_entity_access or hook_block_access(). This is the recommended way for most cases. Also, if the core provided access logic (roles, paths, language) is adequate then see above about changing the visibility property in the configuration enttity.
  • hook_block_MODULE_DELTA_alter() has been replaced with hook_block_ID_alter() and hook_block_NAME_alter()

The above are the hooks normal modules should implement. However, in some cases overriding the default behaviors might be necessary and with Drupal 8 this is possible. At the end of this change notice we will list these.

Examples

Defining a block type

To define a block type, place a MyBlock.php plugin class file in the following directory structure:
my_module/src/Plugin/Block
(See the explanation of the PSR-4 standard for more information.) This plugin class will be automatically discovered and loaded when my_module is enabled.

Drupal 7

In book.module:

function book_block_info() {
  $block = array();
  $block['navigation']['info'] = t('Book navigation');
  $block['navigation']['cache'] = DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE;

  return $block;
} 

Drupal 8

The relevant parts of core/modules/search/src/Plugin/Block/SearchBlock.php:


/**
 * @file
 * Contains \Drupal\search\Plugin\Block\SearchBlock.
 */

namespace Drupal\search\Plugin\Block;

use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'Search form' block.
 *
 * @Block(
 *   id = "search_form_block",
 *   admin_label = @Translation("Search form"),
 *   category = @Translation("Forms")
 * )
 */
class SearchBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account) {
    return $account->hasPermission('search content');
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    return \Drupal::formBuilder()->getForm('Drupal\search\Form\SearchBlockForm');
  }
}

The administration label hook_block_info() is now added to the block plugin's annotation (a special section at the end of the documentation block wrapped in @Block()). (More about annotations and plugin discovery). The information in the annotation (for example, category) is not editable in the UI and is the same for every placement. If some data, like the book block mode can change between placements then it needs to go in the defaultConfiguration() method.

Configuration settings are declared in a defaultSettings() method; instance settings are declared in a defaultInstanceSettings() method in the configuration entity. @see #2136197: Move field/instance/widget/formatter settings out of annotation / plugin definition. No longer applicable: This method was called "settings" and was changed in #2062573: Add method defaultConfiguration() in ConfigurablePluginInterface, see also this change notice [#2088589].

The "admin_label" key was originally called "subject", it was changed in #1591806: Change block "subject" so that it's called a (admin_)label like everything else on the theme layer, and block plugins were given their own separate annotation in #2065721: Add a dedicated @Block plugin annotation.

Adding configuration options to a block type's form

Drupal 7

In book.module:

function book_block_configure($delta = '') {
  $form['book_block_mode'] = array(
// form definition omitted for brevity.
  );
  return $form;
}
function book_block_save($delta = '', $edit = array()) {
  $block = array();
  variable_set('book_block_mode', $edit['book_block_mode']);
} 

Drupal 8

In core/modules/book/src/Plugin/Block/BookNavigationBlock.php:

  function blockForm($form, &$form_state) {
    $form['book_block_mode'] = array(
      '#default_value' => $this->configuration['block_mode'],
// rest of the form omitted for brevity.
    );
    return $form;
  }
  public function blockSubmit($form, &$form_state) {
    $this->configuration['block_mode'] = $form_state['values']['book_block_mode'];
  }

Note that these added form elements allow the user to configure the settings added in defaultConfiguration(). Also note blockSubmit merely sets the configuration; the actual persisting of the configuration is done automatically.

Defining a block type's output

Drupal 7

The user login block is a much better example for this one. In user.module:

function user_block_view($delta = '') {
  $block = array();
  switch ($delta) {
    case 'login':
      if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
        $block['subject'] = t('User login');
        $block['content'] = drupal_get_form('user_login_block');
      }
  }
  return $block;

Drupal 8

In core/modules/user/src/Plugin/Block/UserLoginBlock.php:

class UserLoginBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account) {
    return (!$account->id() && !(arg(0) == 'user' && !is_numeric(arg(1))));
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    // Building of the form is omitted.
    return array(
      'user_login_form' => $form,
      'user_links' => array(
        '#theme' => 'item_list',
        '#items' => $items,
      ),
    );
  }
}

Note how access replaced the if – instead of returning an empty array, the build function is simply not called. This results in much cleaner code. (However, the build function can still return an empty array if it wants the block skipped.)

Altering the visibility of other modules' blocks

Drupal 7

In node.module:

/**                                                                              
 * Implements hook_block_list_alter().                                               
 */
function node_block_list_alter(&$blocks) {
  foreach ($blocks as $key => $block) {
// ...
      else {
        // This is not a node page, remove the block.
        unset($blocks[$key]);
        continue;
      }
  }
}

Drupal 8

In node.module:

/**                                                                              
 * Implements hook_block_access().                                               
 */
function node_block_access($block) {         
// …
    if (empty($allowed_types)) {
      // There are no node types selected in visibility settings so there is
      // nothing to do.
      return;
    }
// …
    return FALSE;
}

Note how the new hook only gets one block and it can return FALSE to deny access or NULL if it doesn't want to make a decision on access.

The meta hooks / mechanisms

As mentioned above, the default behaviors are modifiable via various mechanisms, mostly hooks. The need to use these mechanisms should arise very rarely and even then extreme caution is advised. If two modules try to replace the same functionality, conflicts might arise. Most modules (and developers) will expect the default mechanisms to work. Don't forget: with great power comes great responsibility.

  • By using hook_block_alter it is possible to change the class belonging to a given plugin id.
  • hook_block_alter itself is fired by the block plugin manager which can be replaced: it is a service and services can be altered by a ModulenameServiceProvider class implementing the ServiceModifierInterface.
  • By using hook_entity_info_alter it is possible to change the access, the storage and the view controller of the Block entity type.
Impacts: 
Site builders, administrators, editors
Module developers
Themers
Updates Done (doc team, etc.)
Online documentation: 
Generic online documentation done
Theming guide: 
Theming guide done
Module developer documentation: 
Module developer documentation done
Examples project: 
Examples for developers done
Coder Review: 
Coder review done
Coder Upgrade: 
Coder upgrade done
Other: 
Other updates done

Comments

jbrown’s picture

Note that blocks no longer appear automatically on the Blocks page - they have to be "Placed".

--
Jonathan Brown
http://jonathanpatrick.me/

Christopher James Francis Rodgers’s picture

Just out of curiosity,
(a curiosity that has consumed me for the last several hours)
where might I find out how to "place" a "plugin"
onto the Drupal 8 'Block layout' page;
as for example, the Core-Module Book's navigation plugin,
which I assume is possible,
since I did find BookNavigationBlock.php in the directory
{d8}\core\modules\book\src\Plugin\Block\

[Edited/ Addendum]
I stumbled across a screen-shot showing a sidebar-right block
called "Place blocks" on someone's "Block layout" page,
but I do not have that on my 'Block layout' page,
nor do I have a 'Place blocks' block
on my "Block layout" page under the column "Block".

Please advise.

Thanking you in advance.


All the best; intended.
-Chris (great-grandpa.com)
___
"The number one stated objective for Drupal is improving usability." ~Dries Buytaert *

Dave Reid’s picture

If you need to set your block's title dynamically, or force override, you can manually set $this->configuration['label'] inside the build() method. Views blocks currently do this.

rpmskret’s picture

@David Reid; setting the title in build() as you described did not work for me. But adding this function to my block class did:

public function defaultConfiguration() {
  return array(
    'label' => 'This is Freds label',
  );
}
leandro713’s picture

anybody knows a proper way to set programatically the block title in Drupal 8.2?

drupalninja99’s picture

Looks like '#title' is the winner in your return array.

steamx’s picture

Please note that even if this change as been posted on October 8, 2014 its example do not use the update code e.g. FormStateInterface $form_state$form_state instead of &$form_state

matthiasm11’s picture

Also take a look at the documentation for updated examples: https://www.drupal.org/developing/api/8/block_api.

ARUN AK’s picture

I am not able to find User Login block in Drupal 8 block configuration page.

Thanks and Regards
ARUN AK
www.developersdocs.com

andrewfn’s picture

This took me a couple of hours to find. (The problem is that I have been working with Drupal since 4.7 and was expecting it to work in a similar way.) Here is what you do:
On the bottom of the page admin/structure/block it says:
Disabled (Place block)
No blocks in this region
This is in fact untrue, since there are blocks in this region but they are hidden.
You can make them visible by pressing the (Place block) button, which will show them, and then placing them in a different region.

Christopher James Francis Rodgers’s picture

@AndrewFN:

Brilliant. You are the greatest. Thanks (I'm a D6.12 youngster, compared to you)

----

Drupal 8 - Display all of the additional pre-installed, and immediately available blocks from Drupal 8 Core, that are missing from the set of those displayed by default on the 'Block layout' page (admin/structure/block), by clicking any region's "Place block" button.

That will open up the "Place block" pop-up window with the full list of all the blocks available from the D8 Core modules that you presently have enabled, as well as any blocks from 'Contrib' modules you may have imported and enabled.

That is to say, additional blocks will show-up in the "Place block" window if you first "Enable" additional D8 Core modules that have blocks associated with them; and additional blocks will appear if you "Import" and "Enable" 'Contrib' modules that have blocks associated with them.

Please note that any changes you make to the default setup, as it first appears, will only affect the theme which is selected by the theme-tab at the top of the 'Block layout' page. You must set the blocks for each theme separately.

The 'Block layout' page-top tabs represent only those themes which have been "Installed", and will be listed at the top of the 'Appearance' page, under the heading 'Installed theme'.

Themes classified as 'Installed theme' means that the theme has not only been imported into your D8 codebase, but that you have also gone to the bottom of the Appearance page, under the heading 'Uninstalled theme', and clicked its button 'Install'.

(The 'Bartik' and 'Seven' themes are the only two themes pre-imported and pre-installed by D8 Core. 'Stark' is the only other theme pre-imported by D8 Core, but 'Stark' is not 'Installed' by default.)


All the best; intended.
-Chris (great-grandpa.com)
___
"The number one stated objective for Drupal is improving usability." ~Dries Buytaert *

jason_purdy’s picture

I was trying to follow this doc and it appears that the configuration method has changed a little bit, at least with 8.1. Instead of using just &$form_state, you use FormStateInterface $form_state (make sure to use Drupal\Core\Form\FormStateInterface; at the top of your plugin file.

So instead of this:

  function blockForm($form, &$form_state) {

This worked for me:

  function blockForm($form, FormStateInterface $form_state) {

Same goes for blockSubmit(). Hope this helps someone else.

jason_purdy’s picture

The example above doesn't seem to work in 8.1. Instead, it should go like this:

  /**
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account) {
    return AccessResult::allowedIfHasPermission($account, 'search content');
  }
wluisi’s picture

The example given in "Altering the visibility of other modules' blocks" didnt work for me in Drupal 8, running 8.1. I had to do something like this instead:


use Drupal\Core\Access\AccessResult;
use Drupal\block\Entity\Block;

/**                                                                              
 * Implements hook_block_access($block).                                               
 */
function my_module_block_access(Block $block) {   
  if ($block->id() == 'block_id_1' || $block->id() == 'block_id_2') {
    return AccessResult::forbidden();
  }
}