Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
CounterService
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
8 / 8
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 recordView
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 fetchViews
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 fetchView
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fetchAll
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 resetDayCount
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 deleteViews
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 maxTotalCount
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Drupal\visitors\Service;
4
5use Drupal\Component\Datetime\TimeInterface;
6use Drupal\Core\Database\Connection;
7use Drupal\Core\State\StateInterface;
8use Drupal\visitors\StatisticsViewsResult;
9use Drupal\visitors\VisitorsCounterInterface;
10
11/**
12 * Counts entity views.
13 */
14class CounterService implements VisitorsCounterInterface {
15
16  /**
17   * The number of seconds in one day.
18   */
19  const ONE_DAY = 86400;
20
21  /**
22   * The entities viewed.
23   *
24   * @var array
25   */
26  protected static $entities = [];
27
28  /**
29   * The database connection.
30   *
31   * @var \Drupal\Core\Database\Connection
32   */
33  protected $database;
34
35  /**
36   * The date service.
37   *
38   * @var \Drupal\Component\Datetime\TimeInterface
39   */
40  protected $time;
41
42  /**
43   * The state service.
44   *
45   * @var \Drupal\Core\State\StateInterface
46   */
47  protected $state;
48
49  /**
50   * Constructs the counter service.
51   *
52   * @param \Drupal\Core\Database\Connection $connection
53   *   The database connection for the node view storage.
54   * @param \Drupal\Component\Datetime\TimeInterface $time
55   *   The date service.
56   * @param \Drupal\Core\State\StateInterface $state
57   *   The state service.
58   */
59  public function __construct(Connection $connection, TimeInterface $time, StateInterface $state) {
60    $this->database = $connection;
61    $this->time = $time;
62    $this->state = $state;
63  }
64
65  /**
66   * {@inheritdoc}
67   */
68  public function recordView(string $type, int $id) {
69    return (bool) $this->database
70      ->merge('visitors_counter')
71      ->key('entity_type', $type)
72      ->key('entity_id', $id)
73      ->fields([
74        'today' => 1,
75        'total' => 1,
76        'timestamp' => $this->time->getRequestTime(),
77      ])
78      ->expression('today', '[today] + 1')
79      ->expression('total', '[total] + 1')
80      ->execute();
81  }
82
83  /**
84   * {@inheritdoc}
85   */
86  public function fetchViews(string $type, array $ids): array {
87    $views = $this->database
88      ->select('visitors_counter', 'vc')
89      ->fields('vc', ['total', 'today', 'timestamp'])
90      ->condition('entity_type', $type)
91      ->condition('entity_id', $ids, 'IN')
92      ->execute()
93      ->fetchAll();
94    foreach ($views as $id => $view) {
95      $views[$id] = new StatisticsViewsResult($view->total, $view->today, $view->timestamp);
96    }
97    return $views;
98  }
99
100  /**
101   * {@inheritdoc}
102   */
103  public function fetchView(string $type, int $id) {
104    $views = $this->fetchViews($type, [$id]);
105    return reset($views);
106  }
107
108  /**
109   * {@inheritdoc}
110   */
111  public function fetchAll(string $type, string $order = 'total', int $limit = 5) {
112    assert(in_array($order, ['total', 'today', 'timestamp']), "Invalid order argument.");
113
114    return $this->database
115      ->select('visitors_counter', 'vc')
116      ->fields('vc', ['entity_id'])
117      ->condition('entity_type', $type)
118      ->orderBy($order, 'DESC')
119      ->range(0, $limit)
120      ->execute()
121      ->fetchCol();
122  }
123
124  /**
125   * {@inheritdoc}
126   */
127  public function resetDayCount() {
128    $counter_timestamp = $this->state->get('visitors.count_timestamp', 0);
129    $now = $this->time->getRequestTime();
130    if (($now - $counter_timestamp) >= self::ONE_DAY) {
131      $this->state->set('visitors.count_timestamp', $now);
132      $this->database->update('visitors_counter')
133        ->fields(['today' => 0])
134        ->execute();
135    }
136  }
137
138  /**
139   * {@inheritdoc}
140   */
141  public function deleteViews(string $type, int $id) {
142    return (bool) $this->database
143      ->delete('visitors_counter')
144      ->condition('entity_type', $type)
145      ->condition('entity_id', $id)
146      ->execute();
147  }
148
149  /**
150   * {@inheritdoc}
151   */
152  public function maxTotalCount(string $type) {
153    $query = $this->database->select('visitors_counter', 'vc');
154    $query->addExpression('MAX([total])');
155    $query->condition('entity_type', $type);
156    $max_total_count = (int) $query->execute()->fetchField();
157    return $max_total_count;
158  }
159
160}