4

I added a field of type computed field to existing nodes. It's required to re-save the nodes to set/update computed field values. As I do not want to update the changed timestamp I decided to not use the views bulk update to resave content. The easiest way would be a drush script, that way I can re-save content whenever it is necessary.

My first problem was that I was not able to leave the changed time unchanged on update ($node->save()). But I found that it could be re-set on a second node save.

My problem is that on each node save a new revision gets created. The node type is revision-able and beside the core module content moderation is enabled. I would appreciate any help on how to prevent the creation of revisions.

This is my current drush script in custom_helper.drush.inc:

<?php
/**
 * @file
 * Provides drush command to update all articles.
 */

use Drupal\node\Entity\Node;

/**
 * Implements hook_drush_command().
 */
function custom_helper_drush_command() {
  $items = array();
  $items['custom-resave-content'] = array(
    'description' => dt('Resaves all article entities.'),
    'options' => [
      'types' => dt('Coma separated list of content type to be resaved.'),
      'nids' => dt('Coma separated list if node ids.'),
      'field' => dt('Machine name of computed field to be updated.')
    ],
    'aliases' => array('custom-rc'),
  );
  return $items;
}

/**
 * Moves paragraphs into one paragraph reference field.
 */
function drush_custom_helper_custom_resave_content() {
  $nids = _convert_csv_to_array(drush_get_option('nids', []));
  $types = _convert_csv_to_array(drush_get_option('types', []));

  if (!$nids || $types) {
    // Get an array of node IDs.
    $query = \Drupal::entityQuery('node');
    if ($types) {
      $query->condition('type', $types, 'IN');
    }
    if ($nids) {
      $query->condition('nid', $nids, 'IN');
    }
    $nids = $query->execute();
  }

  // Load all the nodes.
  if ($nids) {
    $field_name = drush_get_option('field', '');

    $nodes = Node::loadMultiple($nids);
    foreach ($nodes as $node) {
      $entity_type = $node->getEntityType();
      $original_changed = $node->getChangedTime();
      if ($entity_type->isRevisionable()) {
        $node->setNewRevision(FALSE);
      }

      // Force update of computed field.
      if ($field_name) {
        /** @var \Drupal\Core\Field\FieldItemListInterface $items */
        $items = $node->get($field_name);
        if (empty($items[0])) {
          $items->appendItem();
        }
        $items->preSave();
      }

      // Save the updated computed field value.
      $node->setChangedTime($original_changed);
      if ($entity_type->isRevisionable()) {
        $node->setNewRevision(FALSE);
      }
      $node->save();

      // Reset changed time to original value.
      $node->setChangedTime($original_changed);
      if ($entity_type->isRevisionable()) {
        $node->setNewRevision(FALSE);
      }
      $node->save();

      $changed_after = $node->getChangedTime();
      drush_print('node:' . $node->id() . ':' . $original_changed . ':' . $changed_after);
    }
  }

  drush_log(dt('Resave finished.'), 'ok');
}
4
+50

Right now, this isn't possible with content moderation installed without doing backflips such as replacing the moderation handler for the node entity type.

This is however being worked on! Here are some issues which are relevant to the problem you are having:

The proposal is essentially two pronged:

  • Better describe to callers of SynchronizableInterface::setSyncing() when it's appropriate to mark an entity as "syncing".
  • Encourage implementors of entity lifecycle hooks to reduce disruptive side effects while entities are syncing.

Quoting myself from the issue:

IMO, the current 'syncing' status for content entities is shaping up into a flag to indicate that a save is happening under conditions which are outside of a typical user initiated content update and that as much as possible, side effects from that save should be limited. Examples might be:

  • Deploying a workspace.
  • Batch updating old revisions.
  • Running an update from a migration.

After defining some of those scenarios it becomes easier to rationalise eliminating some of the side effects that have been discussed (CM revision handling + changed timestamp).

In your case, you are attempting to resolve integrity in your model by bulk updating existing entities/revisions, it's outside a standard editorial based content change, so in my opinion it fits very clearly within the semantics that are being proposed for content entities and this interface. So with a few of these patches applied your code sample might look like:

  $entity->setSyncing(TRUE);
  $node->save();

And then the changed timestamp wouldn't be updated and content moderation wouldn't force the creation of a new revision.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.