Commit 5f53aef8 authored by catch's avatar catch

Issue #2101183 by larowlan, andypost, benjy: Move {comment_entity_statistics} to proper service.

parent 895d4ab8
......@@ -12,7 +12,6 @@
use Drupal\comment\CommentInterface;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\field\FieldInstanceConfigInterface;
......@@ -863,13 +862,9 @@ function comment_entity_load($entities, $entity_type) {
// Do not query database when entity has no comment fields.
return;
}
// Load comment information from the database and update the entity's comment
// statistics properties, which are defined on each CommentItem field.
$result = db_select('comment_entity_statistics', 'ces')
->fields('ces')
->condition('ces.entity_id', array_keys($entities))
->condition('ces.entity_type', $entity_type)
->execute();
// Load comment information from the database and update the entity's
// comment statistics properties, which are defined on each CommentItem field.
$result = \Drupal::service('comment.statistics')->read($entities, $entity_type);
foreach ($result as $record) {
$parts = explode('__', $record->field_id, 2);
list(, $field_name) = $parts;
......@@ -895,54 +890,7 @@ function comment_entity_insert(EntityInterface $entity) {
// maintenance of the {comment_entity_statistics} table.
if (\Drupal::state()->get('comment.maintain_entity_statistics') &&
$fields = \Drupal::service('comment.manager')->getFields($entity->getEntityTypeId())) {
$query = db_insert('comment_entity_statistics')
->fields(array(
'entity_id',
'entity_type',
'field_id',
'cid',
'last_comment_timestamp',
'last_comment_name',
'last_comment_uid',
'comment_count',
));
$execute_query = FALSE;
foreach ($fields as $field_name => $detail) {
// Skip fields that entity does not have.
if (!$entity->hasField($field_name)) {
continue;
}
// There is at least one comment field, the query needs to be executed.
$execute_query = TRUE;
// Get the user ID from the entity if it is 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 = \Drupal::currentUser()->id();
}
// Default to REQUEST_TIME when entity does not have a changed property.
$last_comment_timestamp = REQUEST_TIME;
if ($entity instanceof EntityChangedInterface) {
$last_comment_timestamp = $entity->getChangedTime();
}
$query->values(array(
'entity_id' => $entity->id(),
'entity_type' => $entity->getEntityTypeId(),
'field_id' => $entity->getEntityTypeId() . '__' . $field_name,
'cid' => 0,
'last_comment_timestamp' => $last_comment_timestamp,
'last_comment_name' => NULL,
'last_comment_uid' => $last_comment_uid,
'comment_count' => 0,
));
}
if ($execute_query) {
$query->execute();
}
\Drupal::service('comment.statistics')->create($entity, $fields);
}
}
......@@ -964,10 +912,7 @@ function comment_entity_predelete(EntityInterface $entity) {
->execute()
->fetchCol();
entity_delete_multiple('comment', $cids);
db_delete('comment_entity_statistics')
->condition('entity_id', (int) $entity->id())
->condition('entity_type', $entity->getEntityTypeId())
->execute();
\Drupal::service('comment.statistics')->delete($entity);
}
}
......@@ -1028,7 +973,7 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
function comment_cron() {
// Store the maximum possible comments per thread (used for node search
// ranking by reply count).
\Drupal::state()->set('comment.node_comment_statistics_scale', 1.0 / max(1.0, db_query('SELECT MAX(comment_count) FROM {comment_entity_statistics}')->fetchField()));
\Drupal::state()->set('comment.node_comment_statistics_scale', 1.0 / max(1, \Drupal::service('comment.statistics')->getMaximumCount('node')));
}
/**
......@@ -1530,25 +1475,7 @@ function _comment_per_page() {
* Implements hook_ranking().
*/
function comment_ranking() {
return array(
'comments' => array(
'title' => t('Number of comments'),
'join' => array(
'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_id = 'node__comment'",
),
// Inverse law that maps the highest reply count on the site to 1 and 0
// to 0. Note that the CAST here is necessary for PostgreSQL, 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 * (CAST (:comment_scale AS DECIMAL(10, 4))))',
'arguments' => array(':comment_scale' => \Drupal::state()->get('comment.node_comment_statistics_scale') ?: 0),
),
);
return \Drupal::service('comment.statistics')->getRankingInfo();
}
/**
......
......@@ -9,6 +9,10 @@ services:
class: Drupal\comment\CommentManager
arguments: ['@field.info', '@entity.manager', '@current_user', '@config.factory', '@string_translation', '@url_generator']
comment.statistics:
class: Drupal\comment\CommentStatistics
arguments: ['@database', '@current_user', '@entity.manager', '@state']
comment.route_enhancer:
class: Drupal\comment\Routing\CommentBundleEnhancer
arguments: ['@entity.manager']
......
<?php
/**
* @file
* Contains \Drupal\comment\CommentStatistics.
*/
namespace Drupal\comment;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\KeyValueStore\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\KeyValueStore\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\KeyValueStore\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) {
return $this->database->select('comment_entity_statistics', 'ces')
->fields('ces')
->condition('ces.entity_id', array_keys($entities))
->condition('ces.entity_type', $entity_type)
->execute();
}
/**
* {@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(ContentEntityInterface $entity, $fields) {
$query = $this->database->insert('comment_entity_statistics')
->fields(array(
'entity_id',
'entity_type',
'field_id',
'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;
if ($entity instanceof EntityChangedInterface) {
$last_comment_timestamp = $entity->getChangedTime();
}
$query->values(array(
'entity_id' => $entity->id(),
'entity_type' => $entity->getEntityTypeId(),
'field_id' => $entity->getEntityTypeId() . '__' . $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', array(':entity_type' => $entity_type))->fetchField();
}
/**
* {@inheritdoc}
*/
public function getRankingInfo() {
return array(
'comments' => array(
'title' => t('Number of comments'),
'join' => array(
'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_id = 'node__comment'",
),
// Inverse law that maps the highest reply count on the site to 1 and 0
// to 0. Note that the CAST here is necessary for PostgreSQL, 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 * (CAST (:comment_scale AS DECIMAL(10, 4))))',
'arguments' => array(':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', 'c');
$query->addExpression('COUNT(cid)');
$count = $query->condition('c.entity_id', $comment->getCommentedEntityId())
->condition('c.entity_type', $comment->getCommentedEntityTypeId())
->condition('c.field_id', $comment->getFieldId())
->condition('c.status', CommentInterface::PUBLISHED)
->execute()
->fetchField();
if ($count > 0) {
// Comments exist.
$last_reply = $this->database->select('comment', 'c')
->fields('c', array('cid', 'name', 'changed', 'uid'))
->condition('c.entity_id', $comment->getCommentedEntityId())
->condition('c.entity_type', $comment->getCommentedEntityTypeId())
->condition('c.field_id', $comment->getFieldId())
->condition('c.status', CommentInterface::PUBLISHED)
->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(array(
'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(array(
'entity_id' => $comment->getCommentedEntityId(),
'entity_type' => $comment->getCommentedEntityTypeId(),
'field_id' => $comment->getFieldId(),
))
->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(array(
'cid' => 0,
'comment_count' => 0,
// Use the created date of the entity if it's set, or default to
// REQUEST_TIME.
'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTime() : REQUEST_TIME,
'last_comment_name' => '',
'last_comment_uid' => $last_comment_uid,
))
->condition('entity_id', $comment->getCommentedEntityId())
->condition('entity_type', $comment->getCommentedEntityTypeId())
->condition('field_id', $comment->getFieldId())
->execute();
}
}
}
<?php
/**
* @file
* Contains \Drupal\comment\CommentStatisticsInterface.
*/
namespace Drupal\comment;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
/**
* Provides an interface for storing and retrieving comment statistics.
*/
interface CommentStatisticsInterface {
/**
* Returns an array of ranking information for hook_ranking().
*
* @return array
* Array of ranking information as expected by hook_ranking().
*
* @see hook_ranking()
* @see comment_ranking()
*/
public function getRankingInfo();
/**
* Read comment statistics records for an array of entities.
*
* @param \Drupal\Core\Entity\EntityInterface[] $entities
* Array of entities on which commenting is enabled, keyed by id
* @param string $entity_type
* The entity type of the passed entities.
*
* @return object[]
* Array of statistics records keyed by entity id.
*/
public function read($entities, $entity_type);
/**
* Delete comment statistics records for an entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which comment statistics should be deleted.
*/
public function delete(EntityInterface $entity);
/**
* Update or insert comment statistics records after a comment is added.
*
* @param \Drupal\comment\CommentInterface $comment
* The comment added or updated.
*/
public function update(CommentInterface $comment);
/**
* Find the maximum number of comments for the given entity type.
*
* Used to influence search rankings.
*
* @param string $entity_type
* The entity type to consider when fetching the maximum comment count for.
*
* @return int
* The maximum number of comments for and entity of the given type.
*
* @see comment_update_index()
*/
public function getMaximumCount($entity_type);
/**
* Insert an empty record for the given entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The created entity for which a statistics record is to be initialized.
* @param array $fields
* Array of comment field definitions for the given entity.
*/
public function create(ContentEntityInterface $entity, $fields);
}
......@@ -7,10 +7,12 @@
namespace Drupal\comment;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\FieldableDatabaseStorageController;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\user\EntityOwnerInterface;
use Drupal\field\FieldInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the controller class for comments.
......@@ -20,6 +22,43 @@
*/
class CommentStorageController extends FieldableDatabaseStorageController implements CommentStorageControllerInterface {
/**
* The comment statistics service.
*
* @var \Drupal\comment\CommentStatisticsInterface
*/
protected $statistics;
/**
* Constructs a CommentStorageController object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
* An array of entity info for the entity type.
* @param \Drupal\Core\Database\Connection $database
* The database connection to be used.
* @param \Drupal\field\FieldInfo $field_info
* The field info service.
* @param \Drupal\comment\CommentStatisticsInterface $comment_statistics
* The comment statistics service.
*/
public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info, CommentStatisticsInterface $comment_statistics) {
parent::__construct($entity_info, $database, $field_info);
$this->statistics = $comment_statistics;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
return new static(
$entity_info,
$container->get('database'),
$container->get('field.info'),
$container->get('comment.statistics')
);
}
/**
* {@inheritdoc}
*/
......@@ -47,77 +86,7 @@ protected function postLoad(array &$queried_entities) {
* {@inheritdoc}
*/
public function updateEntityStatistics(CommentInterface $comment) {
// Allow bulk updates and inserts to temporarily disable the maintenance of
// the {comment_entity_statistics} table.
if (!\Drupal::state()->get('comment.maintain_entity_statistics')) {
return;
}
$query = $this->database->select('comment', 'c');
$query->addExpression('COUNT(cid)');
$count = $query->condition('c.entity_id', $comment->getCommentedEntityId())
->condition('c.entity_type', $comment->getCommentedEntityTypeId())
->condition('c.field_id', $comment->getFieldId())
->condition('c.status', CommentInterface::PUBLISHED)
->execute()
->fetchField();
if ($count > 0) {
// Comments exist.
$last_reply = $this->database->select('comment', 'c')
->fields('c', array('cid', 'name', 'changed', 'uid'))
->condition('c.entity_id', $comment->getCommentedEntityId())
->condition('c.entity_type', $comment->getCommentedEntityTypeId())
->condition('c.field_id', $comment->getFieldId())
->condition('c.status', CommentInterface::PUBLISHED)
->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(array(
'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(array(
'entity_id' => $comment->getCommentedEntityId(),
'entity_type' => $comment->getCommentedEntityTypeId(),
'field_id' => $comment->getFieldId(),
))
->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 = \Drupal::currentUser()->id();
}
$this->database->update('comment_entity_statistics')
->fields(array(
'cid' => 0,
'comment_count' => 0,
// Use the created date of the entity if it's set, or default to
// REQUEST_TIME.
'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTime() : REQUEST_TIME,
'last_comment_name' => '',
'last_comment_uid' => $last_comment_uid,
))
->condition('entity_id', $comment->getCommentedEntityId())
->condition('entity_type', $comment->getCommentedEntityTypeId())
->condition('field_id', $comment->getFieldId())
->execute();
}
$this->statistics->update($comment);
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment