Any frontend guy will tell you that content is very important to be able to do their thing. You cannot properly structure content display and style it without having material to play with. And there is nothing more tedious than creating fake content on-the-fly to do the job. It will end up being destroyed, and you'd have to do it again because a new field was created. How about fixturising your content and keep on iterating it?
When time comes to start frontend implementation, you will take over some structure built by the backend guy. And if you usually work on large sites, then you certainly have to deal with a bigger team. Beyond the production of CSS and JS, you will need to have some dummy content to understand how it looks on the display. But, wouldn't it be nice if everyone in the team would be looking at the same site content? Imagine how easier would it be to reference a specific URL or part of a page when debugging. It would also allow the initial site review by the client or PM to happen much sooner, before the actual content is ready. How easier would it now be to test your app, either manually or through an automated setup! Are you nodding yet? Then let me show you how we do it in Marzee Labs as part of our Drupal development workflow.
The Migrate module
You are probably aware of the existence of the migrate module that is now part of Drupal 8 core. From the project page we can read "The migrate module provides a flexible framework for migrating content into Drupal from other sources".
Essentially, the migrate module is a swiss army knife that allows you to insert content into Drupal. By default it can import nodes, users, files, terms, and comments; other modules can easily extend it further. Check the official module documentation for further insight.
A practical tutorial: Sample content ready on install
We'll use a sample migrate module of our own: the MZ Migrate Example module (this module is part of our MZ installation profile).
This sample module feature defines two content types: Trappist (an exclusive Belgian Beer) and Brewery. Feel free to download and check the anatomy of this module. Don't worry if it seems complex, we will dissect it and unveil all its secrets. You can install it directly on a drupal test site but we will also talk about a more advanced way™ later on, so keep reading!
The feature module is responsible for migrating it's own sample content. It's a nice way of organizing your code, keeping both the content definition and the sample content together in a single feature.
We will be focusing on migrating sample content, so open up trappists.csv under the import folder of the same module. It contains the following lines:
id,title,brewery,image,type 1,"Westvleteren XII",1,westvleteren.jpg,Other 2,"Chimay",2,chimay.jpg,Dubbel|Tripel
Breaking it down you have:
- An ID of the content
- The title
- The ID of the related brewery, this is not the final node ID, but an internal ID we use to reference content via these CSV files
- The image file name, under import/files
- The taxonomy term, with support for multivalue, in this case separated by the | character.
This is a simple CSV file. In complex content types with several fields and multiple references it can get more confusing so you can use a spreadsheet tool for editing.
So, now that we have some sample content, we need to define how to import that content. In the file mz_migrate_example.migrate.inc you'll see we have declared a migration class for each content type: MZBrewery and MZTrappist.
trappist.inc defines the actual migration code. You'll see that we prepare the migration of several field types: text (either title or body), references to other migrate content (e.g. the Brewery the trappist beer is associated to via an entityreference field), some images, taxonomy terms and the author (with default uid = 1):
For example, here is a simple field mapping:
// Title $this->addFieldMapping('title', 'title');
and a more complex one:
// Image $this->addFieldMapping('field_trappist_image', 'image'); $this->addFieldMapping('field_trappist_image:file_replace') ->defaultValue(FILE_EXISTS_REPLACE); $this->addFieldMapping('field_trappist_image:source_dir') ->defaultValue(drupal_get_path('module', 'mz_migrate_example') . '/import/files');
That's it, you are now all set to import that content.
Import that content!
You can import the sample content through the migrate UI in the backend, at /admin/content/migrate or using drush.
If you are using the stand-alone example module you can use the following drush commands to test it:
drush en mz_migrate_example drush cc all drush mi MZBrewery,MZTrappist
You can find more information on drush migrate commands and a discussion on why you should use drush instead of the backend UI to use migrate here.
But that's just beginning...
Doing it all automatic now... aka the More Advanced Way™
As I mentioned above, there is a more advanced way™, and that is using Phing. What is Phing you ask? For the more distracted of you, we wrote a blog post on how to automate development using phing.
The integration is pretty simple: whenever the database is rebuilt there is some sample content ready to be used. The best part of this is when a fellow team mate goes in to review your feature, jumps into the branch you are working on and only has to do a phing build. Your team mate will then have the sample content ready to test your feature, because while building the feature you've been updating that magical CSV file for the migrate procedure, right? Of course you have!
On a more personal note, as a frontend guy, but also as a site builder, sometimes I had to add fields to features and I was a bit lost in the beginning looking at the migrate.inc files of the features. That's when I remembered that Drupal has great documentation. In this particular instance I needed to add sample content to a multivalue link field, I checked link.migrate.inc and saw in the comments how to import links automatically. Easy, isn't it?
How could I live without this?
Your frontend folks will love it and they will also start doing it themselves. Besides having the same code base, better yet is to always have the same sample content to play with as you build the website. And all of it done automatically, with phing to the rescue! Speed up the process, and manage short deadlines better by using content fixtures from day 1. Working as a team is great, and with this workflow it just got easier!
Have you got similar experiences, or totally different approaches on how you manage your test content? Drop us a line in the comments below, let's start a conversation.