PluginProbe
Metricool – Social media and site statistics / 2.1.1
Metricool – Social media and site statistics v2.1.1
2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
← All changes | app/Services/Analytics/TrendService.php +125 -0 2.0.0 → 2.1.1 View file →
@@ -1,0 +1,125 @@
1 +<?php
2 +
3 +declare(strict_types=1);
4 +
5 +namespace Metricool\Services\Analytics;
6 +
7 +use Metricool\Vendor\Carbon\Carbon;
8 +use Metricool\Support\Helpers\Collection;
9 +use Metricool\Http\Metricool\Entities\TimelineStatistics;
10 +
11 +class TrendService
12 +{
13 + public const TREND_UP = 'up';
14 + public const TREND_DOWN = 'down';
15 + public const TREND_STABLE = 'stable';
16 +
17 + /**
18 + * Method returns the trend for the given statistic module based on the
19 + * given filters. To be able to calculate the trend the filters need
20 + * at least a start and an end date in Ymd format. Otherwise, a
21 + * 'stable' trend is returned
22 + *
23 + * @param array $filters Optional filters to override the filters used on
24 + * the TimelineStatistics instance. Must contain 'start' and 'end' keys
25 + * in Ymd format.
26 + */
27 + public function getTrend(TimelineStatistics $statistic, Collection $currentStatistics, array $filters = []): string
28 + {
29 + $cacheName = get_class($statistic) . ':' . $statistic->getMetric() . '#' . md5(json_encode($filters));
30 + $cacheValue = wp_cache_get($cacheName, 'metricool', false, $found);
31 + if ($found && is_string($cacheValue)) {
32 + return $cacheValue;
33 + }
34 +
35 + try {
36 + $filters = $this->getCurrentPeriodFilters($filters, $statistic);
37 + $previousStatistics = $statistic->filter(
38 + $this->getPreviousPeriodFilters($filters)
39 + )->get();
40 + } catch (\Throwable $e) {
41 + return self::TREND_STABLE;
42 + }
43 +
44 + $trend = $this->calculateTrendFromPeriods($currentStatistics, $previousStatistics);
45 +
46 + wp_cache_set($cacheName, $trend, 'metricool', (5 * MINUTE_IN_SECONDS));
47 + return $trend;
48 + }
49 +
50 + /**
51 + * Method returns filters for the previous period based on the given
52 + * filters. A period is defined as the difference between start and
53 + * end date.
54 + *
55 + * @param array $filters Must contain 'start' and 'end' keys and should
56 + * reflect the current period to calculate the previous period from.
57 + *
58 + * @throws \InvalidArgumentException When start or end filters are missing
59 + */
60 + public function getPreviousPeriodFilters(array $filters): array
61 + {
62 + if (empty($filters) || empty($filters['start']) || empty($filters['end'])) {
63 + throw new \InvalidArgumentException("Filters 'start' and 'end' are required to get the previous period");
64 + }
65 +
66 + $start = Carbon::createFromFormat('Ymd', $filters['start']);
67 + $end = Carbon::createFromFormat('Ymd', $filters['end']);
68 + $diffInDays = $start->diffInDays($end);
69 +
70 + // Previous end is one day before current start
71 + $previousEnd = $start->copy()->subDay();
72 + $previousStart = $previousEnd->copy()->subDays($diffInDays);
73 +
74 + return [
75 + 'start' => $previousStart->format('Ymd'),
76 + 'end' => $previousEnd->format('Ymd'),
77 + ];
78 + }
79 +
80 + /**
81 + * Method is used to calculate the trend based on the sums of the "amount"
82 + * key from the two given periods.
83 + *
84 + * @param Collection $currentPeriod Statistics for the current period
85 + * @param Collection $previousPeriod Statistics for the previous period
86 + *
87 + * @return string One of the TREND_* constants
88 + */
89 + private function calculateTrendFromPeriods(Collection $currentPeriod, Collection $previousPeriod): string
90 + {
91 + $statisticSumCurrentPeriod = $currentPeriod->sum('amount');
92 + $statisticSumPreviousPeriod = $previousPeriod->sum('amount');
93 +
94 + if ($statisticSumCurrentPeriod > $statisticSumPreviousPeriod) {
95 + return self::TREND_UP;
96 + }
97 +
98 + if ($statisticSumCurrentPeriod < $statisticSumPreviousPeriod) {
99 + return self::TREND_DOWN;
100 + }
101 +
102 + return self::TREND_STABLE;
103 + }
104 +
105 + /**
106 + * Returns the used 'start' and 'end' filters for the given statistic. Uses
107 + * the given filters first, and if they are missing, uses the filters from
108 + * the used statistic instance.
109 + *
110 + * @throws \InvalidArgumentException When start or end filters are missing
111 + * even when using the statistic's filters.
112 + */
113 + private function getCurrentPeriodFilters(array $filters, TimelineStatistics $statistic): array
114 + {
115 + if (empty($filters['start']) || empty($filters['end'])) {
116 + $filters = $statistic->getFilters();
117 + }
118 +
119 + if (empty($filters) || empty($filters['start']) || empty($filters['end'])) {
120 + throw new \InvalidArgumentException("Filters 'start' and 'end' are required to process statistic filters");
121 + }
122 +
123 + return $filters;
124 + }
125 +}