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