annotate core/modules/comment/src/CommentStatistics.php @ 0:c75dbcec494b

Initial commit from drush-created site
author Chris Cannam
date Thu, 05 Jul 2018 14:24:15 +0000
parents
children 12f9dff5fda9
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\comment;
Chris@0 4
Chris@0 5 use Drupal\Core\Database\Connection;
Chris@0 6 use Drupal\Core\Entity\FieldableEntityInterface;
Chris@0 7 use Drupal\Core\Entity\EntityChangedInterface;
Chris@0 8 use Drupal\Core\Entity\EntityInterface;
Chris@0 9 use Drupal\Core\Entity\EntityManagerInterface;
Chris@0 10 use Drupal\Core\State\StateInterface;
Chris@0 11 use Drupal\Core\Session\AccountInterface;
Chris@0 12 use Drupal\user\EntityOwnerInterface;
Chris@0 13
Chris@0 14 class CommentStatistics implements CommentStatisticsInterface {
Chris@0 15
Chris@0 16 /**
Chris@0 17 * The current database connection.
Chris@0 18 *
Chris@0 19 * @var \Drupal\Core\Database\Connection
Chris@0 20 */
Chris@0 21 protected $database;
Chris@0 22
Chris@0 23 /**
Chris@0 24 * The current logged in user.
Chris@0 25 *
Chris@0 26 * @var \Drupal\Core\Session\AccountInterface
Chris@0 27 */
Chris@0 28 protected $currentUser;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * The entity manager service.
Chris@0 32 *
Chris@0 33 * @var \Drupal\Core\Entity\EntityManagerInterface
Chris@0 34 */
Chris@0 35 protected $entityManager;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * The state service.
Chris@0 39 *
Chris@0 40 * @var \Drupal\Core\State\StateInterface
Chris@0 41 */
Chris@0 42 protected $state;
Chris@0 43
Chris@0 44 /**
Chris@0 45 * Constructs the CommentStatistics service.
Chris@0 46 *
Chris@0 47 * @param \Drupal\Core\Database\Connection $database
Chris@0 48 * The active database connection.
Chris@0 49 * @param \Drupal\Core\Session\AccountInterface $current_user
Chris@0 50 * The current logged in user.
Chris@0 51 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
Chris@0 52 * The entity manager service.
Chris@0 53 * @param \Drupal\Core\State\StateInterface $state
Chris@0 54 * The state service.
Chris@0 55 */
Chris@0 56 public function __construct(Connection $database, AccountInterface $current_user, EntityManagerInterface $entity_manager, StateInterface $state) {
Chris@0 57 $this->database = $database;
Chris@0 58 $this->currentUser = $current_user;
Chris@0 59 $this->entityManager = $entity_manager;
Chris@0 60 $this->state = $state;
Chris@0 61 }
Chris@0 62
Chris@0 63 /**
Chris@0 64 * {@inheritdoc}
Chris@0 65 */
Chris@0 66 public function read($entities, $entity_type, $accurate = TRUE) {
Chris@0 67 $options = $accurate ? [] : ['target' => 'replica'];
Chris@0 68 $stats = $this->database->select('comment_entity_statistics', 'ces', $options)
Chris@0 69 ->fields('ces')
Chris@0 70 ->condition('ces.entity_id', array_keys($entities), 'IN')
Chris@0 71 ->condition('ces.entity_type', $entity_type)
Chris@0 72 ->execute();
Chris@0 73
Chris@0 74 $statistics_records = [];
Chris@0 75 while ($entry = $stats->fetchObject()) {
Chris@0 76 $statistics_records[] = $entry;
Chris@0 77 }
Chris@0 78 return $statistics_records;
Chris@0 79 }
Chris@0 80
Chris@0 81 /**
Chris@0 82 * {@inheritdoc}
Chris@0 83 */
Chris@0 84 public function delete(EntityInterface $entity) {
Chris@0 85 $this->database->delete('comment_entity_statistics')
Chris@0 86 ->condition('entity_id', $entity->id())
Chris@0 87 ->condition('entity_type', $entity->getEntityTypeId())
Chris@0 88 ->execute();
Chris@0 89 }
Chris@0 90
Chris@0 91 /**
Chris@0 92 * {@inheritdoc}
Chris@0 93 */
Chris@0 94 public function create(FieldableEntityInterface $entity, $fields) {
Chris@0 95 $query = $this->database->insert('comment_entity_statistics')
Chris@0 96 ->fields([
Chris@0 97 'entity_id',
Chris@0 98 'entity_type',
Chris@0 99 'field_name',
Chris@0 100 'cid',
Chris@0 101 'last_comment_timestamp',
Chris@0 102 'last_comment_name',
Chris@0 103 'last_comment_uid',
Chris@0 104 'comment_count',
Chris@0 105 ]);
Chris@0 106 foreach ($fields as $field_name => $detail) {
Chris@0 107 // Skip fields that entity does not have.
Chris@0 108 if (!$entity->hasField($field_name)) {
Chris@0 109 continue;
Chris@0 110 }
Chris@0 111 // Get the user ID from the entity if it's set, or default to the
Chris@0 112 // currently logged in user.
Chris@0 113 $last_comment_uid = 0;
Chris@0 114 if ($entity instanceof EntityOwnerInterface) {
Chris@0 115 $last_comment_uid = $entity->getOwnerId();
Chris@0 116 }
Chris@0 117 if (!isset($last_comment_uid)) {
Chris@0 118 // Default to current user when entity does not implement
Chris@0 119 // EntityOwnerInterface or author is not set.
Chris@0 120 $last_comment_uid = $this->currentUser->id();
Chris@0 121 }
Chris@0 122 // Default to REQUEST_TIME when entity does not have a changed property.
Chris@0 123 $last_comment_timestamp = REQUEST_TIME;
Chris@0 124 // @todo Make comment statistics language aware and add some tests. See
Chris@0 125 // https://www.drupal.org/node/2318875
Chris@0 126 if ($entity instanceof EntityChangedInterface) {
Chris@0 127 $last_comment_timestamp = $entity->getChangedTimeAcrossTranslations();
Chris@0 128 }
Chris@0 129 $query->values([
Chris@0 130 'entity_id' => $entity->id(),
Chris@0 131 'entity_type' => $entity->getEntityTypeId(),
Chris@0 132 'field_name' => $field_name,
Chris@0 133 'cid' => 0,
Chris@0 134 'last_comment_timestamp' => $last_comment_timestamp,
Chris@0 135 'last_comment_name' => NULL,
Chris@0 136 'last_comment_uid' => $last_comment_uid,
Chris@0 137 'comment_count' => 0,
Chris@0 138 ]);
Chris@0 139 }
Chris@0 140 $query->execute();
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * {@inheritdoc}
Chris@0 145 */
Chris@0 146 public function getMaximumCount($entity_type) {
Chris@0 147 return $this->database->query('SELECT MAX(comment_count) FROM {comment_entity_statistics} WHERE entity_type = :entity_type', [':entity_type' => $entity_type])->fetchField();
Chris@0 148 }
Chris@0 149
Chris@0 150 /**
Chris@0 151 * {@inheritdoc}
Chris@0 152 */
Chris@0 153 public function getRankingInfo() {
Chris@0 154 return [
Chris@0 155 'comments' => [
Chris@0 156 'title' => t('Number of comments'),
Chris@0 157 'join' => [
Chris@0 158 'type' => 'LEFT',
Chris@0 159 'table' => 'comment_entity_statistics',
Chris@0 160 'alias' => 'ces',
Chris@0 161 // Default to comment field as this is the most common use case for
Chris@0 162 // nodes.
Chris@0 163 'on' => "ces.entity_id = i.sid AND ces.entity_type = 'node' AND ces.field_name = 'comment'",
Chris@0 164 ],
Chris@0 165 // Inverse law that maps the highest view count on the site to 1 and 0
Chris@0 166 // to 0. Note that the ROUND here is necessary for PostgreSQL and SQLite
Chris@0 167 // in order to ensure that the :comment_scale argument is treated as
Chris@0 168 // a numeric type, because the PostgreSQL PDO driver sometimes puts
Chris@0 169 // values in as strings instead of numbers in complex expressions like
Chris@0 170 // this.
Chris@0 171 'score' => '2.0 - 2.0 / (1.0 + ces.comment_count * (ROUND(:comment_scale, 4)))',
Chris@0 172 'arguments' => [':comment_scale' => \Drupal::state()->get('comment.node_comment_statistics_scale') ?: 0],
Chris@0 173 ],
Chris@0 174 ];
Chris@0 175 }
Chris@0 176
Chris@0 177 /**
Chris@0 178 * {@inheritdoc}
Chris@0 179 */
Chris@0 180 public function update(CommentInterface $comment) {
Chris@0 181 // Allow bulk updates and inserts to temporarily disable the maintenance of
Chris@0 182 // the {comment_entity_statistics} table.
Chris@0 183 if (!$this->state->get('comment.maintain_entity_statistics')) {
Chris@0 184 return;
Chris@0 185 }
Chris@0 186
Chris@0 187 $query = $this->database->select('comment_field_data', 'c');
Chris@0 188 $query->addExpression('COUNT(cid)');
Chris@0 189 $count = $query->condition('c.entity_id', $comment->getCommentedEntityId())
Chris@0 190 ->condition('c.entity_type', $comment->getCommentedEntityTypeId())
Chris@0 191 ->condition('c.field_name', $comment->getFieldName())
Chris@0 192 ->condition('c.status', CommentInterface::PUBLISHED)
Chris@0 193 ->condition('default_langcode', 1)
Chris@0 194 ->execute()
Chris@0 195 ->fetchField();
Chris@0 196
Chris@0 197 if ($count > 0) {
Chris@0 198 // Comments exist.
Chris@0 199 $last_reply = $this->database->select('comment_field_data', 'c')
Chris@0 200 ->fields('c', ['cid', 'name', 'changed', 'uid'])
Chris@0 201 ->condition('c.entity_id', $comment->getCommentedEntityId())
Chris@0 202 ->condition('c.entity_type', $comment->getCommentedEntityTypeId())
Chris@0 203 ->condition('c.field_name', $comment->getFieldName())
Chris@0 204 ->condition('c.status', CommentInterface::PUBLISHED)
Chris@0 205 ->condition('default_langcode', 1)
Chris@0 206 ->orderBy('c.created', 'DESC')
Chris@0 207 ->range(0, 1)
Chris@0 208 ->execute()
Chris@0 209 ->fetchObject();
Chris@0 210 // Use merge here because entity could be created before comment field.
Chris@0 211 $this->database->merge('comment_entity_statistics')
Chris@0 212 ->fields([
Chris@0 213 'cid' => $last_reply->cid,
Chris@0 214 'comment_count' => $count,
Chris@0 215 'last_comment_timestamp' => $last_reply->changed,
Chris@0 216 'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
Chris@0 217 'last_comment_uid' => $last_reply->uid,
Chris@0 218 ])
Chris@0 219 ->keys([
Chris@0 220 'entity_id' => $comment->getCommentedEntityId(),
Chris@0 221 'entity_type' => $comment->getCommentedEntityTypeId(),
Chris@0 222 'field_name' => $comment->getFieldName(),
Chris@0 223 ])
Chris@0 224 ->execute();
Chris@0 225 }
Chris@0 226 else {
Chris@0 227 // Comments do not exist.
Chris@0 228 $entity = $comment->getCommentedEntity();
Chris@0 229 // Get the user ID from the entity if it's set, or default to the
Chris@0 230 // currently logged in user.
Chris@0 231 if ($entity instanceof EntityOwnerInterface) {
Chris@0 232 $last_comment_uid = $entity->getOwnerId();
Chris@0 233 }
Chris@0 234 if (!isset($last_comment_uid)) {
Chris@0 235 // Default to current user when entity does not implement
Chris@0 236 // EntityOwnerInterface or author is not set.
Chris@0 237 $last_comment_uid = $this->currentUser->id();
Chris@0 238 }
Chris@0 239 $this->database->update('comment_entity_statistics')
Chris@0 240 ->fields([
Chris@0 241 'cid' => 0,
Chris@0 242 'comment_count' => 0,
Chris@0 243 // Use the changed date of the entity if it's set, or default to
Chris@0 244 // REQUEST_TIME.
Chris@0 245 'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTimeAcrossTranslations() : REQUEST_TIME,
Chris@0 246 'last_comment_name' => '',
Chris@0 247 'last_comment_uid' => $last_comment_uid,
Chris@0 248 ])
Chris@0 249 ->condition('entity_id', $comment->getCommentedEntityId())
Chris@0 250 ->condition('entity_type', $comment->getCommentedEntityTypeId())
Chris@0 251 ->condition('field_name', $comment->getFieldName())
Chris@0 252 ->execute();
Chris@0 253 }
Chris@0 254
Chris@0 255 // Reset the cache of the commented entity so that when the entity is loaded
Chris@0 256 // the next time, the statistics will be loaded again.
Chris@0 257 $this->entityManager->getStorage($comment->getCommentedEntityTypeId())->resetCache([$comment->getCommentedEntityId()]);
Chris@0 258 }
Chris@0 259
Chris@0 260 }