message_thread-8.x-1.x-dev/src/MessageStatistics.php
src/MessageStatistics.php
<?php
namespace Drupal\message_thread;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\EntityOwnerInterface;
use Drupal\message_thread\Entity\MessageThread;
use Drupal\message\Entity\Message;
/**
* Drupal\message_thread\MessageStatistics.
*/
class MessageStatistics implements MessageStatisticsInterface {
/**
* 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\EntityTypeManagerInterface
*/
protected $entityManager;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs the MessageStatistics 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\EntityTypeManagerInterface $entity_type_manager
* The entity manager service.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
public function __construct(Connection $database, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, StateInterface $state) {
$this->database = $database;
$this->currentUser = $current_user;
$this->entityManager = $entity_type_manager;
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public function read(array $entities, string $entity_type, $accurate = TRUE) {
$options = $accurate ? [] : ['target' => 'replica'];
$stats = $this->database->select('message_thread_statistics', 'mts', $options)
->fields('mts')
->condition('mts.entity_id', array_keys($entities), 'IN')
->condition('mts.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('message_thread_statistics')
->condition('entity_id', $entity->id())
->condition('entity_type', $entity->getEntityTypeId())
->execute();
}
/**
* {@inheritdoc}
*/
public function create(FieldableEntityInterface $entity) {
$query = $this->database->insert('message_thread_statistics')
->fields([
'entity_id',
'entity_type',
'mid',
'last_message_timestamp',
'last_message_name',
'last_message_uid',
'message_count',
]);
// Get the user ID from the entity if it's set, or default to the
// currently logged in user.
$last_message_uid = 0;
if ($entity instanceof EntityOwnerInterface) {
$last_message_uid = $entity->getOwnerId();
}
if (!isset($last_message_uid)) {
// Default to current user when entity does not implement
// EntityOwnerInterface or author is not set.
$last_message_uid = $this->currentUser->id();
}
// Default to REQUEST_TIME when entity does not have a changed property.
$last_message_timestamp = \Drupal::time()->getRequestTime();
// @todo Make comment statistics language aware and add some tests. See
// https://www.drupal.org/node/2318875
if ($entity instanceof EntityChangedInterface) {
$last_message_timestamp = $entity->getChangedTimeAcrossTranslations();
}
$query->values([
'entity_id' => $entity->id(),
'entity_type' => $entity->getEntityTypeId(),
'mid' => 0,
'last_message_timestamp' => $last_message_timestamp,
'last_message_name' => NULL,
'last_message_uid' => $last_message_uid,
'message_count' => 0,
]);
$query->execute();
}
/**
* {@inheritdoc}
*/
public function getMaximumCount($entity_type) {
return $this->database->query('SELECT MAX(message_count) FROM {message_thread_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' => 'message_thread_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 = 'message'",
],
// 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 :message_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.message_count * (ROUND(:message_scale, 4)))',
'arguments' => [':message_scale' => \Drupal::state()->get('message.node_message_statistics_scale') ?: 0],
],
];
}
/**
* {@inheritdoc}
*/
public function update(Message $message) {
$thread_id = message_thread_relationship($message->id());
if (!$thread_id) {
return;
}
$message_thread = MessageThread::load($thread_id);
// This is relevant during a delete operation.
if (!$message_thread) {
return;
}
// Allow bulk updates and inserts to temporarily disable the maintenance of
// the {message_thread_statistics} table.
if (!$this->state->get('message.maintain_entity_statistics')) {
return;
}
$query = $this->database->select('message_field_data', 'm');
$query->join('message_thread_index', 'i', 'm.mid=i.mid');
$query->addExpression('COUNT(m.mid)');
$count = $query->condition('i.thread_id', $thread_id)
->condition('m.default_langcode', 1)
->execute()
->fetchField();
if ($count > 0) {
// Messages exist.
$query = $this->database->select('message_field_data', 'm');
$query->join('message_thread_index', 'i', 'm.mid=i.mid');
$last_reply = $query->fields('m', ['mid', 'created', 'uid'])
->condition('i.thread_id', $thread_id)
->condition('m.default_langcode', 1)
->orderBy('m.created', 'DESC')
->range(0, 1)
->execute()
->fetchObject();
// Use merge here because entity could be created before comment field.
$this->database->merge('message_thread_statistics')
->fields([
'mid' => $last_reply->mid,
'message_count' => $count,
'last_message_timestamp' => $last_reply->created,
'last_message_name' => $last_reply->uid ? '' : $last_reply->name,
'last_message_uid' => $last_reply->uid,
])
->keys([
'entity_id' => $thread_id,
'entity_type' => $message_thread->getEntityTypeId(),
])
->execute();
}
else {
// Messages do not exist.
$entity = $message_thread->id();
// Get the user ID from the entity if it's set, or default to the
// currently logged in user.
if ($entity instanceof EntityOwnerInterface) {
$last_message_uid = $entity->getOwnerId();
}
if (!isset($last_message_uid)) {
// Default to current user when entity does not implement
// EntityOwnerInterface or author is not set.
$last_message_uid = $this->currentUser->id();
}
$this->database->update('message_thread_statistics')
->fields([
'mid' => 0,
'message_count' => 0,
// Use the changed date of the entity if it's set, or default to
// REQUEST_TIME.
'last_message_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTimeAcrossTranslations() : \Drupal::time()->getRequestTime(),
'last_message_name' => '',
'last_message_uid' => $last_message_uid,
])
->condition('entity_id', $thread_id)
->condition('entity_type', $message_thread->getEntityTypeId())
->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($message->getEntityTypeId())->resetCache([$message_thread->id()]);
}
}
