Objectives & prerequisites
- Add several blocks and routes to my_block_demo to practice creating blocks.
- Use three different caching strategies.
- Know how to programmatically create a block.
- Basic understanding of the Cache API.
Activity
- Add a new block to my_block_demo named
UserInfoBlockthat displays the name of the current user:
You are currently logged in as <username>→ of course <username> should be replaced with the current user's actual username.
1. Retrieving the current user
The service you previously used to retrieve the current user within a class that extends ControllerBase is not available (by default) within a class that extends BlockBase. You can still use that same service, but you'll have to use the service container.
2. Caching
If you don't specify any caching properties, this block will be cached the first time it's shown to a user. As a result that same cached version will be shown to all other users (until the cache is cleared).
In this particular case, you want to cache this block for each user, so that this block is generated correctly for each user. To do so you need to specify a cache tag and a cache context.
One way to do this is to add the following to the $build render array before you return it:
$build['#cache']['contexts'][] = 'user';
$build['#cache']['tags'][] = 'user:' . $current_user->id();(assuming you previously retrieved the current user with the current_user service and assigned it to $current_user)
Activity
- Add a new block to my_block_demo named DateBlock. Every time you load a page with that block it should display the current date and time:
Hi, today is Sunday of week 23 of 2020. The time is 17:22:59.
1. Date and Time
You can use the time class, and retrieve it via the service container:
$current_datetime = \Drupal::time()->getCurrentTime();You can format dates with PHP's date() function and use one of PHP's built-in date formats:
$year = date('Y', $current_datetime);If you're stuck assembling the various date/time parts of this task, here's the full code you can use to retrieve and format the current date and time:
$request_time = \Drupal::time()->getCurrentTime();
$date = $this->t('Hi, today is @weekday of week @weeknum of @year.', [
'@weekday' => date('l', $request_time),
'@weeknum' => date('W', $request_time),
'@year' => date('Y', $request_time),
]);
$time = $this->t('The time is @time.', [
'@time' => date('H:i:s', $request_time),
]);
2. Caching
As we want this block to show the current date and time each time it is displayed, we don't want to cache this block at all.
You can do this with the max-age property:
$build['#cache'] = ['max-age' => 0];Activity
Open up the my_block_demo sample code, and investigate the following files:
This example does the following:
- Adds 3 routes that display something.
- A block that changes its output depending on which route it is being displayed.
Note:
The example also adds a controller with 3 public methods (one for each route) and 1 private method (only used inside the class; shows an image in small, medium, or large format). None of this controller's code has any impact on the BedSizeBlock.
Activity
Inspect the BedSizeBlock class definition. What service is used to retrieve the current route? Look this service up in the reference documentation and have a look at which other interesting methods might be available. Notice how $build['#markup'] depends on the current route. The sample code uses a switch case statement, but the same could have been achieved with if/then/else statements.
In this particular case, this block must be cached by route: when the block is displayed on a page with route /bed/small, the block's text should be this bed is too small. when the block is displayed on a page with route /bed/large, the block's text should be this bed is too large …
The way to do this is slightly more involved than adding cache tags or cache contexts. You can't add your cache tag, or replace any tags that may already be there (from the parent class); you need to merge a new tag with the tags that came from the parent class:
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['route.name']);
}Going into the details of merging cache contexts is beyond the scope of this course. Just remember that you can cache blocks per route, and that this code snippet is the way to do so.
Summary
- You can cache blocks per user.
- You can stop a block from being cached by setting
max-ageto 0. - You can cache blocks per route by merging
['route.name']with the cache contexts your retrieve from the parent class (BlockBase).