Commit e67b313d authored by catch's avatar catch
Browse files

Issue #2068333 by roderik, skipyT, pcambra, plach, Désiré, marcingy, peximo:...

Issue #2068333 by roderik, skipyT, pcambra, plach, Désiré, marcingy, peximo: Convert node SQL queries to the Entity Query API.
parent a78a1063
Loading
Loading
Loading
Loading
+1 −0
Changes for core/modules/node/lib/Drupal/node/Entity/Node.php: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
 *   label = @Translation("Content"),
 *   bundle_label = @Translation("Content type"),
 *   controllers = {
 *     "storage" = "Drupal\node\NodeStorage",
 *     "view_builder" = "Drupal\node\NodeViewBuilder",
 *     "access" = "Drupal\node\NodeAccessController",
 *     "form" = {
+62 −0
Changes for core/modules/node/lib/Drupal/node/NodeStorage.php: 62 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Definition of Drupal\node\NodeStorageController.
 */

namespace Drupal\node;

use Drupal\Core\Database\Database;
use Drupal\Core\Entity\ContentEntityDatabaseStorage;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Language\Language;

/**
 * Defines the controller class for nodes.
 *
 * This extends the base storage class, adding required special handling for
 * node entities.
 */
class NodeStorage extends ContentEntityDatabaseStorage implements NodeStorageInterface {

  /**
   * {@inheritdoc}
   */
  public function revisionIds(NodeInterface $node) {
    return $this->database->query(
      'SELECT vid FROM {node_revision} WHERE nid=:nid ORDER BY vid',
      array(':nid' => $node->id())
    )->fetchCol();
  }

  /**
   * {@inheritdoc}
   */
  public function userRevisionIds(AccountInterface $account) {
    return $this->database->query(
      'SELECT vid FROM {node_field_revision} WHERE uid = :uid ORDER BY vid',
      array(':uid' => $account->id())
    )->fetchCol();
  }

  /**
   * {@inheritdoc}
   */
  public function updateType($old_type, $new_type) {
    return $this->database->update('node')
      ->fields(array('type' => $new_type))
      ->condition('type', $old_type)
      ->execute();
  }

  /**
   * {@inheritdoc}
   */
  public function clearRevisionsLanguage($language) {
    return $this->database->update('node_revision')
      ->fields(array('langcode' => Language::LANGCODE_NOT_SPECIFIED))
      ->condition('langcode', $language->id)
      ->execute();
  }
}
+60 −0
Changes for core/modules/node/lib/Drupal/node/NodeStorageInterface.php: 60 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Contains \Drupal\node\NodeStorageControllerInterface.
 */

namespace Drupal\node;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Defines a common interface for node entity controller classes.
 */
interface NodeStorageInterface extends EntityStorageInterface {

  /**
   * Returns a list of node revision IDs for a specific node.
   *
   * @param \Drupal\node\NodeInterface
   *   The node entity.
   *
   * @return int[]
   *   Node revision IDs (in ascending order).
   */
  public function revisionIds(NodeInterface $node);

  /**
   * Returns a list of revision IDs having a given user as node author.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user entity.
   *
   * @return int[]
   *   Node revision IDs (in ascending order).
   */
  public function userRevisionIds(AccountInterface $account);

  /**
   * Updates all nodes of one type to be of another type.
   *
   * @param string $old_type
   *   The current node type of the nodes.
   * @param string $new_type
   *   The new node type of the nodes.
   *
   * @return int
   *   The number of nodes whose node type field was modified.
   */
  public function updateType($old_type, $new_type);

  /**
   * Unsets the language for all nodes with the given language.
   *
   * @param $language
   *  The language object.
   */
  public function clearRevisionsLanguage($language);
}
+15 −5
Changes for core/modules/node/node.admin.inc: 15 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -26,14 +26,17 @@
 * @param bool $load
 *   (optional) TRUE if $nodes contains an array of node IDs to be loaded, FALSE
 *   if it contains fully loaded nodes. Defaults to FALSE.
 * @param bool $revisions
 *   (optional) TRUE if $nodes contains an array of revision IDs instead of
 *   node IDs. Defaults to FALSE; will be ignored if $load is FALSE.
 */
function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load = FALSE) {
function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load = FALSE, $revisions = FALSE) {
  // We use batch processing to prevent timeout when updating a large number
  // of nodes.
  if (count($nodes) > 10) {
    $batch = array(
      'operations' => array(
        array('_node_mass_update_batch_process', array($nodes, $updates, $langcode, $load))
        array('_node_mass_update_batch_process', array($nodes, $updates, $langcode, $load, $revisions))
      ),
      'finished' => '_node_mass_update_batch_finished',
      'title' => t('Processing'),
@@ -48,10 +51,13 @@ function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load
    batch_set($batch);
  }
  else {
    if ($load) {
    if ($load && !$revisions) {
      $nodes = entity_load_multiple('node', $nodes);
    }
    foreach ($nodes as $node) {
      if ($load && $revisions) {
        $node = entity_revision_load('node', $node);
      }
      _node_mass_update_helper($node, $updates, $langcode);
    }
    drupal_set_message(t('The update has been performed.'));
@@ -97,10 +103,13 @@ function _node_mass_update_helper(NodeInterface $node, array $updates, $langcode
 * @param bool $load
 *   TRUE if $nodes contains an array of node IDs to be loaded, FALSE if it
 *   contains fully loaded nodes.
 * @param bool $revisions
 *   (optional) TRUE if $nodes contains an array of revision IDs instead of
 *   node IDs. Defaults to FALSE; will be ignored if $load is FALSE.
 * @param array $context
 *   An array of contextual key/values.
 */
function _node_mass_update_batch_process(array $nodes, array $updates, $load, array &$context) {
function _node_mass_update_batch_process(array $nodes, array $updates, $load, $revisions, array &$context) {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = count($nodes);
@@ -113,7 +122,8 @@ function _node_mass_update_batch_process(array $nodes, array $updates, $load, ar
    // For each nid, load the node, reset the values, and save it.
    $node = array_shift($context['sandbox']['nodes']);
    if ($load) {
      $node = entity_load('node', $node);
      $node = $revisions ?
        entity_revision_load('node', $node) : entity_load('node', $node);
    }
    $node = _node_mass_update_helper($node, $updates);

+40 −85
Changes for core/modules/node/node.module: 40 added lines, 85 removed lines.
Original line number Diff line number Diff line
@@ -494,10 +494,7 @@ function node_entity_extra_field_info() {
 *   The number of nodes whose node type field was modified.
 */
function node_type_update_nodes($old_id, $new_id) {
  return db_update('node')
    ->fields(array('type' => $new_id))
    ->condition('type', $old_id)
    ->execute();
  return \Drupal::entityManager()->getStorage('node')->updateType($old_id, $new_id);
}

/**
@@ -801,31 +798,18 @@ function node_user_cancel($edit, $account, $method) {
  switch ($method) {
    case 'user_cancel_block_unpublish':
      // Unpublish nodes (current revisions).
      module_load_include('inc', 'node', 'node.admin');
      $nodes = db_select('node_field_data', 'n')
        ->distinct()
        ->fields('n', array('nid'))
      $nids = \Drupal::entityQuery('node')
        ->condition('uid', $account->id())
        ->execute()
        ->fetchCol();
      node_mass_update($nodes, array('status' => 0), NULL, TRUE);
        ->execute();
      module_load_include('inc', 'node', 'node.admin');
      node_mass_update($nids, array('status' => 0), NULL, TRUE);
      break;

    case 'user_cancel_reassign':
      // Anonymize nodes (current revisions).
      // Anonymize all of the nodes for this old account.
      module_load_include('inc', 'node', 'node.admin');
      $nodes = db_select('node_field_data', 'n')
        ->distinct()
        ->fields('n', array('nid'))
        ->condition('uid', $account->id())
        ->execute()
        ->fetchCol();
      node_mass_update($nodes, array('uid' => 0), NULL, TRUE);
      // Anonymize old revisions.
      db_update('node_field_revision')
        ->fields(array('uid' => 0))
        ->condition('uid', $account->id())
        ->execute();
      $vids = \Drupal::entityManager()->getStorage('node')->userRevisionIds($account);
      node_mass_update($vids, array('uid' => 0), NULL, TRUE, TRUE);
      break;
  }
}
@@ -836,15 +820,13 @@ function node_user_cancel($edit, $account, $method) {
function node_user_predelete($account) {
  // Delete nodes (current revisions).
  // @todo Introduce node_mass_delete() or make node_mass_update() more flexible.
  $nodes = db_select('node_field_data', 'n')
    ->distinct()
    ->fields('n', array('nid'))
  $nids = \Drupal::entityQuery('node')
    ->condition('uid', $account->id())
    ->execute()
    ->fetchCol();
  entity_delete_multiple('node', $nodes);
    ->execute();
  entity_delete_multiple('node', $nids);
  // Delete old revisions.
  $revisions = db_query('SELECT DISTINCT vid FROM {node_field_revision} WHERE uid = :uid', array(':uid' => $account->id()))->fetchCol();
  $storage_controller = \Drupal::entityManager()->getStorage('node');
  $revisions = $storage_controller->userRevisionIds($account);
  foreach ($revisions as $revision) {
    node_revision_delete($revision);
  }
@@ -910,34 +892,13 @@ function node_page_title(NodeInterface $node) {
 *
 * @return string
 *   A unix timestamp indicating the last time the node was changed.
 */
function node_last_changed($nid, $langcode = NULL) {
  if (isset($langcode)) {
    $result = db_query('SELECT changed FROM {node_field_data} WHERE nid = :nid AND langcode = :langcode', array(':nid' => $nid, ':langcode' => $langcode))->fetch();
  }
  else {
    $result = db_query('SELECT changed FROM {node_field_data} WHERE nid = :nid AND default_langcode = :default_langcode', array(':nid' => $nid, ':default_langcode' => 1))->fetch();
  }
  return is_object($result) ? $result->changed : FALSE;
}

/**
 * Returns a list of all the existing revision numbers for the node passed in.
 *
 * @param \Drupal\node\NodeInterface $node
 *   The node entity.
 *
 * @return
 *   An associative array keyed by node revision number.
 * @todo Remove once https://drupal.org/node/2002180 is resolved. It's only used
 *       for validation, which will be done by EntityChangedConstraintValidator.
 */
function node_revision_list(NodeInterface $node) {
  $revisions = array();
  $result = db_query('SELECT nr.vid, nfr.title, nr.log, nr.revision_uid AS uid, n.vid AS current_vid, nr.revision_timestamp, u.name FROM {node_field_revision} nfr JOIN {node_revision} nr ON nr.vid = nfr.vid LEFT JOIN {node} n ON n.vid = nfr.vid INNER JOIN {users} u ON u.uid = nr.revision_uid WHERE nfr.nid = :nid AND nfr.default_langcode = 1 ORDER BY nfr.vid DESC', array(':nid' => $node->id()));
  foreach ($result as $revision) {
    $revisions[$revision->vid] = $revision;
  }

  return $revisions;
function node_last_changed($nid, $langcode = NULL) {
  $changed = \Drupal::entityManager()->getStorage('node')->loadUnchanged($nid)->getChangedTime();
  return $changed ? $changed : FALSE;
}

/**
@@ -951,31 +912,31 @@ function node_revision_list(NodeInterface $node) {
 *   visible to the current user.
 */
function node_get_recent($number = 10) {
  $query = db_select('node_field_data', 'n');
  $account = \Drupal::currentUser();
  $query = \Drupal::entityQuery('node');

  if (!user_access('bypass node access')) {
  if (!$account->hasPermission('bypass node access')) {
    // If the user is able to view their own unpublished nodes, allow them
    // to see these in addition to published nodes. Check that they actually
    // have some unpublished nodes to view before adding the condition.
    if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT DISTINCT nid FROM {node_field_data} WHERE uid = :uid AND status = :status', array(':uid' => \Drupal::currentUser()->id(), ':status' => NODE_NOT_PUBLISHED))->fetchCol()) {
      $query->condition(db_or()
        ->condition('n.status', NODE_PUBLISHED)
        ->condition('n.nid', $own_unpublished, 'IN')
      );
    $access_query =  \Drupal::entityQuery('node')
      ->condition('uid', $account->id())
      ->condition('status', NODE_NOT_PUBLISHED);
    if ($account->hasPermission('view own unpublished content') && ($own_unpublished = $access_query->execute())) {
      $query->orConditionGroup()
        ->condition('status', NODE_PUBLISHED)
        ->condition('nid', $own_unpublished, 'IN');
    }
    else {
      // If not, restrict the query to published nodes.
      $query->condition('n.status', NODE_PUBLISHED);
      $query->condition('status', NODE_PUBLISHED);
    }
   }
  $nids = $query
    ->distinct()
    ->fields('n', array('nid'))
    ->orderBy('n.changed', 'DESC')
    ->sort('changed', 'DESC')
    ->range(0, $number)
    ->addTag('node_access')
    ->execute()
    ->fetchCol();
    ->execute();

  $nodes = node_load_multiple($nids);

@@ -1078,16 +1039,13 @@ function node_feed($nids = FALSE, $channel = array()) {
  $rss_config = \Drupal::config('system.rss');

  if ($nids === FALSE) {
    $nids = db_select('node_field_data', 'n')
      ->distinct()
      ->fields('n', array('nid'))
      ->condition('n.promote', 1)
      ->condition('n.status', 1)
      ->orderBy('n.created', 'DESC')
    $nids = \Drupal::entityQuery('node')
      ->condition('status', 1)
      ->condition('promote', 1)
      ->sort('created', 'DESC')
      ->range(0, $rss_config->get('items.limit'))
      ->addTag('node_access')
      ->execute()
      ->fetchCol();
      ->execute();
  }

  $item_length = $rss_config->get('items.view_mode');
@@ -1272,9 +1230,9 @@ function node_form_system_themes_admin_form_submit($form, &$form_state) {
 * the process above is followed except that hook_node_access() is not called on
 * each node for performance reasons and for proper functioning of the pager
 * system. When adding a node listing to your module, be sure to use a dynamic
 * query created by db_select() and add a tag of "node_access". This will allow
 * modules dealing with node access to ensure only nodes to which the user has
 * access are retrieved, through the use of hook_query_TAG_alter().
 * entity query and add a tag of "node_access". This will allow modules dealing
 * with node access to ensure only nodes to which the user has access are
 * retrieved, through the use of hook_query_TAG_alter().
 *
 * Note: Even a single module returning NODE_ACCESS_DENY from hook_node_access()
 * will block access to the node. Therefore, implementers should take care to
@@ -1749,10 +1707,7 @@ function node_file_download_access($field, EntityInterface $entity, File $file)
 */
function node_language_entity_delete(LanguageEntity $language) {
  // On nodes with this language, unset the language.
  db_update('node_revision')
    ->fields(array('langcode' => ''))
    ->condition('langcode', $language->id())
    ->execute();
  \Drupal::entityManager()->getStorage('node')->clearRevisionsLanguage($language);
}

/**
Loading