Skip to main content

A potential default content solution for Drupal 8 core

Inspired by Jeff Eaton and Roy Scholten's session on install profiles from Drupalcon Prague, I recently set about building a potential Snowman install profile for a musician using Drupal 8.

During the process I came across a few bugs in core (patches filed of course) and then started working on the main missing piece of the puzzle - a default content solution - using the REST and Serialization API's already in core.

by lee.rowlands /


The first step in the process was getting a reasonable functioning site going with Drupal 8. As per the Snowman manifest, I was only allowed to use Core, no contributed modules. Much to my suprise I was able to get the site nearly fully functional, using only core. After the building was done, I started creating example content and then began looking at the best way to export it. I looked at how we did that in Drupal 7 and found that generally we encoded the data into a 'serialized' format. Now luckily the next session in my Drupalcon Prague playlist was Rest and Serialization in Drupal 8 by Lin Clark and Klaus Purer.

During that session I gleaned that the Serializer module in core is used by Rest module to encode a portable version of an entity. Sounds exactly like what we need for a default content solution.

First attempt

I started by enabling the Rest and Serialization modules and fetching some of the content in hal+json format and then saved them to disk and started stepping through the POST logic in the Rest module. This was relatively easy to get going, the Rest module is well architected and; as with most things in Drupal 8; being built on the plugin API made it immediately familiar.

Confident that I was onto something workable, I started work on creating a YAML serializer, as I felt the hal+json was too hard to hand-edit.

Here again, the architecture of the serialization module meant it was easy to register new serializers, and I was able to quickly knock-up a hybrid hal+yaml solution. Then I had something going in a basic format and thought it was time to discuss my approach with Lin and Klausi.

Community Feedback

So initial feedback from Lin and Klausi was that we had already rejected a Yaml serializer for Drupal core on security reasons (see the issue) so I decided to abandon that idea. Additionally they expressed trepidation about a hybrid hal+yaml format, as hal+json was an accepted format whilst the hal+yaml mutant hybrid was entering the unknown. So I reverted back to the original solution, hal+json and continued refactoring.

Dependency resolution

The next big piece of the puzzle was dependency resolution. If I had a default node file and that referenced a taxonomy term that was also default content, but not yet created, then the term reference field on the node would be empty. Luckily core already ships with Sam Boyers' Gliph library for resolving dependencies. With some help from Sam I was able to adapt my read logic to include a sort process so that entities were created in depdency order.

Putting it all together

Take the current code for a test spin from my github repo, note that without this core patch you can't import taxonomy terms, but thats down to a bug in the Hal EntityNormalizer.

Any module that requires default content can put hal+json versions of the entities in individual files inside {modulename}/content/{entity_type} folders. The module will detect any json files in folders matching content entity types at the time of install (just like default config) and import that content. 

For example see default_content_test in the repo which has the following structure

  • modules/default_content_test/content
  • modules/default_content_test/content/node
  • modules/default_content_test/content/node/imported.json
  • modules/default_content_test/content/taxonomy_term
  • modules/default_content_test/content/taxonomy_term/tag.json

This results in one node being imported (as specified in imported.json) and one term (tag.json).

At the moment these files need to be hand-created or exported using the Rest, Hal and Serialization modules. Note that the default functionality of the Hal module is to make all links point to the origin site's FDQN whilst the default_content module expects these (at this stage) to be relative to as there is no point in having default content that can only be re-imported on the originating site. So for example the 'type' link for a page node-type has href, whereas the default hal serialization would use

Next Steps

So the question from here is - where should this live - this is one of the final pieces of the Snowman puzzle and there is demand for this in core see these issues, however this is clearly a new feature, but maybe the new release cycle proposal means this can be considered for a future minor release.

If it lives in contrib, well that gives us time to iterate on a UI for exporting, although I'd argue that would never belong in core. 

So what do you think? Should I formulate this as a patch and add it to one of the core issues around default content? Or should it live out a cycle in contrib-land?


Posted by lee.rowlands
Senior Drupal Developer



Comment by JvE


I like it.
How would this work when updating to a newer version of a module with changes to the default content?

Comment by dawehner


Interesting idea.

You know, the rest of hook_menu() which are actual menu links would certainly profit from such a functionality.

Given that serializer will certainly be used for tools like deploy this might be an even better solution than the primary one in Drupal 7: migrate. It seems to be that maybe migrate in D8 could just leverage some pieces of the functionality of serializer.

Comment by lee.rowlands


The only issue with using it for default menu links, is that we need to have hal, rest and serialization modules enabled.

Comment by dgurba


Why is the sub-folder called "content" and not "default_content". It would seem keeping parity with the module name and the folder you interact with would help in the long run.

Comment by lee.rowlands


Core used config for config, this is content, seemed to fit

Comment by PWolanin


So, taking a quick look at the test example, I'm worried about the fact that tid 1, or nid 1 is specified in this format. That seems like a it must be generated on save, and so the uuid or other machine name has to be used to detect or find an this content if it's already been imported.

Comment by lee.rowlands


The nid is optional, I just included for ease of testing.
The hal/serialization modules already resolve via uuid, as seen in the taxonomy reference test.

Comment by tzm


This is working great, however, if I set my module which imports content (based off default_content_test) to install via profile.install it throws a Gliph\Exception\RuntimeException of:
No start vertex or vertices were provided, and no source vertices could be found in the provided graph.
Any guesses on why that may be? Thanks.

Comment by mikejw


...another question - do you have any solution for importing users? the default_content module doesn't set the user status and doesn't capture/set email or password.

Comment by lee.rowlands


No, but we could expand the normalizers to add one for the status and email, the password is another story...