Drupal 9: How To Tell If A Page Is First Published

I was writing some code on a Drupal site that detects if a page is being published and I realised that this state isn't as clear cut as you might expect.  Drupal stores the published state of a page as the 'status', with 0 being unpublished and 1 being published. With revisions being turned on by default since Drupal 8 it is possible to see past states of the page when saving the page. The issue is that there is nothing in the current state of the page stating that this is the first time it is being published.

As an example of this in action, and where I cam across this, we can use the hook_ENTITY_TYPE_update() hooks to detect if a page is published as it gets updated like this.

<?php

use Drupal\Core\Entity\EntityInterface;

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function my_module_node_update(EntityInterface $entity) {
  if ($entity->isPublished()) {
    // The node is published, so perform an action.
  }
}

The problem here is that the next time that page is updated this logic would trigger as the current revision is published. This means that whatever action you perform in the above code will be repeated, even if a user is just correcting a spelling error on the page.

It is possible to solve this problem using data that you already have in the system. The node_field_revision table is used by Drupal to track the revisions of a page, with each revision having a status as to whether it is published or not. It is therefore possible to count the number of published status for a page to figure out if this is the first time something is published.

Here is the function that tests a node for the number of published statuses that exist in the revisions table. If the number of items found is 1 then this is the first time this page is being published.

/**
 * Test if a page is first published.
 *
 * @param \Drupal\node\NodeInterface $node
 *   The node to test with.
 *
 * @return bool
 *   Returns true if this is the first time a page is published.
 */
function is_first_published(NodeInterface $node) {
  $database = \Drupal::database();
  $query = $database->select('node_field_revision', 'nfr');
  $query->fields('nfr', ['status']);
  $query->condition('nid', $node->id(), '=');
  $query->condition('status', '1', '=');
  return $query->countQuery()->execute()->fetchField() == 1;
}

We can do this in the update hook as by the time the hook is triggered all of the database interactions have been completed.

As a side note, if you want to also detect if the page has been published when created then you'll need a hook_ENTITY_TYPE_insert() hook. Like this.

<?php

use Drupal\Core\Entity\EntityInterface;

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function my_module_node_insert(EntityInterface $entity) {
}

If using hooks like this as a solution doesn't suit you, then there are a couple of other options available.

The Publication Date module adds a "published on" field for each page that contains a date that it was first published. Using this module allows things like sorting by the publication date and has a number of integrations with other modules.

There is also a module called Published and corrected dates. This module adds couple of different date fields to pages, including the publication date, last corrected date and the number of corrections that the page has had since it was first published.

Although these modules are useful, they should really be installed before creating any content as they won't retrospectively contain publication dates for your pages. The Publication Date module does have an install hook that will update all existing pages with the publication date, although this is worked out based on the creation date and might not be entirely correct. I also had problems getting this to work so you might need to 

Finally, if you want to see this functionality in Drupal core then the good news is that there is an issue for this on the Drupal website. The bad news is that this issue was created in 2012 and has lots of comments but no resolution so using one of the above solutions for now might be your best bet.

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
5 + 0 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.