Drupal Recipes logo

Default Content in Drupal

In Drupal 10.3, the DefaultContent API was added to Drupal core as part of the experimental Recipes APIs. These APIs allow Drupal to create content from files that are part of a recipe.

This content that we programmatically create isn’t intended for deploying or migrating content, we have the Workspaces and other modules for that.

Instead, it can be a great tool in our toolkit for creating demos and test content, while freeing developers and testers from the drudgery of repetitive content creation.

How did we get here?

Starting back in 2011, a group of open-source Drupal developers started working on a new install profile for Drupal called Snowman. Instead of having a generic and unopinionated starting point, the idea behind Snowman was that it wanted to provide a ready-to-use Drupal site tailored for a specific use case.

One such use case was for an install profile for a band site, during which it looked like Lee Rowlands (larowlan) came up with the idea which eventually became the Default Content module.

The Default Content contrib module

While we added a way in core to consume content config files from recipes, we are still at the start of what core can do, and mid-journey of what the contrib module aims to do.

Until the recent interest from the Recipes Initiative team, the Default Content module has always been used for creating default content configuration files for custom modules and install profiles. You could create content on a local site, export it to your module or a module in an install profile, and when that module was enabled (or a site was started from the install profile), and you installed the default content module as a dependency, you would get your content imported. This all still works exactly as it always has, and now it can also be used with recipes. 

Drupal Recipes are a framework and set of tools for automating the configuration of a site. They allow developers to rapidly configure entities, fields, and other settings. A recipe can be applied to any number of different sites. This means creating multiple sites can be achieved faster and with a greater degree of consistency in how they are configured. A great deal of human error and forgetfulness can be avoided by using Recipes, particularly for repetitive config. (Check out my comprehensive guide to Drupal recipes.)

Using the Default Content module

The export process is the same for modules and recipes. Let’s review that now.

Getting set up

Install the module as you normally would.  Since I am using the module for creating content for recipes, I install it in the dev section of my composer requirements as I do not need it in production.

composer require drupal/default_content –dev

And enable the module:

drush en -y default_content

The module doesn’t have a user interface. Instead, it provides drush commands that can be run to export content.

What can we export?

Out of the box, we can export the following entity types:

  • Content type nodes: node <node id>
  • Taxonomy Terms: taxonomy_term <taxonomy term id>
  • Files: file <file id>
  • Media: media <media id>
  • Menu Links: menu_link_content <menu link id>
  • Content blocks: block_content <block id>
  • Shortcuts: shortcut <shortcut id>

At this time, there isn’t a way to export by bundle type, think all nodes of type Article, or all menu links from the main menu, but there is a patch for it.

Let’s take a look at the three commands that the module provides to export content.

Default content drush commands

default-content-export-references

Exports an entity and all its referenced entities. This is my go to command as it helps you export referenced media, files, taxonomy terms, and users from the parent node.

Alias:

  • dcer

Arguments:

  • entity_type – The entity type to export.
  • entity_id – The ID of the entity to export

One of these arguments is required.

Options:

  • folder – The module or recipe’s content folder to export to. Entities are automatically grouped by entity type into directories.

Examples:

# Exports node 123 and all its dependencies.
# To a module:
drush dcer node 123 module_name 

# To a recipe:
drush dcer node 123 --folder=recipes/recipe_name/content

# Export all nodes and all their dependencies.
drush dcer node --folder=modules/custom/my_custom_module/content

default-content-export

Exports a single entity. If you want to manage your own dependencies.

Alias:

  • dce

Arguments:

  • entity_type – The entity type to export.
  • entity_id – The ID of the entity to export

Both of these arguments are required.

Options:

  • file – Write out the exported content to a file instead of stdout

Examples:

# Exports node 123 only. You are responsible to export dependencies.
drush dce node 123 module_name

default-content-export-module

Exports all the content defined in a module info file. This is most helpful for distribution and install profile maintainers where they would want to maintain and update default content from a canonical source.

Alias:

  • dcem

