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