Nicer date ranges in Drupal – part 3
This is the last part of a series on improving the way date ranges are presented in Drupal, by creating a field formatter that can omit the day, month or year where appropriate, displaying the date ranges in a nicer, more compact form:
- 24–25 January 2017
- 29 January–3 February 2017
- 9:00am–4:30pm, 1 April 2017
The first post, looked at porting some existing code from Drupal 7 to Drupal 8, adding an automated test along the way. In the second post, we made the format configurable.
There’s currently no administrative interface though, so site builders can’t add and edit formats from Drupal’s UI. We’ll add that in this last post.
Routing
According to the routing overview on drupal.org,
a route is a path which is defined for Drupal to return some sort of content on
.
For our administrative interface, we want to define a number of routes:
/admin/config/regional/date_range_format
- show a list of the formats, with links to:/admin/config/regional/date_range_format/add
/admin/config/regional/date_range_format/*/edit
/admin/config/regional/date_range_format/*/delete
There are two ways in which our module can provide routes.
We could include a routing.yml
file along with our module.
This file contains the same kind of information as would have been in
hook_menu
in Drupal 7. But it’s a static file—if we want something that’s
dynamic we can provide it at runtime using a route provider.
For dealing with entities, it’s often much easier to use Drupal’s bundled
AdminHtmlRouteProvider
class. This examines various properties on the entity
annotation—we’ll look at those next—and provides suitable routes for us
automatically.
To use this route provider, we add the following to the entity annotation:
@ConfigEntityType(
…
handlers = {
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
},
},
…
)
At this point we need to run the drupal router:rebuild
command from Drupal
console. We must do this whenever we change a routing.yml
file or any of
the properties in the entity that affect routes.
The collection view
An entity can define a collection view—typically a page showing a list of entities with links to edit them. Drupal provides a list builder which can be used to show a list of entities with buttons for common add/edit/delete type tasks. We’ll create one of these for our new configuration entity:
<?php
namespace Drupal\daterange_compact;
class DateRangeFormatListBuilder extends ConfigEntityListBuilder {
function buildHeader() {
/* return an array of column headings */
}
function buildRow(EntityInterface $entity) {
/* return an array of column values for the given entity */
}
}
We then associate this list builder with our entity by declaring it
within the @ConfigEntityType
annotation:
handlers = {
"list_builder" = "Drupal\daterange_compact\DateRangeFormatListBuilder",
}
The actual list builder is quite a rich, showing examples of different ranges. You can see the full implementation here.
The collection page
Once we have the list builder in place, we can add the collection link
to our @ConfigEntityType
annotation. The route provider will pick up on
this link template and provide a route for the entity collection page
automatically.
links = {
"collection" = "/admin/config/regional/date_range_format"
}
By defining the link, our page appears at the appropriate URL. Note that the add/edit/delete links won’t show just yet—we still have to define those.

Updating the main configuration page
In order to reach this new page, we’ll create a menu link on the main
configuration page, within the regional and language section.
We do that by supplying a daterange_compact.links.menu.yml
file:
entity.date_range_format.collection:
title: 'Date and time range formats'
route_name: entity.date_range_format.collection
description: 'Configure how date and time ranges are displayed.'
parent: system.admin_config_regional
weight: 0
That link gives us the starting point for our interface:

We can now view all the date and time range formats from the main administrative interface in Drupal. Next we’ll build some forms to maintan them, after which the add/edit/delete links should start to appear on our collection page.
Forms
We need a form to be able to edit date range formats. The same form is used
to create new ones. Drupal provides a lot of built-in functionality via the
EntityForm
class which we can extend. Drupal will then take care of loading
and saving the entity. We just need to provide the form elements to map
values on to our entity’s properties.
Adding & editing
We can add any number of forms, but we only need one to edit an existing
format, and we can reuse the same form for adding a new format. This form
is defined as a class, and lives in src/Form/DateRangeFormatForm.php
:
<?php
namespace Drupal\daterange_compact\Form;
class DateRangeFormatForm extends EntityForm {
/* implementation */
}
Configuration entities don’t use the field API, so we need to build the form ourselves. Although the form looks quite complicated and has a lot of options, it’s reasonably easy to build—each property in the configuration entity can be populated by a single element, like this:
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $this->entity->label(),
'#description' => $this->t("Name of the date time range format."),
'#required' => TRUE,
];
The full implementation of the form is here.
We also need to tell Drupal about this form, which we can do by adding the
following to the @ConfigEntityType
annotation:
"form" = {
"add" = "Drupal\daterange_compact\Form\DateRangeFormatForm",
"edit" = "Drupal\daterange_compact\Form\DateRangeFormatForm",
}
We also add some links, to match up operations such as add and edit
with the new form. These are also defined in the @ConfigEntityType
annotation:
links = {
"add-form" = "/admin/config/regional/date_range_format/add",
"edit-form" = "/admin/config/regional/date_range_format/{date_range_format}/edit",
}
If we look at the collection view again we see that alongside each format there
is a link to edit it. That is because of the edit-form
link declared in the
annotation.
We also want a link at the top of that page, to add a new format.
We can do that by providing an action link that refers to the add-form
link.
This belongs in the daterange_compact.links.action.yml
file:
entity.date_range_format.add_form:
route_name: 'entity.date_range_format.add_form'
title: 'Add format'
appears_on:
- entity.date_range_format.collection
At this point we have a means of adding and editing formats. Our form looks like this:

Deletion
Deleting entities is slightly different. We want to show a confirmation page
after a before performing the actual deletion. The EntityDeleteForm
class
does just that. All we need to do is subclass it and provide the wording
for the question:
<?php
namespace Drupal\daterange_compact\Form;
class DateRangeFormatDeleteForm extends EntityDeleteForm {
public function getQuestion() {
return $this->t('Are you sure?');
}
}
We declare this form and link on the @ConfigEntityType
annotation in the
same way as for add/edit:
"form" = {
"delete" = "Drupal\foo\Form\DateRangeFormatDeleteForm"
}
links = {
"delete-form" = "/admin/config/regional/date_range_format/{date_range_format}/delete",
}
Conclusion
That’s it. We’ve got a field formatter to render date and time ranges in a very flexible way. Users can define their own formats thorough the web interface, and these are represented as configuration entities, giving us all the benefits of the configuration management initiative, such as predictable deployments and multilingual support.
The module is available at https://www.drupal.org/project/daterange_compact.
I hope you found this write-up useful.
Want to help?
I’m currently working on getting this module up to scratch in order to have coverage from the Drupal security team. If you want to help make that happen, please review the code following this process and leave a comment on this issue. Thanks :-)