Issue #1446932 by timmillwood, mallezie, RobLoach, hussainweb, Wim Leers, rpayanm, kostyashupenko, mparker17, dawehner, Berdir, Crell: Improve statistics performance by adding a swappable backend
parent
e043264b99
commit
ec59a51fe7
|
|
@ -0,0 +1,148 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\statistics;
|
||||||
|
|
||||||
|
use Drupal\Core\Database\Connection;
|
||||||
|
use Drupal\Core\State\StateInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the default database storage backend for statistics.
|
||||||
|
*/
|
||||||
|
class NodeStatisticsDatabaseStorage implements StatisticsStorageInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The database connection used.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Database\Connection
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state service.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\State\StateInterface
|
||||||
|
*/
|
||||||
|
protected $state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request stack.
|
||||||
|
*
|
||||||
|
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||||
|
*/
|
||||||
|
protected $requestStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the statistics storage.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Database\Connection $connection
|
||||||
|
* The database connection for the node view storage.
|
||||||
|
* @param \Drupal\Core\State\StateInterface $state
|
||||||
|
* The state service.
|
||||||
|
*/
|
||||||
|
public function __construct(Connection $connection, StateInterface $state, RequestStack $request_stack) {
|
||||||
|
$this->connection = $connection;
|
||||||
|
$this->state = $state;
|
||||||
|
$this->requestStack = $request_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function recordView($id) {
|
||||||
|
return (bool) $this->connection
|
||||||
|
->merge('node_counter')
|
||||||
|
->key('nid', $id)
|
||||||
|
->fields([
|
||||||
|
'daycount' => 1,
|
||||||
|
'totalcount' => 1,
|
||||||
|
'timestamp' => $this->getRequestTime(),
|
||||||
|
])
|
||||||
|
->expression('daycount', 'daycount + 1')
|
||||||
|
->expression('totalcount', 'totalcount + 1')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetchViews($ids) {
|
||||||
|
$views = $this->connection
|
||||||
|
->select('node_counter', 'nc')
|
||||||
|
->fields('nc', ['totalcount', 'daycount', 'timestamp'])
|
||||||
|
->condition('nid', $ids, 'IN')
|
||||||
|
->execute()
|
||||||
|
->fetchAll();
|
||||||
|
foreach ($views as $id => $view) {
|
||||||
|
$views[$id] = new StatisticsViewsResult($view->totalcount, $view->daycount, $view->timestamp);
|
||||||
|
}
|
||||||
|
return $views;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetchView($id) {
|
||||||
|
$views = $this->fetchViews(array($id));
|
||||||
|
return reset($views);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetchAll($order = 'totalcount', $limit = 5) {
|
||||||
|
assert(in_array($order, ['totalcount', 'daycount', 'timestamp']), "Invalid order argument.");
|
||||||
|
|
||||||
|
return $this->connection
|
||||||
|
->select('node_counter', 'nc')
|
||||||
|
->fields('nc', ['nid'])
|
||||||
|
->orderBy($order, 'DESC')
|
||||||
|
->range(0, $limit)
|
||||||
|
->execute()
|
||||||
|
->fetchCol();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function deleteViews($id) {
|
||||||
|
return (bool) $this->connection
|
||||||
|
->delete('node_counter')
|
||||||
|
->condition('nid', $id)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function resetDayCount() {
|
||||||
|
$statistics_timestamp = $this->state->get('statistics.day_timestamp') ?: 0;
|
||||||
|
if (($this->getRequestTime() - $statistics_timestamp) >= 86400) {
|
||||||
|
$this->state->set('statistics.day_timestamp', $this->getRequestTime());
|
||||||
|
$this->connection->update('node_counter')
|
||||||
|
->fields(['daycount' => 0])
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function maxTotalCount() {
|
||||||
|
$query = $this->connection->select('node_counter', 'nc');
|
||||||
|
$query->addExpression('MAX(totalcount)');
|
||||||
|
$max_total_count = (int)$query->execute()->fetchField();
|
||||||
|
return $max_total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current request time.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* Unix timestamp for current server request time.
|
||||||
|
*/
|
||||||
|
protected function getRequestTime() {
|
||||||
|
return $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,14 @@ namespace Drupal\statistics\Plugin\Block;
|
||||||
|
|
||||||
use Drupal\Core\Access\AccessResult;
|
use Drupal\Core\Access\AccessResult;
|
||||||
use Drupal\Core\Block\BlockBase;
|
use Drupal\Core\Block\BlockBase;
|
||||||
|
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||||
use Drupal\Core\Form\FormStateInterface;
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
|
use Drupal\Core\Render\RendererInterface;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||||
|
use Drupal\statistics\StatisticsStorageInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a 'Popular content' block.
|
* Provides a 'Popular content' block.
|
||||||
|
|
@ -15,7 +21,72 @@ use Drupal\Core\Session\AccountInterface;
|
||||||
* admin_label = @Translation("Popular content")
|
* admin_label = @Translation("Popular content")
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
class StatisticsPopularBlock extends BlockBase {
|
class StatisticsPopularBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity type manager.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||||
|
*/
|
||||||
|
protected $entityTypeManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity repository service.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Entity\EntityRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $entityRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The storage for statistics.
|
||||||
|
*
|
||||||
|
* @var \Drupal\statistics\StatisticsStorageInterface
|
||||||
|
*/
|
||||||
|
protected $statisticsStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\Core\Render\RendererInterface
|
||||||
|
*/
|
||||||
|
protected $renderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an StatisticsPopularBlock object.
|
||||||
|
*
|
||||||
|
* @param array $configuration
|
||||||
|
* A configuration array containing information about the plugin instance.
|
||||||
|
* @param string $plugin_id
|
||||||
|
* The plugin_id for the plugin instance.
|
||||||
|
* @param mixed $plugin_definition
|
||||||
|
* The plugin implementation definition.
|
||||||
|
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||||
|
* The entity type manager.
|
||||||
|
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
|
||||||
|
* The entity repository service
|
||||||
|
* @param \Drupal\statistics\StatisticsStorageInterface $statistics_storage
|
||||||
|
* The storage for statistics.
|
||||||
|
*/
|
||||||
|
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityRepositoryInterface $entity_repository, StatisticsStorageInterface $statistics_storage, RendererInterface $renderer) {
|
||||||
|
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||||
|
$this->entityTypeManager = $entity_type_manager;
|
||||||
|
$this->entityRepository = $entity_repository;
|
||||||
|
$this->statisticsStorage = $statistics_storage;
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||||
|
return new static(
|
||||||
|
$configuration,
|
||||||
|
$plugin_id,
|
||||||
|
$plugin_definition,
|
||||||
|
$container->get('entity_type.manager'),
|
||||||
|
$container->get('entity.repository'),
|
||||||
|
$container->get('statistics.storage.node'),
|
||||||
|
$container->get('renderer')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
|
|
@ -82,28 +153,64 @@ class StatisticsPopularBlock extends BlockBase {
|
||||||
$content = array();
|
$content = array();
|
||||||
|
|
||||||
if ($this->configuration['top_day_num'] > 0) {
|
if ($this->configuration['top_day_num'] > 0) {
|
||||||
$result = statistics_title_list('daycount', $this->configuration['top_day_num']);
|
$nids = $this->statisticsStorage->fetchAll('daycount', $this->configuration['top_day_num']);
|
||||||
if ($result) {
|
if ($nids) {
|
||||||
$content['top_day'] = node_title_list($result, $this->t("Today's:"));
|
$content['top_day'] = $this->nodeTitleList($nids, $this->t("Today's:"));
|
||||||
$content['top_day']['#suffix'] = '<br />';
|
$content['top_day']['#suffix'] = '<br />';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->configuration['top_all_num'] > 0) {
|
if ($this->configuration['top_all_num'] > 0) {
|
||||||
$result = statistics_title_list('totalcount', $this->configuration['top_all_num']);
|
$nids = $this->statisticsStorage->fetchAll('totalcount', $this->configuration['top_all_num']);
|
||||||
if ($result) {
|
if ($nids) {
|
||||||
$content['top_all'] = node_title_list($result, $this->t('All time:'));
|
$content['top_all'] = $this->nodeTitleList($nids, $this->t('All time:'));
|
||||||
$content['top_all']['#suffix'] = '<br />';
|
$content['top_all']['#suffix'] = '<br />';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->configuration['top_last_num'] > 0) {
|
if ($this->configuration['top_last_num'] > 0) {
|
||||||
$result = statistics_title_list('timestamp', $this->configuration['top_last_num']);
|
$nids = $this->statisticsStorage->fetchAll('timestamp', $this->configuration['top_last_num']);
|
||||||
$content['top_last'] = node_title_list($result, $this->t('Last viewed:'));
|
$content['top_last'] = $this->nodeTitleList($nids, $this->t('Last viewed:'));
|
||||||
$content['top_last']['#suffix'] = '<br />';
|
$content['top_last']['#suffix'] = '<br />';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the ordered array of node links for build().
|
||||||
|
*
|
||||||
|
* @param int[] $nids
|
||||||
|
* An ordered array of node ids.
|
||||||
|
* @param string $title
|
||||||
|
* The title for the list.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* A render array for the list.
|
||||||
|
*/
|
||||||
|
protected function nodeTitleList(array $nids, $title) {
|
||||||
|
$nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($nids);
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
foreach ($nids as $nid) {
|
||||||
|
$node = $this->entityRepository->getTranslationFromContext($nodes[$nid]);
|
||||||
|
$item = [
|
||||||
|
'#type' => 'link',
|
||||||
|
'#title' => $node->getTitle(),
|
||||||
|
'#url' => $node->urlInfo('canonical'),
|
||||||
|
];
|
||||||
|
$this->renderer->addCacheableDependency($item, $node);
|
||||||
|
$items[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'#theme' => 'item_list__node',
|
||||||
|
'#items' => $items,
|
||||||
|
'#title' => $title,
|
||||||
|
'#cache' => [
|
||||||
|
'tags' => $this->entityTypeManager->getDefinition('node')->getListCacheTags(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\statistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an interface defining Statistics Storage.
|
||||||
|
*
|
||||||
|
* Stores the views per day, total views and timestamp of last view
|
||||||
|
* for entities.
|
||||||
|
*/
|
||||||
|
interface StatisticsStorageInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count a entity view.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* The ID of the entity to count.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if the entity view has been counted.
|
||||||
|
*/
|
||||||
|
public function recordView($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times entities have been viewed.
|
||||||
|
*
|
||||||
|
* @param array $ids
|
||||||
|
* An array of IDs of entities to fetch the views for.
|
||||||
|
*
|
||||||
|
* @return array \Drupal\statistics\StatisticsViewsResult
|
||||||
|
*/
|
||||||
|
public function fetchViews($ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times a single entity has been viewed.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* The ID of the entity to fetch the views for.
|
||||||
|
*
|
||||||
|
* @return \Drupal\statistics\StatisticsViewsResult
|
||||||
|
*/
|
||||||
|
public function fetchView($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of times a entity has been viewed.
|
||||||
|
*
|
||||||
|
* @param string $order
|
||||||
|
* The counter name to order by:
|
||||||
|
* - 'totalcount' The total number of views.
|
||||||
|
* - 'daycount' The number of views today.
|
||||||
|
* - 'timestamp' The unix timestamp of the last view.
|
||||||
|
*
|
||||||
|
* @param int $limit
|
||||||
|
* The number of entity IDs to return.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An ordered array of entity IDs.
|
||||||
|
*/
|
||||||
|
public function fetchAll($order = 'totalcount', $limit = 5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete counts for a specific entity.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* The ID of the entity which views to delete.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if the entity views have been deleted.
|
||||||
|
*/
|
||||||
|
public function deleteViews($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the day counter for all entities once every day.
|
||||||
|
*/
|
||||||
|
public function resetDayCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the highest 'totalcount' value.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* The highest 'totalcount' value.
|
||||||
|
*/
|
||||||
|
public function maxTotalCount();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\statistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object for passing statistic results.
|
||||||
|
*/
|
||||||
|
class StatisticsViewsResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $totalCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $dayCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $timestamp;
|
||||||
|
|
||||||
|
public function __construct($total_count, $day_count, $timestamp) {
|
||||||
|
$this->totalCount = $total_count;
|
||||||
|
$this->dayCount = $day_count;
|
||||||
|
$this->timestamp = $timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of times the entity has been viewed.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTotalCount() {
|
||||||
|
return $this->totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of times the entity has been viewed "today".
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getDayCount() {
|
||||||
|
return $this->dayCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of when the entity was last viewed.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getTimestamp() {
|
||||||
|
return $this->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
namespace Drupal\statistics\Tests;
|
namespace Drupal\statistics\Tests;
|
||||||
|
|
||||||
|
use Drupal\Core\Cache\Cache;
|
||||||
|
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests display of statistics report blocks.
|
* Tests display of statistics report blocks.
|
||||||
*
|
*
|
||||||
|
|
@ -9,6 +12,8 @@ namespace Drupal\statistics\Tests;
|
||||||
*/
|
*/
|
||||||
class StatisticsReportsTest extends StatisticsTestBase {
|
class StatisticsReportsTest extends StatisticsTestBase {
|
||||||
|
|
||||||
|
use AssertPageCacheContextsAndTagsTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the "popular content" block.
|
* Tests the "popular content" block.
|
||||||
*/
|
*/
|
||||||
|
|
@ -30,7 +35,7 @@ class StatisticsReportsTest extends StatisticsTestBase {
|
||||||
$client->post($stats_path, array('headers' => $headers, 'body' => $post));
|
$client->post($stats_path, array('headers' => $headers, 'body' => $post));
|
||||||
|
|
||||||
// Configure and save the block.
|
// Configure and save the block.
|
||||||
$this->drupalPlaceBlock('statistics_popular_block', array(
|
$block = $this->drupalPlaceBlock('statistics_popular_block', array(
|
||||||
'label' => 'Popular content',
|
'label' => 'Popular content',
|
||||||
'top_day_num' => 3,
|
'top_day_num' => 3,
|
||||||
'top_all_num' => 3,
|
'top_all_num' => 3,
|
||||||
|
|
@ -44,9 +49,16 @@ class StatisticsReportsTest extends StatisticsTestBase {
|
||||||
$this->assertText('All time', 'Found the all time popular content.');
|
$this->assertText('All time', 'Found the all time popular content.');
|
||||||
$this->assertText('Last viewed', 'Found the last viewed popular content.');
|
$this->assertText('Last viewed', 'Found the last viewed popular content.');
|
||||||
|
|
||||||
// statistics.module doesn't use node entities, prevent the node language
|
$tags = Cache::mergeTags($node->getCacheTags(), $block->getCacheTags());
|
||||||
// from being added to the options.
|
$tags = Cache::mergeTags($tags, $this->blockingUser->getCacheTags());
|
||||||
$this->assertRaw(\Drupal::l($node->label(), $node->urlInfo('canonical', ['language' => NULL])), 'Found link to visited node.');
|
$tags = Cache::mergeTags($tags, ['block_view', 'config:block_list', 'node_list', 'rendered', 'user_view']);
|
||||||
|
$this->assertCacheTags($tags);
|
||||||
|
$contexts = Cache::mergeContexts($node->getCacheContexts(), $block->getCacheContexts());
|
||||||
|
$contexts = Cache::mergeContexts($contexts, ['url.query_args:_wrapper_format']);
|
||||||
|
$this->assertCacheContexts($contexts);
|
||||||
|
|
||||||
|
// Check if the node link is displayed.
|
||||||
|
$this->assertRaw(\Drupal::l($node->label(), $node->urlInfo('canonical')), 'Found link to visited node.');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,9 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array
|
||||||
if ($context['view_mode'] != 'rss') {
|
if ($context['view_mode'] != 'rss') {
|
||||||
$links['#cache']['contexts'][] = 'user.permissions';
|
$links['#cache']['contexts'][] = 'user.permissions';
|
||||||
if (\Drupal::currentUser()->hasPermission('view post access counter')) {
|
if (\Drupal::currentUser()->hasPermission('view post access counter')) {
|
||||||
$statistics = statistics_get($entity->id());
|
$statistics = \Drupal::service('statistics.storage.node')->fetchView($entity->id());
|
||||||
if ($statistics) {
|
if ($statistics) {
|
||||||
$statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics['totalcount'], '1 view', '@count views');
|
$statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics->getTotalCount(), '1 view', '@count views');
|
||||||
$links['statistics'] = array(
|
$links['statistics'] = array(
|
||||||
'#theme' => 'links__node__statistics',
|
'#theme' => 'links__node__statistics',
|
||||||
'#links' => $statistics_links,
|
'#links' => $statistics_links,
|
||||||
|
|
@ -70,18 +70,10 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array
|
||||||
* Implements hook_cron().
|
* Implements hook_cron().
|
||||||
*/
|
*/
|
||||||
function statistics_cron() {
|
function statistics_cron() {
|
||||||
$statistics_timestamp = \Drupal::state()->get('statistics.day_timestamp') ?: 0;
|
$storage = \Drupal::service('statistics.storage.node');
|
||||||
|
$storage->resetDayCount();
|
||||||
if ((REQUEST_TIME - $statistics_timestamp) >= 86400) {
|
$max_total_count = $storage->maxTotalCount();
|
||||||
// Reset day counts.
|
\Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, $max_total_count));
|
||||||
db_update('node_counter')
|
|
||||||
->fields(array('daycount' => 0))
|
|
||||||
->execute();
|
|
||||||
\Drupal::state()->set('statistics.day_timestamp', REQUEST_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the maximum of node views, for node search ranking.
|
|
||||||
\Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, db_query('SELECT MAX(totalcount) FROM {node_counter}')->fetchField()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -123,26 +115,21 @@ function statistics_title_list($dbfield, $dbrows) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a node's "view statistics".
|
* Retrieves a node's "view statistics".
|
||||||
*
|
*
|
||||||
* @param int $nid
|
* @deprecated in Drupal 8.2.x, will be removed before Drupal 9.0.0.
|
||||||
* The node ID.
|
* Use \Drupal::service('statistics.storage.node')->fetchView($id).
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* An associative array containing:
|
|
||||||
* - totalcount: Integer for the total number of times the node has been
|
|
||||||
* viewed.
|
|
||||||
* - daycount: Integer for the total number of times the node has been viewed
|
|
||||||
* "today". For the daycount to be reset, cron must be enabled.
|
|
||||||
* - timestamp: Integer for the timestamp of when the node was last viewed.
|
|
||||||
*/
|
*/
|
||||||
function statistics_get($nid) {
|
function statistics_get($id) {
|
||||||
|
if ($id > 0) {
|
||||||
if ($nid > 0) {
|
/** @var \Drupal\statistics\StatisticsViewsResult $statistics */
|
||||||
// Retrieve an array with both totalcount and daycount.
|
$statistics = \Drupal::service('statistics.storage.node')->fetchView($id);
|
||||||
return db_query('SELECT totalcount, daycount, timestamp FROM {node_counter} WHERE nid = :nid', array(':nid' => $nid), array('target' => 'replica'))->fetchAssoc();
|
return [
|
||||||
|
'totalcount' => $statistics->getTotalCount(),
|
||||||
|
'daycount' => $statistics->getDayCount(),
|
||||||
|
'timestamp' => $statistics->getTimestamp(),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,9 +138,8 @@ function statistics_get($nid) {
|
||||||
*/
|
*/
|
||||||
function statistics_node_predelete(EntityInterface $node) {
|
function statistics_node_predelete(EntityInterface $node) {
|
||||||
// Clean up statistics table when node is deleted.
|
// Clean up statistics table when node is deleted.
|
||||||
db_delete('node_counter')
|
$id = $node->id();
|
||||||
->condition('nid', $node->id())
|
return \Drupal::service('statistics.storage.node')->deleteViews($id);
|
||||||
->execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@ $autoloader = require_once 'autoload.php';
|
||||||
|
|
||||||
$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod');
|
$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod');
|
||||||
$kernel->boot();
|
$kernel->boot();
|
||||||
|
$container = $kernel->getContainer();
|
||||||
|
|
||||||
$views = $kernel->getContainer()
|
$views = $container
|
||||||
->get('config.factory')
|
->get('config.factory')
|
||||||
->get('statistics.settings')
|
->get('statistics.settings')
|
||||||
->get('count_content_views');
|
->get('count_content_views');
|
||||||
|
|
@ -23,15 +24,7 @@ $views = $kernel->getContainer()
|
||||||
if ($views) {
|
if ($views) {
|
||||||
$nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT);
|
$nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT);
|
||||||
if ($nid) {
|
if ($nid) {
|
||||||
\Drupal::database()->merge('node_counter')
|
$container->get('request_stack')->push(Request::createFromGlobals());
|
||||||
->key('nid', $nid)
|
$container->get('statistics.storage.node')->recordView($nid);
|
||||||
->fields(array(
|
|
||||||
'daycount' => 1,
|
|
||||||
'totalcount' => 1,
|
|
||||||
'timestamp' => REQUEST_TIME,
|
|
||||||
))
|
|
||||||
->expression('daycount', 'daycount + 1')
|
|
||||||
->expression('totalcount', 'totalcount + 1')
|
|
||||||
->execute();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
services:
|
||||||
|
statistics.storage.node:
|
||||||
|
class: Drupal\statistics\NodeStatisticsDatabaseStorage
|
||||||
|
arguments: ['@database', '@state', '@request_stack']
|
||||||
|
tags:
|
||||||
|
- { name: backend_overridable }
|
||||||
Loading…
Reference in New Issue