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()
, andhook_block_view()
have been replaced with block plugin methods.hook_block_info_alter()
has been removed. The administrative label can be changed with the newhook_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 evenhook_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
orhook_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 thevisibility
property in the configuration enttity.hook_block_MODULE_DELTA_alter()
has been replaced withhook_block_ID_alter()
andhook_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
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 aModulenameServiceProvider
class implementing theServiceModifierInterface
.- By using
hook_entity_info_alter
it is possible to change the access, the storage and the view controller of theBlock
entity type.
Comments
Note that blocks no longer
Note that blocks no longer appear automatically on the Blocks page - they have to be "Placed".
--
Jonathan Brown
http://jonathanpatrick.me/
Placing plugin (Block) on 'Block layout' page
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 *
Manually setting title in build()
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.
Setting the title in defaultConfiguration()
@David Reid; setting the title in build() as you described did not work for me. But adding this function to my block class did:
not working for me
anybody knows a proper way to set programatically the block title in Drupal 8.2?
Looks like '#title' is the
Looks like '#title' is the winner in your return array.
Please note that even if this
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
Also take a look at the
Also take a look at the documentation for updated examples: https://www.drupal.org/developing/api/8/block_api.
I am not able to find User
I am not able to find User Login block in Drupal 8 block configuration page.
Thanks and Regards
ARUN AK
www.developersdocs.com
Placing blocks for the first time
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.
D8-Display all available blocks using any 'Place block' button
@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 *
Configuration form update
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:
This worked for me:
Same goes for
blockSubmit()
. Hope this helps someone else.Also, blockAccess() has changed
The example above doesn't seem to work in 8.1. Instead, it should go like this:
hook_block_list_alter() D8 version doesn't work in 8.1
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: