Development

Getting the correct node revision in Drupal 8

Mike Potter, Software Architect
#Drupal 8 | Posted

It all started as a simple question to our Drupal developers:

"If you have a Published node, then create a new Draft, what version do you get from Node::load?"

There were two answers:

  1. "Node::load will always give you the Published revision of the node."
  2. "Node::load will give you the latest revision of the node."

Take a moment to think about this and select an answer before you continue reading.

Plain Drupal 8 without any Content Moderation

When you install a fresh Drupal 8 site, content moderation is disabled. In this situation, life is very simple:

2. Node::load will give you the latest revision of the node

regardless of whether it is published or not. Going to the node/view page will show the latest revision (and will have a pink background in the standard Bartik theme if it isn't published).  The node/edit page will show the latest revision.

Basically, in this scenario, it doesn't matter whether the node is published or not. The latest revision is always the "default" revision.

Adding Content Moderation

Content Moderation was added to Drupal core in version 8.5.0. When you enable the content_moderation, a basic "Draft, Published, Archived" workflow is created for you.

Using the default workflow, enable it for one of your content types (such as Articles).  Then create an Article and set the workflow state to Published. Then edit the same article and set the workflow state back to Draft.  In this case, the answer is:

1. Node::load will give you the Published revision of the node (not the latest draft).

Now Publish the article again, and then edit the article and set the workflow state to Archived.  Now:

2. Node::load will give you the latest revision of the node (the Archived version, which is not published).

So how does this all work? Why does it seem, at first glance, to be inconsistent?

Understanding the Default workflow state

If you look at the default workflow configuration and edit a State, you will see a checkbox called "Default revision".  In the Drupal node_revision database table, there is a column for each revision of a node called revision_default.

When you save a new revision, Drupal will set the database revision_defaultcolumn based on the "Default revision" checkbox for the state that you selected.

In the case of the default workflow installed by content_moderation, the Published state has "Default revision" checked (because all Published states must also be set to default). But the Archived state also has "Default revision" checked! Only the Draft state has the "Default revision" unchecked.

The Node::loadfunction, and the node/view page will always show the latest "default" revision of a node.  The node/edit page will always show the latest revision (highest revision id) regardless of the "default" value.  When the highest revision id does not have the revision_default value set to True, Drupal will add a "Latest  Version" tab next to View and Edit that allows you to view the latest revision rather than the current default/published version.

This behavior holds true even at the database layer. The node and node_field_data tables always contain the data for the default revision, not necessarily the published revision.

In code, you can get the latest (highest) revision id for content entities via:

  1. $vid = \Drupal::entityTypeManager()
  2.   ->getStorage('node')
  3.   ->getLatestRevisionId($nid);

and then load that revision via:

  1. $node = \Drupal::entityTypeManager()
  2.   ->getStorage('node')
  3.   ->loadRevision($vid);

Custom Workflow States

When creating your own Workflow and States, be sure to understand how this "Default Revision" option works in Drupal 8. The "Published" checkbox for the State is controlling the status property of the node, and the "Default Revision" checkbox is controlling the revision_default property. Any number of states can be set to be Published or Default.

If you are creating an API or other backend code that needs to load either the published revision of a node, or the latest revision (to be edited), be sure you are not just calling Node::load without understanding the default revision. When an API makes changes to content, it should always be loading the latest revision, not just using Node::load.

For example, if you add a state called "Revised" to indicate a node that was previously published and now has a forward "draft" revision, you don't want this marked as a "Default Revision" otherwise users might see unpublished content (or get an "access denied" error if they don't have permission for unpublished content).

Summary

In the future it might be nice to have a direct getStorage()->loadLatestRevision() API in Drupal code to make this a bit easier. The storage query has a latestRevision tag method that can be added (for example, creating a loadProperties helper that returns the latest revision of the matching query). But for now, the "default" terminology remains somewhat confusing. As more sites start using the core Content Moderation function I expect to see this continue to improve in core.

Mike Potter

Mike Potter

Software Architect