Skip to main content
Version: v1

Generated Content

Generated Content fills a site with placeholder content from code, so a rebuilt environment comes back with pages, terms and files already in place. You write a plugin per bundle, and the module runs the plugins in order. The module runs the plugins when it's installed with GENERATED_CONTENT_CREATE=1, or whenever you ask it to. Plugins built on its static helpers produce identical output on every run, which visual regression tests and consistent demo sites need.

This page is for developers on a project built from Vortex who need demo or test content. It shows how to write a generator plugin, which helpers keep its output stable and how to run generation. The module documentation is the reference for its full API.

Plugin system​

Each generator is a PHP class in a module's src/Plugin/GeneratedContent/ directory, carrying the #[GeneratedContent] attribute and extending GeneratedContentPluginBase. Its generate() method creates the entities and returns them, so the module can track them for cleanup.

Create a plugin​

The following plugin, a shorter version of the shipped Page plugin, creates 20 page nodes with static titles and body text:

namespace Drupal\ys_demo\Plugin\GeneratedContent\Node;

use Drupal\generated_content\Attribute\GeneratedContent;
use Drupal\generated_content\Plugin\GeneratedContent\GeneratedContentPluginBase;

#[GeneratedContent(
id: 'ys_demo_node_page',
entity_type: 'node',
bundle: 'page',
weight: 20,
)]
class Page extends GeneratedContentPluginBase {

public function generate(): array {
$entities = [];
$storage = $this->entityTypeManager->getStorage('node');

for ($i = 1; $i <= 20; $i++) {
$node = $storage->create([
'type' => 'page',
'title' => sprintf('Demo page %s %s', $i, $this->helper::staticName()),
'status' => 1,
]);

$node->set('body', [
'value' => $this->helper::staticRichText(3),
'format' => 'full_html',
]);

$node->save();

$entities[] = $node;
}

return $entities;
}

}
note

For a bundle under content moderation, also set the moderation_state field to published before saving. A new node otherwise takes the workflow's default moderation state. With a draft default, the node stays unpublished even when its status is 1. The shipped Page plugin sets the field only when the node has a moderation_state field.

Attribute parameters​

The #[GeneratedContent] attribute takes the following parameters:

ParameterTypeRequiredDescription
idstringyesUnique plugin ID
entity_typestringyesTarget entity type, such as node or taxonomy_term
bundlestringyesTarget bundle
weightintnoExecution order, lowest first (default: 0)
trackingboolnoIf TRUE, tracks the created entities so the module can remove them later. If FALSE, doesn't track them (default: TRUE)
labelTranslatableMarkupnoHuman-readable name of the plugin
helperstringnoCustom helper class extending GeneratedContentHelper. If the class doesn't extend it, the plugin uses the default helper

Generation helpers​

$this->helper is a GeneratedContentHelper singleton, or the subclass named in the plugin's helper parameter. It produces field values, creates file assets and looks up entities that earlier plugins created. The following examples show some of its methods. The module documentation lists the full set.

Static helpers return the same values on every run, which visual regression testing needs. They read a fixed list of text in order, so the output stays stable as long as the plugins make the same calls in the same order:

$this->helper::staticName(); // Fixed-length name.
$this->helper::staticSentence(5); // Sentence of 5 words.
$this->helper::staticRichText(3); // 3 paragraphs of HTML.

Random helpers vary on every run, which suits content that only needs to make the site look populated:

$this->helper::randomName();
$this->helper::randomSentence(5, 10); // Between 5 and 10 words.
$this->helper::randomEmail();
$this->helper::randomTimestamp('-1year', 'now');

Entity helpers reference content that earlier plugins produced. Use weight to make sure those plugins ran first. Pick the static variant when the reference itself must stay stable between runs:

// In a node plugin with weight: 20 (runs after terms at weight: 10).
$node->set('field_tags', $this->helper::randomTerms('tags', 3));
$node->set('field_author', $this->helper::staticUser());

Asset helpers create managed files. By default, createFile() draws a random image for png, jpg and jpeg. For any other extension, it writes a placeholder text file under that extension and also raises a PHP notice, unless the extension is txt.

To get a real file, pass the static generation type as the third argument. The helper then copies 1 of the module's sample files for that extension. Repeated calls for the same extension cycle through its samples, so the same sequence of calls gives the same file contents on every run. The static type covers the following extensions:

  • doc and docx
  • gif, jpeg, jpg, png and svg
  • mp3 and mp4
  • pdf
  • xls and xlsx

For any other extension, such as csv, createFile() throws an exception before it creates a file, with the message Assets of type csv do not exist. The following calls create a random image and a static PDF:

$node->set('field_image', $this->helper::createFile('png'));
$node->set('field_attachment', $this->helper::createFile('pdf', [], GeneratedContentAssetGenerator::GENERATE_TYPE_STATIC));

The [] second argument holds options for the new file. Its filename option sets the name without the extension, and each file without it gets a random name. Its directory option defaults to public://generated_content.

GeneratedContentAssetGenerator lives in the Drupal\generated_content\Helpers namespace.

Trigger generation​

Generation starts in 1 of 4 ways, each covered in the following sections.

Drush command​

To run the page plugin, pass the entity type, the bundle and a count to the following command:

drush generated-content:create-content node page 20

The command needs all 3 arguments. It splits the count into batches of 50 and runs the bundle's plugin once per batch, so the plugin decides how many entities each run creates. With 20, the Page plugin runs once and creates its 20 pages. Confirm that the new pages appear in the content list at /admin/content.

The command runs only the plugin for that entity type and bundle, and none of the plugins that weight orders before it. A plugin that references other generated content therefore finds only what already exists. For example, if your project has a plugin for tags terms, create the terms before the pages that reference them:

drush generated-content:create-content taxonomy_term tags 10
drush generated-content:create-content node page 20

Admin UI​

To generate content from the administration interface, go to /admin/config/development/generated-content. The Generate content page needs the generate content permission. It lists every plugin with its weight, its tracking setting and the number of entities it created.

Select the plugins to process, or none to process them all, then click Generate, Delete or Regenerate. Delete removes only the entities the module tracks.

Environment variable​

With GENERATED_CONTENT_CREATE=1 set, installing generated_content or a module that provides plugins generates the content, except during a configuration import. Generation runs only while no generated content exists, so a module installed later adds no duplicates. GENERATED_CONTENT_ITEMS limits generation to a comma-separated list of entity_type-bundle pairs:

GENERATED_CONTENT_ITEMS="taxonomy_term-tags,node-page"

Provisioning​

Vortex installs the module from scripts/provision-10-enable-dev-modules.sh in the local, ci, dev and stage environments and sets GENERATED_CONTENT_CREATE=1 for that install, so the content arrives with the module. Setting DRUPAL_GENERATED_CONTENT_SKIP=1 installs the module without the content.

Set DRUPAL_GENERATED_CONTENT_SKIP and GENERATED_CONTENT_ITEMS in .env, in .env.local for local overrides or in the hosting provider's environment variables. Provisioning loads both files, so the script and the install it runs see their values.

Provisioning again doesn't duplicate the content. The script's drush pm:install command does nothing on a site that already has the module, so generation doesn't start.

➡️ See Generated Content in the provisioning documentation.

Example in Vortex​

The ys_demo module ships a single generated content plugin, Page. It creates 20 page nodes whose titles and body text come from the static helpers, so the same pages appear on every rebuild. The nodes back the demo /pages view used by the Testmode example.