Replace placeholder in custom block with preprocess hook
Question
This is the first time I'm using the preprocees hook, apologies in advance.
I added a placeholder, {{user-uid}}
, in a custom block body field, <a href="advisor/{{user-uid}}">Advisor Page</a></h5>
.
My code is very simple, I have this hook theme.
function boostrap_kampuster_preprocess_block(&$variables) {
if (isset($variables['content']['#block_content'])) {
$content = $variables['content']['#block_content'];
/** custom block ID is 4 **/
if( method_exists($content,'id') && $content->id() == '4') {
$body = $variables['content']['body'][0]['#text'];
$vars['user'] = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
$body=str_replace('{{user-uid}}',$vars['user'],$body);
}
}
}
I am not getting any errors but the function is not doing what it is supposed to do and replace {{user-uid}}
with logged in user's ID either.
FYI, I cleared caches several times.
Solution
Recommend using @4k4's answer instead.
There are a few issues here:
- You're setting your
$body
variable, but not setting the change in your$variables
array. You can fix this by using a reference, update with$body = &$variables['content']['body'][0]['#text'];
- You're replacing with the user object rather than the user id. You can't insert an object into the middle of a string. You can get the id of the current users with just
\Drupal::currentUser()->id()
. - I'd recommend not setting the current user to
$vars['user']
in a preprocess function when you're using$variables
as the accepted parameter. It's pretty common to use$vars
as a shorthand in a preprocess$variables
function and randomly setting$vars['user']
here is definitely going to cause confusion for anyone reading this in the future. - I'd also recommend not using
{{ VARIABLE }}
as a custom placeholder since D8 templates use twig and that is twig syntax. For the same reasons you wouldn't use<?php echo $VARIABLE; ?>
as your custom placeholder.
Your code updated:
function boostrap_kampuster_preprocess_block(&$variables) {
if (isset($variables['content']['#block_content'])) {
$content = $variables['content']['#block_content'];
/** custom block ID is 4 **/
if( method_exists($content,'id') && $content->id() == '4') {
// These lines updated.
$body = &$variables['content']['body'][0]['#text'];
$body = str_replace('{{user-uid}}', \Drupal::currentUser()->id(), $body);
// As 4k4 pointed out - you must set a cache context or this won't work as expected.
$variables['#cache']['contexts'][] = 'user';
}
}
}
OTHER TIPS
In addition to @sonfd answer you also need to consider caching. The easiest way would be to add a cache context:
$variables['#cache']['contexts'][] = 'user';
But then the database needs to store a cached version for each user.
Better for performance would be to postpone replacing the placeholder by attaching a lazy builder to the render array:
function mytheme_preprocess_block(&$variables) {
$variables['#attached']['placeholders']['{{user-uid}}'] = [
'#lazy_builder' => ['_mytheme_replace_uid', []],
];
}
which then returns the markup of the user UID:
function _mytheme_replace_uid() {
return [
'#markup' => \Drupal::currentUser()->id(),
'#cache' => ['contexts' => ['user']],
];
}
Drupal 9
Drupal 9 requires using only trusted callbacks. So define a class implementing TrustedCallbackInterface or RenderCallbackInterface:
mytheme/src/MythemeLazyBuilders.php:
<?php
namespace Drupal\mytheme;
use Drupal\Core\Render\Element\RenderCallbackInterface;
class MythemeLazyBuilders implements RenderCallbackInterface {
public static function replaceUid() {
return [
'#markup' => \Drupal::currentUser()->id(),
'#cache' => ['contexts' => ['user']],
];
}
}
and add it to the render array:
function mytheme_preprocess_block(&$variables) {
$variables['#attached']['placeholders']['{{user-uid}}'] = [
'#lazy_builder' => ['\Drupal\mytheme\MythemeLazyBuilders::replaceUid', []],
];
}