How to implement the best SEO practices 2020 using Drupal 8

seo search engine optimization

In order to create a 2 column custom template just like the one above that renders an image next to the body text, you should use these 2 Bootstrap columns:

IMAGE inside: .col-md-5
Body TEXT inside: .col-md-7

Im not a great fun of Panels, I simply implement such flexible templates using simply the TWIG Templates. I wanted to achieve a flexible theme that shows the field image next the field body text but only if the user uploads an image, if the image is not present then it only render the Field Body text, in my case the fields are multiples, this is the main code in the template file, if you are interested in getting all my code please read my article on how to Install Drupal 8 using Docker and you can download this code from my Github repository:

<div{{ attributes.addClass(classes) }}>
  {% for item in items %}
    <div{{ item.attributes.addClass('field--item') }}>
      {% if item.service_body_image %}
      <div class="block-6 views-row">
        <div class="description col-sm-5">
          <figure class="img-polaroid">
            {{ item.service_body_image }}
          </figure>
        </div>
        <div class="field-content related-services list col-sm-7">
          {{ item.content }}
        </div>
      </div>
      {% else %}
        {{ item.content }}
      {% endif %}
    </div>
  {% endfor %}
</div>

As you can see it shows the Service body Image only if present otherwise it renders only the Body field. I placed this code inside: templates/field/field--node--body--service.html.twig

I set the variable 'service_body_image' inside the Field preprocess:

<?php
/**
 * @file
 * Contains \Drupal\maria_consulting\Plugin\Preprocess\Field.
 */

namespace Drupal\maria_consulting\Plugin\Preprocess;

use Drupal\bootstrap\Plugin\Preprocess\PreprocessBase;
use Drupal\bootstrap\Plugin\Preprocess\PreprocessInterface;
use Drupal\bootstrap\Utility\Variables;

/**
 * Pre-processes variables for the "field" theme hook.
 *
 * @ingroup plugins_preprocess
 *
 * @BootstrapPreprocess("field")
 */
class Field extends PreprocessBase implements PreprocessInterface
{

  /**
   * {@inheritdoc}
   */
  public function preprocessVariables(Variables $variables)
  {

    $element = $variables['element'];

   if ($element['#field_name'] == 'body' && $element['#bundle'] == "service") {
      $node = $element['#object'];
      $field_image = $node->get('field_image');
      $image_iterator = $field_image->getIterator();
      $total = $element['#items']->count();
      for ($delta = 0; $delta < $total; $delta++) {
        if ($image_iterator->offsetExists($delta)) {
          $element_image = $image_iterator->offsetGet($delta);
          $element_image_view = $element_image->view();
          $raw_html = render($element_image_view);
          $markup = \Drupal\Core\Render\Markup::create($raw_html);
          $variables['items'][$delta]['service_body_image'] = $markup;
        }
        else {
          $variables['items'][$delta]['service_body_image'] = FALSE;
        }
      }
    }
    parent::preprocessVariables($variables);
  }

}

In Drupal 7 we were used to place the preprocess functions all inside the template.php but in version 8 we create a Plugin for each Preprocess function. To preprocess the field body, I added a simple IF inside preprocessVariables(). The Plugin must be placed inside this folder:

/drupal/htdocs/themes/maria_consulting/src/Plugin/Preprocess

And the Plugin class must extends PreprocessBase and implements PreprocessInterface and use this Annotation:

/**
 * Pre-processes variables for the "field" theme hook.
 *
 * @ingroup plugins_preprocess
 *
 * @BootstrapPreprocess("field")
 */
class Field extends PreprocessBase implements PreprocessInterface

The code is much cleaner compared to the Version 7, In Symfony things are made much cleaner using Plugins. 

Note: To render the image, you have to have another fields on the service entity for field_image.

In the Default installation the ALT and Title attributes are not set for images, you need to enable them in the 'field_image' settings: 

/admin/structure/types/manage/service/fields/node.service.field_image

 

Short description of the image used by screen readers and displayed when the image is not loaded. Enabling this field is recommended.

 

Making this field required is recommended.

 

The title attribute is used as a tooltip when the mouse hovers over the image. Enabling this field is not recommended as it can cause problems with screen readers.

 

Then the image is printed as:

<img src="/sites/default/files/services/seo-search-engine-optimization.jpg" width="618" height="454"
alt="seo search engine optimization" title="Drupal Best CMS for SEO" typeof="foaf:Image" class="img-responsive">

I wrote a simple Image Preprocess Plugin to show you how to make sure that the Alt and Title are always set:

<?php

namespace Drupal\maria_consulting\Plugin\Preprocess;

use Drupal\bootstrap\Utility\Element;
use Drupal\bootstrap\Utility\Variables;
use Drupal\bootstrap\Plugin\Preprocess\PreprocessBase;
use Drupal\bootstrap\Plugin\Preprocess\PreprocessInterface;

/**
 * Pre-processes variables for the "image" theme hook.
 *
 * @ingroup plugins_preprocess
 *
 * @see image.html.twig
 *
 * @BootstrapPreprocess("image")
 */
class Image extends PreprocessBase implements PreprocessInterface {

  /**
   * {@inheritdoc}
   */
  public function preprocessVariables(Variables $variables) {
    $title = $variables->getAttribute('title');
    if (empty($title)) {
      if ($current_node = \Drupal::routeMatch()->getParameter('node')) {
        $content_type = $current_node->bundle();
        if ($content_type == "service") {
          $title = $current_node->label();
          $alt = $variables->getAttribute('alt');
          if (empty($alt)) {
            $variables->setAttribute('alt', $title);
          }
          $variables->setAttribute('title', $title);
        }
      }
    }
  }

}

As you can see, it checks if the Image has got the ALT and Title attributes, it they are empty they are set to value of the service Node Title. The title is always useful to have because it is like a tool tip on the image.

In order to include all the Service nodes in the Google site map you need to change this configuration inside /admin/structure/types/manage/service:

DEFAULT

 

 


The priority entities of this type will have in the eyes of search engine bots.


The frequency with which entities of this type change. Search engine bots may take this as an indication of how often to index them.


Determines if images referenced by entities of this type should be included in the sitemap.

Note: I also wanted to include the image as part of the site map so for example my entry for this page looks like:

<url>
  <loc>http://maria-consulting.co.uk/drupal-best-cms-seo</loc>
  <lastmod>2020-04-23T13:23:14+01:00</lastmod>
  <changefreq>monthly</changefreq>
  <priority>0.5</priority>
  <image:image>
   <image:loc>http://maria-consulting.co.uk/sites/default/files/services/seo-search-engine-optimization.jpg</image:loc>
   <image:title>Drupal Best CMS for SEO</image:title>
   <image:caption>seo search engine optimization</image:caption>
  </image:image>
 </url>
 <url>

 

1. Accessibility

cyber security and digital data protection concept

Drupal 8 custom drop down menus, blocks, breadcrumbs, url path aliases,&nbsp;light box, search offer lot of options on how to access and find your relevant content.

2. Example

reviewing financial reports in returning on investment analysis

Plan your strategy to use Drupal at the best of its capability and performance. As Drupal is written in PHP it requires that a module is..

3. Migrate

digital conceptual business technology background arrows chip

Migrate Drupal by follow our very simple Six Steps! Set up your YML configuration to define your custom migrate modules to import data from Drupal 7.

4. Responsive

Responsive Web Design

Building custom Drupal Bootstrap Responsive Sub Themes: we use open-source HTML / CSS framework to build responsive website.