6

Several fields in a node need to be displayed in a particular way depending upon which newsletter body they are a part of. How do I pass a variable from the enclosing newsletter body to enclosed fields so that I can inform the field of its context and control the way the field is displayed?

I have a theme that has preprocess functions both for the newsletter body and for fields contained within the body.

I want to set a variable in the body preprocess function, and its related twig template, that can be inspected by the field preprocess function, and/or its own template.

In other words, I want each field to be aware of the body context in which it is being rendered.

Specifically, I have mytheme_preprocess_simplenews_newsletter_body() that sets a variable ($variables['summons'] = 'placeholder';) which can be inspected (and also set) by the theme's simplenews-newsletter-body.html.twig template, but (because it's in a different render array) is subsequently neither visible to mytheme_preprocess_field() nor field--node--title.html.twig.

How do I communicate between the two render arrays?

Note that

  1. There are apparently global variables like $db_is_active and $is_admin.

  2. Sometimes, it is possible to achieve this kind of thing by cascading CSS class attributes from a containing element to a contained element. This is not generally satisfactory and in this particular case, it won't wash on different target mail clients unpredictable treatment of CSS.

1
  • I've tried $variables['context'] but it doesn't work. Using drupal_static() seems the only way - ridiculous
    – Codium
    Commented Dec 4, 2016 at 14:48

3 Answers 3

4

The solution I am working with is to use drupal_static(); simplified essentials in code below - take care with the &ampersands. It is giving me control where I need it.

So, using this approach, I can - taking their global context into account - per field, set variables in the usual way e.g. in mytheme_preprocess_field() subsequently to be used in a related template like field--node--title--agenda.html.twig

function mytheme_preprocess_simplenews_newsletter_body(&$variables) {
  // prepare to alter static var ...
  // get a reference binding to static var
  $var = &myvariable_function();
  // alter static var
  $var = 'simplenews_newsletter';
}

function mytheme_preprocess_field(&$variables, $hook) {
  // prepare to read static var
  // assign static var
  $var = myvariable_function();
  // $element is the render array for the field
  $element = $variables['element'];
  // filter those cases where it matters that we're theming a newsletter
  if ($element['#view_mode'] == 'email_html' and $var == 'simplenews_newsletter') {
    // Do stuff here like setting a variable for twig ...
    $variables['summons'] = 'some text';
  }
}
function &myvariable_function() {
  $var = &drupal_static(__FUNCTION__);
  if (!isset($var)) {
    // generate contents of static variable
    $var =  'initial_value';
  }
  return $var;
}

I am sure that there are other ways of communicating common context between different levels of a page's render hierarchy. Let me know how you'd do it.

1
1

I use this code:

/**
 * Helper function to pass slider variables from preprocess_page to preprocess_page_title_custom
 * @param  string $new_image Image URL
 * @return array             Images URL Array
 */
function _core_slider_images($new_image = NULL) {
  $vars = &drupal_static(__FUNCTION__, array());
  // If a new value has been passed add to the array.
  if ($new_image) {
    $vars[] = $new_image;
  }
  return $vars;
}

From this template:

In page.html.twig we have access to the $node var, and we need to pass the values to another template that don't have access to this var. We will search the values that we need to pass and call the _core_slider_images(), this function will be the bridge between the two templates (preprocess functions)

/**
 * Implements hook_preprocess_page().
 */
function MODULE_preprocess_page(&$variables) {
  // ...
  // Sending Images URL and titles from page to page_title.
    for ($i = 0; $i < $cant; $i++) {
      // Adding the images to the static function.
      _core_slider_images($variables['node']->field_image[$i]->entity->url());
    }
  }

To this template:

page-title-custom.html.twig is a custom template and don't have access to $node. As hook_preprocess_page() is called first we have stored the $node values in a static var, and we only need to call the _core_slider_images() function.

/**
 * Implements hook_preprocess_HOOK().
 */
function MODULE_preprocess_page_title_custom(&$variables) {
  // Searching the variables for sending to the page-title-custom template.
   $images = _core_slider_images();
}
0
0

I'm still new to D8, so I could easily be missing a trick. Generally in this situation you want to look for the most specific hook that gives you access to everything you want to alter. So altering at the field level is too specific and at the page level (for example) is too general.

Unfortunately MailEntity isn't a real (Drupal) entity, so you can't use standard hooks to alter its render array. It doesn't invoke any custom hooks I can see to allow you to alter the content either. In D7 in this situation I'd be tempted to use hook_page_alter() which is dirty but would work I think, but that's been removed from D8.

You could always override the MailEntity class and provide your own customisations directly there, but needing to do that for theme changes does seem a bit wrong to me.

5
  • My OP poses my question as clearly as I can: you've quoted the nub of it in your post. In this case the "body" is not the body field of a node; it's the body of the newsletter that contains the displayed view_mode of the node. If you were to look at the twig debug info you'd see clearly how simplenews-newsletter-body.html.twig contains an hierarchy of entities; entity relationships; taxonomy terms; and fields that make up the email "body". Even if I were to employ ` hook_entity_view_alter()` I'd still need to know the context of the body in which the entities were being displayed.
    – iainH
    Commented Jan 15, 2016 at 16:33
  • @iainH You've described the mechanism you want, just not what exactly you're using it for: maybe there's another way to reach your ultimate goal. I'm still not clear what you really mean by the context of the body in which the entities [are] being displayed. I'll try to update the answer with what seems like the way forwards to me. (Btw I don't see any twig debug info??)
    – Andy
    Commented Jan 15, 2016 at 18:28
  • I added an initial paragraph in the question. Hope that makes the case clearer.
    – iainH
    Commented Jan 15, 2016 at 18:38
  • I appreciate your time. re twig debug info ... a bit lengthy to include a meaningful snippet in this forum but if I show you the enclosing template, I think this illustrates the hierarchical nature of the newsletter body. This is simplenews-newsletter-body.html.twig: <h2>{{ title }}</h2> {{ build }} - that's it.
    – iainH
    Commented Jan 15, 2016 at 18:52
  • @iainH it's very clear now I've looked at the module itself, thanks! It feels to me like the code in MailEntity.php is missing at least one alter hook, but with D8 I'm still not confident what the Drupal Way is yet. I'd rather not be overriding plugins for theme level changes tho :/
    – Andy
    Commented Jan 15, 2016 at 19:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.