While building an installation profile or a distribution using Drupal it's likely to set up the contents every time there is a new build. This becomes a chore when the number of available functionalities or listing increases. It becomes difficult to test the different listings without a proper number of contents.

Default content provides a way to include default contents for the required entities in JSON format and added as a dependency to the profile setup. This will import the minimum required set of contents available for testing and demo upon installation.

Using the Default content module

The Default Content module doesn't do anything on its own. It needs a custom module to be in the module which has the entity JSON exports to import. Now, when I say entities any type of entities can be imported; files included. However, for files, we need to do some manual work. The project page however mentions that it supports Files import using the File entity module, but I couldn't make it work but used a workaround.

Pre-Requisites & Setup

The post assumes you have the Default Content modulus & Drush CLI is installed. 

Create a new module demo_default_content in your profile and add the default_content module as a dependency. HAL & Serialization can also be added as a dependency but since it's added a dependency in default_content so it can be skipped.


name: 'Demo default content'
type: module
description: 'Default content export'
core_version_requirement: ^8 || ^9
package: "SDNN"
dependencies:
  - default_content:default_content

Exporting contents

The exported JSON files for the entities will be placed in a directory called content inside the custom module. To export content, there 2 Drush commands available.

  1. Export a single entity drush dce <entity_type> <entity_id>
  2. Export all referenced entities for an entity drush dcer <entity_type> which is preferable since all the references are handles automatically. However, there are a few things to take care of while using it [mentioned below]

Provided, there is an article or number articles with the fields like term reference, media & author; running the following Drush command will export all the articles node and the referencing entities for it.


drush dcer node --folder=path/to/demo_default_content/content

This will create a folder structure like the following


content/
├── file
│   └── 136733d4-def1-4d2d-ba68-9cf20a2c5094.json
├── media
│   └── 116c9694-0f3a-4bcc-82f1-9302b766afd0.json
├── node
│   └── c48fe37c-8660-4838-9984-017f06565e51.json
├── taxonomy_term
│   ├── ea674d7c-e086-40e5-9956-41c0cf6746b2.json
│   └── ff343e13-0136-4ca7-aa7a-6b6596ebf844.json
└── user
    ├── cc247ecf-6435-4a23-9552-1b4d49489efe.json
    └── cc247ecf-6435-4a23-9552-1b4d49489efe.json

Fixing the exported contents

  1. Remove any JSON file which exports the user with ID 1 if the module is being installed separately and not added as a dependency to the profile. Search for an entry "uid": [ { "value": 1 } ] in the JSON exports under the user directory. If the file contains the user ID 1 then remove the file.

  2. Now, files JSON export will have the references to the file path in the public:// directory. But in a fresh install, since the files will not be present the images will appear to be broken.

To handle the broken image, create a folder called "assets" (name is arbitrary). Place the upload files in the "assets" folder like images in Media and user Picture. It's recommended to place the files in separate folders for each entity type.

Now, since the image files are not present in the public:// directory, copy the files from the modules assets directory to the public:// directory when the module will be installed using hook_install()


$fs = \Drupal::service('file_system');

// Copy the media assets.
$media_path = 'public://media/';
$fs->prepareDirectory($media_path, FileSystemInterface::CREATE_DIRECTORY);
$media = $fs->scanDirectory(drupal_get_path('module', 'demo_default_content') . '/assets/media', '/\.(jpg|png|jpeg)$/');
foreach ($media as $source) {
  $fs->copy($source->uri, $media_path . $source->filename);
}

// Copy the user pictures.
$picture_path = 'public://picture/';
$fs->prepareDirectory($picture_path, FileSystemInterface::CREATE_DIRECTORY);
$picture = $fs->scanDirectory(drupal_get_path('module', 'demo_default_content') . '/assets/picture', '/\.(jpg|png|jpeg)$/');
foreach ($picture as $source) {
  $fs->copy($source->uri, $picture_path . $source->filename);
}

This will ensure that the files are present when the default entities are exported.

Conclusion

So, the Default content is a great tool to quickly setup a default set of contents while working on an installation profile. This is really helpful during doing a manual QA, running Behat test or when we need to demo the functionalities of the profile. Although, there are other ways to create default contents using which Umami demo profile is using, but the Default content module make exporting and importing contents pretty easy to use.