From 36675eaafc9a85ad3a9324fca004437f01ea65a8 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 12 Aug 2014 17:40:09 -0700
Subject: [PATCH] Issue #2280861 by roderik, pcambra: CommentStatistics service
 followup.

---
 core/modules/comment/comment.install          |  2 +-
 .../modules/comment/src/CommentStatistics.php |  5 ++-
 .../src/CommentStatisticsInterface.php        |  5 ++-
 core/modules/comment/src/CommentStorage.php   | 20 +---------
 .../comment/src/CommentStorageInterface.php   | 18 ---------
 core/modules/comment/src/Entity/Comment.php   | 10 ++---
 .../tests/src/Entity/CommentLockTest.php      |  4 ++
 core/modules/tracker/tracker.pages.inc        | 38 +++++++++----------
 8 files changed, 35 insertions(+), 67 deletions(-)

diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 4ce2b37cffe6..64aa6bd78f23 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -27,7 +27,7 @@ function comment_uninstall() {
  */
 function comment_install() {
   // By default, maintain entity statistics for comments.
-  // @see \Drupal\comment\CommentStorage::updateEntityStatistics().
+  // @see \Drupal\comment\CommentStatisticsInterface
   \Drupal::state()->set('comment.maintain_entity_statistics', TRUE);
 }
 
diff --git a/core/modules/comment/src/CommentStatistics.php b/core/modules/comment/src/CommentStatistics.php
index 164e8aa0bef2..0f2e93603dec 100644
--- a/core/modules/comment/src/CommentStatistics.php
+++ b/core/modules/comment/src/CommentStatistics.php
@@ -68,8 +68,9 @@ public function __construct(Connection $database, AccountInterface $current_user
   /**
    * {@inheritdoc}
    */
-  public function read($entities, $entity_type) {
-    $stats = $this->database->select('comment_entity_statistics', 'ces')
+  public function read($entities, $entity_type, $accurate = TRUE) {
+    $options = $accurate ? array() : array('target' => 'replica');
+    $stats =  $this->database->select('comment_entity_statistics', 'ces', $options)
       ->fields('ces')
       ->condition('ces.entity_id', array_keys($entities))
       ->condition('ces.entity_type', $entity_type)
diff --git a/core/modules/comment/src/CommentStatisticsInterface.php b/core/modules/comment/src/CommentStatisticsInterface.php
index 92a0b7af45ea..5e54a169b04b 100644
--- a/core/modules/comment/src/CommentStatisticsInterface.php
+++ b/core/modules/comment/src/CommentStatisticsInterface.php
@@ -32,11 +32,14 @@ public function getRankingInfo();
    *   Array of entities on which commenting is enabled, keyed by id
    * @param string $entity_type
    *   The entity type of the passed entities.
+   * @param boolean $accurate
+   *   (optional) Indicates if results must be completely up to date. If set to
+   *   FALSE, a replica database will used if available. Defaults to TRUE.
    *
    * @return object[]
    *   Array of statistics records.
    */
-  public function read($entities, $entity_type);
+  public function read($entities, $entity_type, $accurate = TRUE);
 
   /**
    * Delete comment statistics records for an entity.
diff --git a/core/modules/comment/src/CommentStorage.php b/core/modules/comment/src/CommentStorage.php
index 2d5541e54cd0..9639d597b731 100644
--- a/core/modules/comment/src/CommentStorage.php
+++ b/core/modules/comment/src/CommentStorage.php
@@ -25,13 +25,6 @@
  */
 class CommentStorage extends ContentEntityDatabaseStorage implements CommentStorageInterface {
 
-  /**
-   * The comment statistics service.
-   *
-   * @var \Drupal\comment\CommentStatisticsInterface
-   */
-  protected $statistics;
-
   /**
    * The current user.
    *
@@ -50,14 +43,11 @@ class CommentStorage extends ContentEntityDatabaseStorage implements CommentStor
    *   The entity manager.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
    *   Cache backend instance to use.
-   * @param \Drupal\comment\CommentStatisticsInterface $comment_statistics
-   *   The comment statistics service.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
    */
-  public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, CommentStatisticsInterface $comment_statistics, AccountInterface $current_user, CacheBackendInterface $cache) {
+  public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, AccountInterface $current_user, CacheBackendInterface $cache) {
     parent::__construct($entity_info, $database, $entity_manager, $cache);
-    $this->statistics = $comment_statistics;
     $this->currentUser = $current_user;
   }
 
@@ -69,19 +59,11 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_info,
       $container->get('database'),
       $container->get('entity.manager'),
-      $container->get('comment.statistics'),
       $container->get('current_user'),
       $container->get('cache.entity')
     );
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function updateEntityStatistics(CommentInterface $comment) {
-    $this->statistics->update($comment);
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/comment/src/CommentStorageInterface.php b/core/modules/comment/src/CommentStorageInterface.php
index 9087e5c72ba7..9eb4a668454d 100644
--- a/core/modules/comment/src/CommentStorageInterface.php
+++ b/core/modules/comment/src/CommentStorageInterface.php
@@ -107,24 +107,6 @@ public function getChildCids(array $comments);
    */
   public function loadThread(EntityInterface $entity, $field_name, $mode, $comments_per_page = 0, $pager_id = 0);
 
-  /**
-   * Updates the comment statistics for a given node.
-   *
-   * The {comment_entity_statistics} table has the following fields:
-   * - last_comment_timestamp: The timestamp of the last comment for the entity,
-   *   or the entity created timestamp if no comments exist for the entity.
-   * - last_comment_name: The name of the anonymous poster for the last comment.
-   * - last_comment_uid: The user ID of the poster for the last comment for
-   *   this entity, or the entity author's user ID if no comments exist for the
-   *   entity.
-   * - comment_count: The total number of approved/published comments on this
-   *   entity.
-   *
-   * @param \Drupal\comment\CommentInterface $comment
-   *   The comment being saved.
-   */
-  public function updateEntityStatistics(CommentInterface $comment);
-
   /**
    * Returns the number of unapproved comments.
    *
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 1bb7ef0f2547..bb6c872291f8 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -76,9 +76,9 @@ public function preSave(EntityStorageInterface $storage) {
       $thread = $this->getThread();
       if (empty($thread)) {
         if ($this->threadLock) {
-          // As preSave() is protected, this can only happen when this class
-          // is extended in a faulty manner.
-          throw new \LogicException('preSave is called again without calling postSave() or releaseThreadLock()');
+          // Thread lock was not released after being set previously.
+          // This suggests there's a bug in code using this class.
+          throw new \LogicException('preSave() is called again without calling postSave() or releaseThreadLock()');
         }
         if (!$this->hasParentComment()) {
           // This is a comment with no parent comment (depth 0): we start
@@ -146,7 +146,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
 
     $this->releaseThreadLock();
     // Update the {comment_entity_statistics} table prior to executing the hook.
-    $storage->updateEntityStatistics($this);
+    \Drupal::service('comment.statistics')->update($this);
   }
 
   /**
@@ -169,7 +169,7 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
     entity_delete_multiple('comment', $child_cids);
 
     foreach ($entities as $id => $entity) {
-      $storage->updateEntityStatistics($entity);
+      \Drupal::service('comment.statistics')->update($entity);
     }
   }
 
diff --git a/core/modules/comment/tests/src/Entity/CommentLockTest.php b/core/modules/comment/tests/src/Entity/CommentLockTest.php
index a9a04f4ec8c8..af6a7f7ce806 100644
--- a/core/modules/comment/tests/src/Entity/CommentLockTest.php
+++ b/core/modules/comment/tests/src/Entity/CommentLockTest.php
@@ -27,6 +27,7 @@ public function testLocks() {
     $container->set('module_handler', $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'));
     $container->set('current_user', $this->getMock('Drupal\Core\Session\AccountInterface'));
     $container->set('cache.test', $this->getMock('Drupal\Core\Cache\CacheBackendInterface'));
+    $container->set('comment.statistics', $this->getMock('Drupal\comment\CommentStatisticsInterface'));
     $request_stack = new RequestStack();
     $request_stack->push(Request::create('/'));
     $container->set('request_stack', $request_stack);
@@ -85,7 +86,10 @@ public function testLocks() {
       ->method('getListCacheTags')
       ->will($this->returnValue(array('comments' => TRUE)));
     $storage = $this->getMock('Drupal\comment\CommentStorageInterface');
+
+    // preSave() should acquire the lock. (This is what's really being tested.)
     $comment->preSave($storage);
+    // Release the acquired lock before exiting the test.
     $comment->postSave($storage);
   }
 
diff --git a/core/modules/tracker/tracker.pages.inc b/core/modules/tracker/tracker.pages.inc
index 0aac664d66a1..a88b75451654 100644
--- a/core/modules/tracker/tracker.pages.inc
+++ b/core/modules/tracker/tracker.pages.inc
@@ -45,28 +45,24 @@ function tracker_page($account = NULL) {
 
   $rows = array();
   if (!empty($tracker_data)) {
-    $nids = array_keys($tracker_data);
-    $nodes = node_load_multiple($nids);
-    // @todo This should be actually filtering on the desired language and just
-    //   fall back to the default language.
-    $result = db_query("
-    SELECT
-      n.nid,
-      SUM(l.comment_count) AS comment_count
-    FROM {node_field_data} n
-      INNER JOIN {comment_entity_statistics} l
-        ON n.nid = l.entity_id AND l.entity_type = 'node'
-      INNER JOIN {users} u
-        ON n.uid = u.uid
-    WHERE n.nid IN (:nids)
-      AND n.default_langcode = 1
-    GROUP BY n.nid
-    ORDER BY n.changed DESC", array(
-      ':nids' => array_keys($nodes)
-    ), array('target' => 'replica'))->fetchAllKeyed();
-    foreach ($result as $nid => $comment_count) {
+    // Load nodes into an array with the same order as $tracker_data.
+    $nodes = entity_load_multiple('node', array_keys($tracker_data));
+
+    // Enrich the node data.
+    $result = \Drupal::service('comment.statistics')->read($nodes, 'node', FALSE);
+    foreach ($result as $statistics) {
+      // The node ID may not be unique; there can be multiple comment fields.
+      // Make comment_count the total of all comments.
+      $nid = $statistics->entity_id;
+      if (empty($nodes[$nid]->comment_count)
+          || !is_numeric($nodes[$nid]->comment_count)) {
+        $nodes[$nid]->comment_count = $statistics->comment_count;
+      }
+      else {
+        $nodes[$nid]->comment_count += $statistics->comment_count;
+      }
+
       $nodes[$nid]->last_activity = $tracker_data[$nid]->changed;
-      $nodes[$nid]->comment_count = $comment_count;
     }
 
     // Display the data.
-- 
GitLab