Version 1
[yaffs-website] / web / core / modules / comment / src / CommentStatistics.php
diff --git a/web/core/modules/comment/src/CommentStatistics.php b/web/core/modules/comment/src/CommentStatistics.php
new file mode 100644 (file)
index 0000000..3c6b571
--- /dev/null
@@ -0,0 +1,261 @@
+<?php
+
+namespace Drupal\comment;
+
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\State\StateInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\EntityOwnerInterface;
+
+class CommentStatistics implements CommentStatisticsInterface {
+
+  /**
+   * The current database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The current logged in user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * Constructs the CommentStatistics service.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   The active database connection.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current logged in user.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state service.
+   */
+  public function __construct(Connection $database, AccountInterface $current_user, EntityManagerInterface $entity_manager, StateInterface $state) {
+    $this->database = $database;
+    $this->currentUser = $current_user;
+    $this->entityManager = $entity_manager;
+    $this->state = $state;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function read($entities, $entity_type, $accurate = TRUE) {
+    $options = $accurate ? [] : ['target' => 'replica'];
+    $stats = $this->database->select('comment_entity_statistics', 'ces', $options)
+      ->fields('ces')
+      ->condition('ces.entity_id', array_keys($entities), 'IN')
+      ->condition('ces.entity_type', $entity_type)
+      ->execute();
+
+    $statistics_records = [];
+    while ($entry = $stats->fetchObject()) {
+      $statistics_records[] = $entry;
+    }
+    return $statistics_records;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete(EntityInterface $entity) {
+    $this->database->delete('comment_entity_statistics')
+      ->condition('entity_id', $entity->id())
+      ->condition('entity_type', $entity->getEntityTypeId())
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function create(FieldableEntityInterface $entity, $fields) {
+    $query = $this->database->insert('comment_entity_statistics')
+      ->fields([
+        'entity_id',
+        'entity_type',
+        'field_name',
+        'cid',
+        'last_comment_timestamp',
+        'last_comment_name',
+        'last_comment_uid',
+        'comment_count',
+      ]);
+    foreach ($fields as $field_name => $detail) {
+      // Skip fields that entity does not have.
+      if (!$entity->hasField($field_name)) {
+        continue;
+      }
+      // Get the user ID from the entity if it's set, or default to the
+      // currently logged in user.
+      $last_comment_uid = 0;
+      if ($entity instanceof EntityOwnerInterface) {
+        $last_comment_uid = $entity->getOwnerId();
+      }
+      if (!isset($last_comment_uid)) {
+        // Default to current user when entity does not implement
+        // EntityOwnerInterface or author is not set.
+        $last_comment_uid = $this->currentUser->id();
+      }
+      // Default to REQUEST_TIME when entity does not have a changed property.
+      $last_comment_timestamp = REQUEST_TIME;
+      // @todo Make comment statistics language aware and add some tests. See
+      //   https://www.drupal.org/node/2318875
+      if ($entity instanceof EntityChangedInterface) {
+        $last_comment_timestamp = $entity->getChangedTimeAcrossTranslations();
+      }
+      $query->values([
+        'entity_id' => $entity->id(),
+        'entity_type' => $entity->getEntityTypeId(),
+        'field_name' => $field_name,
+        'cid' => 0,
+        'last_comment_timestamp' => $last_comment_timestamp,
+        'last_comment_name' => NULL,
+        'last_comment_uid' => $last_comment_uid,
+        'comment_count' => 0,
+      ]);
+    }
+    $query->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMaximumCount($entity_type) {
+    return $this->database->query('SELECT MAX(comment_count) FROM {comment_entity_statistics} WHERE entity_type = :entity_type', [':entity_type' => $entity_type])->fetchField();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRankingInfo() {
+    return [
+      'comments' => [
+        'title' => t('Number of comments'),
+        'join' => [
+          'type' => 'LEFT',
+          'table' => 'comment_entity_statistics',
+          'alias' => 'ces',
+          // Default to comment field as this is the most common use case for
+          // nodes.
+          'on' => "ces.entity_id = i.sid AND ces.entity_type = 'node' AND ces.field_name = 'comment'",
+        ],
+        // Inverse law that maps the highest view count on the site to 1 and 0
+        // to 0. Note that the ROUND here is necessary for PostgreSQL and SQLite
+        // in order to ensure that the :comment_scale argument is treated as
+        // a numeric type, because the PostgreSQL PDO driver sometimes puts
+        // values in as strings instead of numbers in complex expressions like
+        // this.
+        'score' => '2.0 - 2.0 / (1.0 + ces.comment_count * (ROUND(:comment_scale, 4)))',
+        'arguments' => [':comment_scale' => \Drupal::state()->get('comment.node_comment_statistics_scale') ?: 0],
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function update(CommentInterface $comment) {
+    // Allow bulk updates and inserts to temporarily disable the maintenance of
+    // the {comment_entity_statistics} table.
+    if (!$this->state->get('comment.maintain_entity_statistics')) {
+      return;
+    }
+
+    $query = $this->database->select('comment_field_data', 'c');
+    $query->addExpression('COUNT(cid)');
+    $count = $query->condition('c.entity_id', $comment->getCommentedEntityId())
+      ->condition('c.entity_type', $comment->getCommentedEntityTypeId())
+      ->condition('c.field_name', $comment->getFieldName())
+      ->condition('c.status', CommentInterface::PUBLISHED)
+      ->condition('default_langcode', 1)
+      ->execute()
+      ->fetchField();
+
+    if ($count > 0) {
+      // Comments exist.
+      $last_reply = $this->database->select('comment_field_data', 'c')
+        ->fields('c', ['cid', 'name', 'changed', 'uid'])
+        ->condition('c.entity_id', $comment->getCommentedEntityId())
+        ->condition('c.entity_type', $comment->getCommentedEntityTypeId())
+        ->condition('c.field_name', $comment->getFieldName())
+        ->condition('c.status', CommentInterface::PUBLISHED)
+        ->condition('default_langcode', 1)
+        ->orderBy('c.created', 'DESC')
+        ->range(0, 1)
+        ->execute()
+        ->fetchObject();
+      // Use merge here because entity could be created before comment field.
+      $this->database->merge('comment_entity_statistics')
+        ->fields([
+          'cid' => $last_reply->cid,
+          'comment_count' => $count,
+          'last_comment_timestamp' => $last_reply->changed,
+          'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
+          'last_comment_uid' => $last_reply->uid,
+        ])
+        ->keys([
+          'entity_id' => $comment->getCommentedEntityId(),
+          'entity_type' => $comment->getCommentedEntityTypeId(),
+          'field_name' => $comment->getFieldName(),
+        ])
+        ->execute();
+    }
+    else {
+      // Comments do not exist.
+      $entity = $comment->getCommentedEntity();
+      // Get the user ID from the entity if it's set, or default to the
+      // currently logged in user.
+      if ($entity instanceof EntityOwnerInterface) {
+        $last_comment_uid = $entity->getOwnerId();
+      }
+      if (!isset($last_comment_uid)) {
+        // Default to current user when entity does not implement
+        // EntityOwnerInterface or author is not set.
+        $last_comment_uid = $this->currentUser->id();
+      }
+      $this->database->update('comment_entity_statistics')
+        ->fields([
+          'cid' => 0,
+          'comment_count' => 0,
+          // Use the changed date of the entity if it's set, or default to
+          // REQUEST_TIME.
+          'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTimeAcrossTranslations() : REQUEST_TIME,
+          'last_comment_name' => '',
+          'last_comment_uid' => $last_comment_uid,
+        ])
+        ->condition('entity_id', $comment->getCommentedEntityId())
+        ->condition('entity_type', $comment->getCommentedEntityTypeId())
+        ->condition('field_name', $comment->getFieldName())
+        ->execute();
+    }
+
+    // Reset the cache of the commented entity so that when the entity is loaded
+    // the next time, the statistics will be loaded again.
+    $this->entityManager->getStorage($comment->getCommentedEntityTypeId())->resetCache([$comment->getCommentedEntityId()]);
+  }
+
+}