• Published:
  • Under: Development

As a developer who mostly focuses on Drupal, I have been asked a few times to build lists of periodical content—news, events and others—sorted by date. When all content types use the same field type to store dates, it is easy to sort content using the Views module.

However, Drupal 8 created a distinction between content with a single date, which can be stored in the Date field type, and content with a start and end date, which now requires the Date Range field type. Views can sort by either one of these field types, and even by one and then the other, but not on both at the same time as though they were the same field.

Following are two methods that build on the functionality of the Views module to allow sorting across Date and Date Range simultaneously. In the first method, I show how to add a common date field to all content types that need to be sorted. This method is best implemented as part of a site migration, because all affected content must be re-saved to populate new Date values. The second method leverages the Search API module’s indexing functionality to build a common field to sort on. I recommend using this method only in cases where Search API is already in use, since adding the module solely for date sorting would cause an unnecessary increase in loading time.

A content-centric approach: Adding a common field

In this example, our site has several types of periodical content: Blogs, News, Press Releases, Announcements and Events. Nearly all of these content types use the same field—Date—to allow an editor to specify their date and time of publication. The exception is the Event content type, which uses a Date Range field instead. However, the client has expressed a wish to be able to include Events in a master list of all published periodical content.

The simplest way to address this is to add the common Date field to the Event content type, in addition to its own Date Range field:

What you need: 

  • Drupal version: Drupal 8
  • Modules needed: Views

In the Event content type, add both fields to the Event content type

Image1

However, we don’t want editors to be able to set a different date for the common field. To prevent this, we disable access to the Date field on the Event content type’s Manage Form Display tab:

Manage Form Display Tab

Now the editor can’t access this field at all. Finally, we implement a hook to insert the Date Range field’s start value into the disabled field when the content is saved. To do this, we will use hook_entity_presave(), which triggers when the save button is clicked, but before the changes are written. Create a simple Drupal Module (if you don’t already have one) and add the following to its .module file:


<?php

/**
 * @file
 * Presave directives for Event nodes.
 */

use Drupal\Core\Entity\EntityInterface;

/**
 * Implements hook_entity_presave().
 */
function mymodule_entity_presave(EntityInterface $entity) {

  // Make sure it’s the right content type.
  if ($entity->getEntityType()->id() == 'node' && $entity->getType() == 'event') {

    // If the event date contains any data
    $event_date_empty = $entity->get('field_event_date_time')->isEmpty();

    // If the event date field has data.
    if ($event_date_empty == FALSE) {

      $event_date = $entity->get('field_event_date_time')->getValue()[0]['value'];
      // Remove everything after T for Date range field so the value can be comparable.
      // For example: 
      // Date field:       2018-07-26
      // Date range field: 2018-07-26T00:00:00 - 2018-07-26T23:59:59
      $new_date = explode('T', $event_date);

     // Save the value to Date field.
      $entity->set('field_date', $new_date);
    }
  }
}

Rebuild your Drupal caches to register your hook. When you save any Event content, the start value of the Date Range field will be inserted into the common Date field. This will allow your Event content to be sorted using the same Date field used by all of your other periodical content.

A shortfall of the content-centric solution is that event content must be re-saved for the date values to populate. If content has already been migrated, the approach will not work unless a re-migration is performed or a hook is written to programmatically update every existing event node. If migration data is not available, or if a lot of new content has been created since the migration, these solutions can be time-consuming and prone to errors.

An index-centric approach: combining fields using Search API

When re-saving content is not a good solution, there is an alternative. In this case, our site uses a custom search index provided by the Search API module. While this module’s intended purpose is beyond the scope of our view, we’re already using it to aggregate and provision field content for use in our searches. We can use this aggregation capability to combine the values of our date fields, and then use the combined values as our sort criteria.

To clarify, I only suggest this approach in cases where Search API is already in use.

The new aggregated field can be built as a Concatenation Field to return a unified string of numbers. Once this is complete, we can build our View based on the custom search index, rather than on the site content itself.

What you need: 

To make a new field, navigate to the database content index within Search API:

  • Go to Administration > Configuration > Search and metadata > Search API > Database content index
  • Or /admin/config/search/search-api/index/database_content_index/fields
database API

Click “Add fields” and select all the necessary date and date range fields for Concatenation

Published Date Field Image

After it’s saved, the new field will be available for use.

Database content index

The search index will need to be re-indexed before the new field will be available for sorting:

  • Go to Administration > Configuration > Search and metadata > Search API
  • Or /admin/config/search/search-api/index/database_content_index

Be sure to clear all indexed data first so the Index Now button will be enabled afterwards. In general, depending on the content volume and environment, indexing can take a few minutes to an hour.  

Index Status Image

After the re-indexing is done, go to Views and create a new view using Index Database content index. We can modify the sorting once it’s saved.

Add View Drupal

Add the necessary fields, including the date fields from different content types. We will select our new aggregated date field as our Sort Criteria. This option will function regardless of which plugin is used to display the View content.

Title Window Drupal

In this case, I checked the option for “Hide if empty” under No Results Behavior in each date field setting so the view won’t generate any empty HTML markup.

As you can see from the preview below, the aggregated field becomes a string of numbers and can be sorted in either direction. The field can be hidden from view as the user will only need to see the correctly formatted date (see Publish Date, Event Date).

Drupal Image 7

In conclusion, the solutions above work well in different situations. Both approaches provide the flexibility one should expect when periodical content is pulled into View. More importantly, they can be cloned and exported easily for other Drupal 8 sites.

Thank you to CHIEF’s senior developers, James Thompson and Daniel Gading, for contributing to these solutions. Lastly, I want to thank our senior copywriter Lea Sidoti for proofreading this article.