Create better hero banners using entities in Drupal 8

Hero banners are a super common design pattern on the web today. They usually include an image, a big title, and maybe some description. Often, sites I build will also have slight variations across different pages and sections. It took me a while, but I came up with an architecture I’m happy with.

Sitting down to build this pattern, I first started with a Field Collection, but quickly realized that was a bit too limiting. Enter custom entities. Using ECK we are able to create a banner entity and a hero bundle. This creates the ability to abstract the fields out and re-use banners between other entities. As with other entities, it creates ample ability to leverage Drupal data API’s and preprocess functions to adjust each hero to your exact specifications.

Requirements

  1. Drupal 8
  2. ECK version 8.x
  3. Inline Entity Form version 8.x

I’m going to assume you have a working local copy of Drupal 8. Or at least an environment to develop on. Download ECK and IEF, installing them to /modules. Enable these modules on the Extend page (/admin/modules). Both have just a single module.

Creating the hero banner

Once they’re enabled, we’ll start with creating the Banner entities. Head to Structure > ECK Entity Types (/admin/structure/eck/entity_type). Here we’ll start with adding an entity type of “Banner”. I always add all the base fields. Now we’ll create a “Hero” bundle. Once the bundle is created we can add fields to it. Since it has a title, we can add the hero image and hero body fields. You can add any field you’d like here. Once you’ve created the hero fields it’s time to add the hero reference to our pages.

Often I’ll have a content type called “Landing page” which will be a larger, more designed page. These are where I’ll add the hero reference field. You’re free to create a new Content Type, but for this example, we can just add the hero reference to our basic pages.

We want to add a new field to our basic pages, so head to Structure > Content types. Click “Manage fields” for Basic pages. Add a new field, choose “Reference” and “Other…”. I’ll usually name it “Hero reference” so the machine name is field_hero_reference. Now on the field settings at the bottom should be a new section for our Banner entity. Select the “Banner” item to reference, allowing only 1. Select the one Hero bundle we have, leaving the rest as default.

Now that we have the reference field set up, let’s use Inline Entity Form to provide a smoother admin experience. To use the IEF field formatter, navigate back to the Basic page content type edit page. Click the “manage form display” tab. Now we’re looking for the new Hero Reference field. Change the widget to “Inline entity form - Complex”. In the settings you can override the labels (hero and heroes), and decide if they can choose from already created heroes.

Display the hero banner

Now that we have the field added, we’ll want to adjust how it’s display. If you’re using Display Suite or Panels, you’ll be able to integrate this field like you would others. I don’t always use these tools, and when I don’t I like to pull the hero banner into my page template, instead of at the node level. This gives me a bit more freedom on placement and interaction with page level elements. We don’t need a view, it’s actually pretty simple to do just that!

To be able to render our hero banner at the page level, we want to create a variable that we’ll output in the page.html.twig template. Whichever theme you’re using, you’ll want to open the ‘THEME_NAME.theme’ file. We’ll want to add code to the HOOK_preprocess_page() function, so if theres not a ‘THEME_NAME_preprocess_page()’ add one in. It would look like this.

1
2
3
4
5
6
/**
 * Implements HOOK_preprocess_page().
 */
function THEME_NAME_preprocess_page(&$vars) {
  // Your code here
}

Now that we can hook into our page variables we need to do a few things:

  1. Test if we’re on a node
  2. Test if our node has our hero field
  3. Test if that field has data.

If all 3 things pass, we want to display our hero entity in our page template. There may be a cleaner way to do this, but this is how I’ve done it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function THEME_NAME_preprocess_page(&$vars) {
  // init our hero variable
  $vars['hero'] = NULL;

  if (!empty($vars['node'])) {
    $node = $vars['node'];

    if ($node->hasField('field_hero_reference')) {
      $hero = $node->get('field_hero_reference');

      if ($hero->count() > 0) {
        // finally if all our tests pass view our entity
        $vars['hero'] = entity_view($hero->entity, 'default');
      }
    }
  }
}

Now that we have our variable built, it needs to get added to the page template. If there’s no page template in the theme you’re working with, you need to copy one from core to your theme. I would suggest using Classy’s template. Copy the file /core/themes/classy/templates/layout/page.html.twig to /themes/YOUR_THEME/templates/layout/page.html.twig. You don’t need to follow the exact folder structure, but I recommend doing so for clarity. With a page template in place to override, we can add the following code to print our hero banner, if it’s there.

1
2
3
4
5
{{ if page.hero }}
  <div class="site-hero">
    {{ page.hero }}
  </div>
{{ endif }}

Place this wherever the design calls for. Now, to adjust the field output of the banner, you’ll want to go to the entity’s manage display page (/admin/structure/eck/entity/banner/types/manage/hero/display if you’re following this to the T). You can remove the labels, re-arrange the fields, and adjust the field formats. Integrating the Responsive Image module in core is as straight forward as setting up the breakpoints and using the field formatter. Now, any Hero on the site, regardless of it’s parent entity, will behave the same.

Creating a template for the hero banner

Now that it’s displayed on the page, and can be created from the node inline, the last thing that will probably be needed is a template for the entity. ECK provides a small template file that can be moved to the custom theme being used. Copy the file /modules/eck/templates/eck-entity.html.twig to /themes/YOUR_THEME/templates/entity/eck-entity.html.twig. I like to add the extra folder for organization. If you have twig debug enabled on the theme, you’ll be able to see inline what template name to use for the hero banners. You can either use eck-entity--banner.html.twig or eck-entity--banner--hero.html.twig. Some sites will have different types of banners, you’ll be able to create a template per-bundle if that’s what is appropriate.

With your new template file for your entity you can go nuts theming to your desire. Pull out fields, add div wrappers were necessary. You’re also able to preprocess your ECK entities like normal.

1
2
3
4
5
6
/**
 * Implements hook_preprocess_HOOK().
 */
function gotham_preprocess_eck_entity(&$vars) {
  // Do sweet stuff
}

Conclusion

This was just a glimpse into the power and flexibility of abstracting design patterns into entities. I do this with any piece of data that doesn’t have a forward-facing page, but has logic and complex structure to it. It allows me to create DRY code as well as site architecture. What do you think? What have you used custom entities for?