1

I want to remove the

<link rel="canonical" href="/taxonomie_term_url">

From my <head> section

and add

<link rel="next" href="base_url?page=1" />

on the first page

<link rel="prev" href="base_url" />
<link rel="next" href="base_url?page=2" />

on the second

and on the last page only

<link rel="prev" href="base_url?page={x}" />

I tried to do it in a page_attachment_alter() hook but there were no view attachments. Than I tried to do it in a view pre_render / post_render / pre_build / post_build hook. I'm pretty sure this is the false tendency, though.

if($view->current_display == 'block_pager') {
  $links = $view->getBaseEntityType()->get('links');
  if (array_key_exists('canonical', $links)) {
    $links =  array_filter($links, function ($link) {
      return $link !== 'canonical';
    }, ARRAY_FILTER_USE_KEY);
    $view->getBaseEntityType()->set('links', $links);
  }
}

But that gives me

Drupal\Core\Entity\Exception\UndefinedLinkTemplateException: No link template 'canonical' found for the 'node' entity type in Drupal\Core\Entity\Entity->toUrl() (line 214 of core/lib/Drupal/Core/Entity/Entity.php).
Drupal\node\Controller\NodeViewController->view(Object, 'full', NULL)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 144)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 64)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 652)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

And adding those rel"next/prev" stuff I tried to include them in my html.html.twig but I have no pager context there.

I have found a solution about those next/prev stuff

global $pager_total, $pager_page_array;
  $current_path = Url::fromRoute('<current>');
  $path = $current_path->toString();
  if ($path == '/startseite') {
    $path = '/';
  }
  if ($pager_page_array[0] == 1) {
    $prev = '';
  }
  else {
    $prev = '?page=' . $page = $pager_page_array[0] - 1;
  }
  if ($pager_page_array[0] == 0) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'next',
        'href' => $path . '?page=' . $next = $pager_page_array[0]+1,
      ],
    ], 'rel_next_first'];
  }
  if ($pager_page_array[0] != 0 && $pager_page_array[0]+1 != $pager_total[0]) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'next',
        'href' => $path . '?page=' . $next = $pager_page_array[0]+1,
      ],
    ], 'rel_next'];
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'prev',
        'href' => $path . $prev,
      ],
    ], 'rel_prev'];
  }
  if ($pager_page_array[0]+1 == $pager_total[0]) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'prev',
        'href' => $path . $prev,
      ],
    ], 'rel_prev_last'];
  }
  if ($pager_page_array[0] > 0) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'canonical',
        'href' => $path . '?page=' . $pager_page_array[0],
      ],
    ], 'canonical_url'];
  }

This is the the point where this ghost <link rel="canonical"> is created. At Line 69 NodeViewController::view()

 if ($this->currentUser->isAuthenticated() || $url->access($this->currentUser)) {
   // Set the node path as the canonical URL to prevent duplicate content.
   $build['#attached']['html_head_link'][] = [[
     'rel' => $rel,
     'href' => $url->toString(),
   ], TRUE,];
 }

Now if I wrap this snippet with

if (rel !== 'canonical') {}

It works and that ghost attachment is gone. But hacking core is real evil, so how do I achieve this in a clean or almost clean way?

1 Answer 1

1

To add next and prev link tags to your add use a page_attachment_alter hook.

/**
 * Implements hook_page_attachments_alter().
 */
function my_module_base_page_attachments_alter(array &$attachments) {      
  global $pager_total, $pager_page_array;
  $current_path = Url::fromRoute('<current>');
  $path = $current_path->toString();
  if ($path == '/startseite') {
    $path = '/';
  }
  if ($pager_page_array[0] == 1) {
    $prev = '';
  }
  else {
    $prev = '?page=' . $page = $pager_page_array[0] - 1;
  }
  if ($pager_page_array[0] == 0) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'next',
        'href' => $path . '?page=' . $next = $pager_page_array[0]+1,
      ],
    ], 'rel_next_first'];
  }
  if ($pager_page_array[0] != 0 && $pager_page_array[0]+1 != $pager_total[0]) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'next',
        'href' => $path . '?page=' . $next = $pager_page_array[0]+1,
      ],
    ], 'rel_next'];
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'prev',
        'href' => $path . $prev,
      ],
    ], 'rel_prev'];
  }
  if ($pager_page_array[0]+1 == $pager_total[0]) {
    $attachments['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'prev',
        'href' => $path . $prev,
      ],
    ], 'rel_prev_last'];
  }  
}

For that duplicate canonical I had to alter the entity.node.canonical route controller...

<?php

namespace Drupal\my_module\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Class RouteSubscriber.
 *
 * @package Drupal\mylife_base\Routing
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    $hmm = $collection;
    if ($route = $collection->get('entity.node.canonical')) {
      $route->setDefault('_controller', '\Drupal\my_module\Controller\NodeViewController::view');
    }
  }
}
1
  • I had to replace : $current_path = Url::fromRoute('<current>’); by $current_path = \Drupal\Core\Url::fromRoute('<current>’); And I need to disable cache in Views otherwise the module didn’t update the attribute. Aug 30, 2018 at 13:23

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.