You might remember the Date module from Drupal 7. Along with the Date Repeat API, it allowed users to generate fields that stored more complex rules for dates than a single date and time. You were able to store a series of dates that are repeated weekly or even yearly on specific dates and even exclude a few.

But none of this is available anymore since the Date module was made part of the core. Now, with the help of the Recurring Events module, this functionality is available, as it was in Drupal 7. As its name says, it allows you to create a recurring events series with complex date rules. What’s different from Drupal 7’s Date module is that, instead of being a field that can be added to any content type, Recurring Events is a custom entity type.

This brings a new question to the table: how do you migrate content with different types of date rules to Drupal’s latest version and keep all information intact? Let's dive into how this works with the Recurring Events module.

Recurring Event Migration

Before we start, a quick note: this article is written for those who have some basic knowledge about how migrations work in Drupal. If that’s not your case, you'll want to learn more about the subject first. Here’s a list of some resources that will help you:

For this example, let's assume we have two websites, one built in Drupal 7 and a brand new one built in Drupal 9. You need to migrate the content with dates that were created with complex rules. Like with any other Drupal migration, you need to create a custom module for each content entity you want to migrate. In this case, we're naming it d7_event_migration.

In the d7_event_migration.info.yml file, you’ll need to add the dependencies of the modules we're going to need, which are:

  • Migrate

  • Migrate Drupal

  • Recurring Events

This is how our d7_event_migration.info.yml file looks:

name: D7 Events migration
type: module
description: Helper module for content migration from old D7 site.
package: Other
core_version_requirement: ^9

dependencies:
 - drupal:migrate_drupal
 - drupal:recurring_events

Now, let's create the migration file. We'll create a folder inside our module named migrations and then we'll add to it a file named events.yml. The file looks like this:

id: events
label: 'D7 events'

source:
 plugin: d7_node
 node_type: event
process:
 title: title
 status: status
 created: created
 changed: changed
 uid:
plugin: default_value
default_value: 1
 langcode:
plugin: default_value
default_value: "und"
 body: body
destination:
 plugin: 'entity:eventseries'
 default_bundle: events
 source_date_field: field_event_date
 source_timezone: 'America/Los_Angeles'

The first few lines are standard for a Drupal 7 migration. The source plugin is d7_node since we're migrating content from a Drupal 7 content type which is named event. In the process section, we're adding some default fields like title and body that can be found in any other migration.

Here we're also showing the use of the default_value process plugin that allows you to set a specific scalar value for a field in a migration. In this case, we're using it for the uid field to set it by default to the user 1 which is the admin, and also for the langcode to set it by default as und which means undefined.

The Destination section is where we're using the plugin provided by the Recurring events module. The plugin is named entity:eventseries and it's used to indicate that the migration is going to create an entity of type Event series which is one of the two types of entities that are added with the Recurring events module.

For this migration, the target entity is a custom event that we created called events, indicated in the default_bundle config key. The next two config keys are where the magic happens: source_date_field and source_timezone. Both of these config keys are used by the entity:eventseries plugin to store the correct date rules that were available in the Drupal 7 date fields.

In the source_date_field we specify the date field from the content type in the Drupal 7 site, which is named in this case field_event_date. For the source_timezone config key, we need to specify a valid php timezone. In this example we're using America/Los_Angeles.

Now, let's test our migration. First we need to enable the module that we just created. It can be done with drush or via Drupal UI. Next, to run the migrations, we're going to use drush. We'll check the migration status first with the following command.

drush migrate-status

The output that we get from the console is this:

------------------------------------------------------ -------- ------- ---------- ------------- ---------------

 Migration ID                                       Status   Total   Imported   Unprocessed   Last Imported

 ------------------------------------------------------ -------- ------- ---------- ------------- ---------------

 events                                             Idle 11270      1127

 ------------------------------------------------------ -------- ------- ---------- ------------- ---------------

Next, we're going to run the migration we created with the following command.

drush migrate-import events

After the migration process finishes, we run a command again to check the migration status (drush migrate-status) and we get the following output:

------------------------------------------------------ -------- ------- -------------- ------------- ---------------------

 Migration ID                                       Status   Total   Imported   Unprocessed   Last Imported

 ------------------------------------------------------ -------- ------- -------------- ------------- ---------------------

 events                                             Idle 11271126 (99.9%)   0         2022-06-02 03:43:26

 ------------------------------------------------------ -------- ------- -------------- ------------- ---------------------

As you may notice, the migration was not completely successful: there was one single content item that was not migrated. However, this is not an error with the migration itself. In our example, there was content in the original Drupal 7 site which didn't have a value set in the field_event_date. When that scenario happens, the import process for that content fails with an exception Drupal\Component\Plugin\Exception\PluginNotFoundException: The "" plugin does not exist. and is not migrated.

A workaround for this issue can be created by extending the EntityEventSeries plugin from the Recurring events module, but we’ll leave this solution for another article.

When to Use Recurring Events?

You can use this method in many different cases when the original website has any recurring events with complex date rules. Use cases include:

  • Monthly meetings with stakeholders

  • A training course that is held every quarter

  • Lists of birthdays and anniversaries

  • Cultural events with specific dates and times, such as exhibitions, plays or movie showings

This approach will help you migrate additional date information in your future Drupal projects with the highest possible efficiency, practically eliminating the risk of losing relevant data.