Arguments:

  • module – The machine name of the module

The argument is required.

Options:

  • None

Examples:

# Exports all the content defined in a module info file.
drush dcem module_name

# The module's info file would need to have a section like this:
default_content:
  node:
	- c9a89616-7057-4971-8337-555e425ed782
	- b6d6d9fd-4f28-4918-b100-ffcfb15c9374
  file:
	- 59674274-f1f5-4d6a-be00-fecedfde6534
	- 0fab901d-36ba-4bfd-9b00-d6617ffc2f1f
  media:
	- ee63912a-6276-4081-93af-63ca66285594
	- bcb3c719-e266-45c1-8b90-8f630f86dcc7
  menu_link_content:
	- 9fbb684c-156d-49d6-b24b-755501b434e6
	- 19f38567-4051-4682-bf00-a4f19de48a01
  block_content:
	- af171e09-fcb2-4d93-a94d-77dc61aab213
	- a608987c-1b74-442b-b900-a54f40cda661

Creating a Drupal recipe with Default content

So let’s create a recipe that can store default content we can send to a test server so our QA engineer can test. We’ve created a node that has our new feature on our local environment. Rather than having to rebuild that page manually, we will export the content, and apply the recipe after it has been deployed.

Create the recipe structure

We keep our recipes in the recommended folder above the webroot.

You can manually create a recipe folder, the content folder, and a recipe.yml file or use the command line:

mkdir recipes/recipe-name
mkdir recipes/recipe-name/content
touch recipes/recipe-name/recipe.yml

In our recipe.yml file, we only need a name, description, and type.

name: 'Recipe name'
description: 'Demo content for ticket xyz'
type: 'Content'

Export your content

Using the drush commands above, usually the dcer command to export the content you need. Verify in your /content folder that you now have the entities you expect. It should look something like this:

We can now uninstall the Default Content module from our local environment.

Normally we just use recipes in our local development environments and deploy the resulting config, but when leveraging the Default Content module in your recipes, you will deploy them to your server. 

Once deployed and applied on your site, you’ll probably want to remove your recipe in a subsequent pull request as you don’t need it in production.

Commit and deploy your new recipe along with your code changes as you normally would.

Applying the recipe

Once your deployment has finished, you can use Drupal core’s recipe script to apply the recipe. If you have cli access to your server, from the webroot you can run:

php core/scripts/drupal recipe ../recipes/recipe-name -v

This uses the recipe PHP script from Drupal to apply the recipe from its current location. The -v flag prints verbose information during the application process.

If you are using Drush 13 or above, they added a recipe command which is a wrapper around the core script.

drush recipe ../recipes/recipe-name -v

If you are on Pantheon, you can user Terminus to run either command:

# PHP script
terminus drush site.env -- ev "passthru('php core/scripts/drupal recipe ../recipes/recipe-name -v');"

# Drush 13+
terminus drush site.env -- recipe ../recipes/recipe-name -v

Verify the content has been created as expected and have your PMs and clients rejoice in the fact that you saved them some time having to recreate it!

What Default content isn’t made for

Default content is not meant to be a replacement for content deployment solutions. It can be a good starting point on new sites. It’s great for demo sites, and quality assurance and user acceptance testing, but there are better solutions like the Workspaces module in Drupal core that allow for you to stage content and deploy it later. 

One of the biggest reasons is that we don’t know what the IDs will be on the destination site. You may have started out with the same database, but there is a good chance that the IDs have been increased on the production site by content editors adding new articles or pages.

You can also apply a recipe multiple times, or install and uninstall a module and add that default content again. There are issues in the module’s queue to address some of these situations, but work still needs to be done to improve the processes.

What’s next?

There are plenty of issues in the module’s issue queue to improve the quality and functionality of Default content, and the eleven year old Support default content entities core issue that has recently won some success thanks to the Recipes initiative.

I’d love to hear your thoughts and feedback on how you are using Default Content, or how you plan on using it! Please reach out to me in the Drupal Slack or on LinkedIn.