1

For my school I need to add metadata into the tag of every node/page within the folder /course/

The metadata is the same for every page in this folder:

<meta class="swiftype" name="tags" data-type="string" content="course" />

This will allow me to use weighting for all our course pages in the Swiftype search app.

I've narrowed it down to the html.html.twig file in \core\modules\system\templates - where I believe I need to be adding something along the lines of:

if (preg_match('$^course/', $_SERVER['REQUEST_URI']])) { print_metatag }

But I won't be wanting to use print_metatag. Instead I should be using the fixed metadata referenced above.

Can anyone help me figure out what I need to use, and where, to apply this metadata to all those /course/ nodes/pages.

Many thanks!

  • First of all you can change \core\modules\system\templates\html.html.twig it's core template you should override it in your custom module or theme second thing this is if (preg_match('$^course/', $_SERVER['REQUEST_URI']])) { print_metatag } php can't be in twig. Did you use metatags module ? – berramou Nov 29 at 20:42
  • Welcome Dave 👋 would all of these be one content type that is only used in this place? or do you need to be doing it by path? – Nick Wilde Nov 29 at 20:56
  • @NickWilde it's for just one content type - called Instance. Every node of the Instance content type. – Dave Thackeray Nov 29 at 21:13
  • 1
    The simplest way would be to add the metatag in a node preprocess hook, then you don't need to check for a path or route parameters. The metadata will then bubble up on each page where the content type is displayed, see drupal.stackexchange.com/a/283609/47547 – 4k4 Nov 30 at 9:40
  • @4k4 this is an intriguing idea. Would the idea be to simply customise the code in the link you shared, and append it to the node.module file as per api.drupal.org/api/drupal/core%21modules%21node%21node.module/… – Dave Thackeray Nov 30 at 14:48
2

You won't be able to use PHP like that in Twig, but your logic isn't far off. One of the reasons Drupal 8 switched to using Twig was to avoid having potentially unsafe PHP in template files – this sort of thing generally belongs in a module rather than in a template, especially as it's not related to the presentation of content.

Drupal doesn't have a concept of folders, but we can look at the path of the content (e.g. /courses/drupal-development or /contact-us or /news/2018/cool-thing-that-happened) and work out if it's 'under' courses or not that way.

Something like this should work, but is untested:

/**
 * Implements hook_page_attachments().
 */
function YOUR_MODULE_NAME_page_attachments(array &$page) {
  $current_path = \Drupal::service('path.current')->getPath();
  $exploded_path = explode('/', $current_path);

  // Add Swiftype metatag, see https://swiftype.com/documentation/site-search/crawler-configuration/meta-tags.
  if ($exploded_path[1] === 'courses') {
    $metatag = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'tags',
        'data-type' => 'course',
        'class' => 'swiftype',
        'content' => 'course',
      ],
    ];

    $page['#attached']['html_head'][] = [$metatag, 'swiftype_course'];
  }
}

Or to do it by content type:

/**
 * Implements hook_page_attachments().
 */
function YOUR_MODULE_NAME_page_attachments(array &$page) {
  $node = \Drupal::routeMatch()->getParameter('node');

  if ($node instanceof \Drupal\node\NodeInterface) {
    if ($node->getType() === 'instance') {
      $metatag = [
        '#tag' => 'meta',
        '#attributes' => [
          'name' => 'tags',
          'data-type' => 'course',
          'class' => 'swiftype',
          'content' => 'course',
        ],
      ];

      $page['#attached']['html_head'][] = [$metatag, 'swiftype_course'];
    }
  }
}

If you need help setting up a module then take a look at https://befused.com/drupal/first-drupal8-module - but you can stop once you've created a your_module_name.module file – this is the file that the code above will go into.

  • 1
    CTI, eh? Always the cream of the crop. Phil - you are an absolute legend for attempting this! Thanks so much. Will give it a shot this weekend and see what occurs. I'm XAMPPed up so no damage will be done either way. Keep you updated; thanks so much for taking time out of your Friday night to help. – Dave Thackeray Nov 29 at 21:21
  • Hey @DaveThackeray, I saw Paul's bat-signal and thought I might be able to help out! I've just updated my answer having seen your comment that this is to be applied by content type really, rather than path. Doing it by content type makes things a lot more robust and cleaner feeling, as changing the URL structure won't now break functionality. – Phil Wolstenholme Nov 29 at 21:23
  • 1
    Do note that ``` if ($node = \Drupal::routeMatch()->getParameter('node')) { if ($node->getType() === 'instance') { ``` is a bit risky, better to do: ``` if ($node = \Drupal::routeMatch()->getParameter('node')) { if (is_obj($node) && $node->getType() === 'instance') { ``` Since on some routes (ie node revisions, the node parameter doesn't get upcasted and hence is an int). – Nick Wilde Nov 29 at 21:30
  • 1
    Ah, good point – this is why I'm primarily a frontender! It looks like that issue isn't fixed yet (see drupal.org/node/2730631) so I'll edit my answer to incorporate @NickWilde's suggestion. – Phil Wolstenholme Nov 29 at 21:34
  • 1
    I used a check for $node instanceof \Drupal\node\NodeInterface instead of is_object(), but I think both approaches would prevent the problem that Nick described – thanks for the suggestion! 👍🏻 – Phil Wolstenholme Nov 29 at 21:40
2

The answer linked in the comments is for a render array you build yourself. If you want to attach something to an existing render array you would need to look for a render element (#theme, #type, #markup) you can attach it to. You could look for such a render element in $variables, but a preprocess hook is an exception to this rule. Here the (only) officially supported method of attaching bubbleable metadata (#cache, #attached) is the top level of $variables, even if there is no render element. Drupal looks for such metadata and bubbles it up.

Example:

/**
 * Implements hook_preprocess_HOOK() for node.html.twig.
 */
function mytheme_preprocess_node(&$variables) {

  if ($variables['node']->getType() == 'article' && $variables['view_mode'] == 'full') {

    $metatag = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'tags',
        'data-type' => 'course',
        'class' => 'swiftype',
        'content' => 'course',
      ],
    ];

    $variables['#attached']['html_head'][] = [$metatag, 'swiftype_course'];

  }
}
1

So there are three methods really.

  1. I won't go over, since it is what Phil said

  2. Create a custom module extending metatag. This is in many way ideal, but is the most complicated method. Given your stated experience level, this is likely a challenge, and probably not worth it for such a static option.

  3. Use the html template you identified. For this just copy core/themes/classy/templates/layout/html.html.twig to %your_theme/templates/layout/ as html--node--instance.html.twig. Or if you you already have an html.html.twig in yuor theme, copy that to the new name. Then add your metatag directly into the new template.

Do note that any of those three options will require you to clear caches or they won't take effect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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