In my previous blog post, I introduced a convenient composer script for effortless project reinstallation. However, a crucial element missing from this setup is the swift availability of content to test new features or assess the site structure.
If you're following along, you're likely already familiar with the command: composer project:reinstall
If not, check out this post for a quick guide: https://cyberschorsch.dev/drupal/effortless-drupal-reinstallations-quick-guide-using-composer
Importing content in Drupal can be achieved through various methods, but my preferred approach involves leveraging the Migrate module and its numerous extensions. This method provides flexibility, scalability, and easy extension through custom plugins.
The Goal:
I want a quick way to create a lot of blog posts without using a database as source with text and images. I should be able to easily modify and share this demo setup.
The Setup:
This blog post's setup mirrors the structure of my personal blog, featuring a content type "Blog post." This content type includes an introduction text, a field for the blog post, a main image, and a reference to a channel where the post will be listed and organized.
Several modules are employed in this demo setup:
Creating the Demo Module:
You can explore and download the module from here: https://gitlab.com/cyberschorsch-dev-public/cyb_demo
Please note: Note that the module follows my structure and requires modification for your specific purpose.
The main concept revolves around migration definitions for the desired content and providing JSON files containing the demo content.
Example structure of the module's directories:
Migration Definitions:
In this example I have 3 migrations: The demo_blog_posts migration requires the demo_channel and demo_media_images migration to run first since we use migrate_lookup to reference the created entities in the blog posts. The json files inside the artifacts folder contains the demo content where manually created the structure and used ChatGPT to create the actual content.
The content of the json files always follow a similar structure. Inside the json there is a data array which will act as the data source for the migration.
The source part of the blog posts migration
The data_parser_plugin json allows looping through the array, where each entry acts as a row for migration. With the help of the system_stream_wrapper module, the JSON file is packaged with the module.
source:
plugin: url
data_fetcher_plugin: file
data_parser_plugin: json
urls:
- 'module://cyb_demo/artifacts/blog_posts.json'
track_changes: true
item_selector: data
fields:
- name: name
label: 'The name of the blog post'
selector: name
- name: field_introduction_text
label: 'The field_introduction_text of the blog post'
selector: field_introduction_text
- name: field_blog_post_content
label: 'The field_blog_post_content of the blog post'
selector: field_blog_post_content
- name: field_channel
label: 'The field_channel of the blog post'
selector: field_channel
- name: field_main_image
label: 'The id of the media image'
selector: field_main_image
Looking Up the Channel by Name and Image by ID:
Examples of using migration_lookup for referencing created entities in blog posts.
Excerpt from demo_blog_posts.yml:
field_channel:
- plugin: skip_on_empty
source: field_channel
method: process
- plugin: sub_process
source: field_channel
process:
target_id:
- plugin: migration_lookup
migration:
- demo_channel
source: name
'field_main_image/target_id':
- plugin: skip_on_empty
source: field_main_image
method: process
- plugin: migration_lookup
migration:
- demo_media_images
source:
- field_main_image
Excerpt of the blog_posts.json file:
{
"data": [
{
"name": "The Adventures of Lorem Ipsum",
"field_introduction_text": "Join Lorem Ipsum on a whimsical journey through the world of placeholder text.",
"field_blog_post_content": "<p>\n This is a hilarious tale of <strong>Lorem Ipsum</strong>, the unsung hero of the text world, and his escapades in the land of placeholder content. Strap in for a <u>rollercoaster</u> of laughs!</p><p>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.</p>",
"field_channel": [
{
"name": "Demo Channel A"
}
],
"field_main_image": "1"
},
{
"name": "The Secret Life of Placeholder Text",
"field_introduction_text": "Unveiling the hidden secrets of Lorem Ipsum and its clandestine activities.",
"field_blog_post_content": "<p>\n Did you know that <strong>Lorem Ipsum</strong> has a secret life? Dive into the mysterious world of placeholder text and discover the surprising adventures that unfold behind the scenes!</p>",
"field_channel": [
{
"name": "Demo Channel A"
}
],
"field_main_image": "2"
},
Using Migrate to Import External Images:
With the Migrate File module, importing placeholder images is streamlined. The image_import plugin handles downloading the image file into the defined destination folder.
field_media_image:
plugin: image_import
source: image_url
destination: 'constants/file_destination'
title: title
alt: alt_text
Excerpt of the media_images.json:
{
"data": [
{
"id": 1,
"file_url": "https://picsum.photos/1920/1080.jpg",
"alt_text": "Lorem Ipsum 1",
"title": "Lorem Ipsum Image 1"
},
{
"id": 2,
"file_url": "https://picsum.photos/1920/1081.jpg",
"alt_text": "Lorem Ipsum 2",
"title": "Lorem Ipsum Image 2"
},
Importing the content
With the module in place I can simply execute the following command to have my content imported:
drush mim --group=demo
[notice] Processed 2 items (2 created, 0 updated, 0 failed, 0 ignored) - done with 'demo_channel'
[notice] Processed 20 items (20 created, 0 updated, 0 failed, 0 ignored) - done with 'demo_media_images'
[notice] Processed 20 items (20 created, 0 updated, 0 failed, 0 ignored) - done with 'demo_blog_posts'
This command imports all migrations in the "demo" group, resolving dependencies to ensure that dependent migrations run before demo_blog_posts.
My newly created blog posts all have different content, are assigned to a channel and also have an image imported.
And with this one-liner I can reinstall and create demo content very quickly:
ddev composer project:reinstall && ddev drush mim --group=demo
Conclusion
Leveraging Migrate for importing demo content proves to be quick, easy, and extendable. Adding new fields later poses no problem, and the knowledge gained about Migrate benefits developers for future tasks involving project migrations to Drupal.
I'd love to hear your thoughts and learn about alternative ways you import demo content!
Comments