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:
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>