PluginProbe
Metricool – Social media and site statistics / trunk
Metricool – Social media and site statistics vtrunk
2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
metricool / app / Http / Endpoints / DistributionEndpoint.php

DistributionEndpoint.php in Metricool – Social media and site statistics trunk, at app/Http/Endpoints/DistributionEndpoint.php

145 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Metricool\Http\Endpoints;
6
7 use Metricool\Http\Endpoints\Responses\DistributionResponse;
8 use Metricool\Http\Endpoints\Responses\Statistics\CountriesResponse;
9 use Metricool\Http\Endpoints\Responses\Statistics\RefererResponse;
10 use Metricool\Http\Metricool\DTOs\DistributionDTO;
11 use Metricool\Http\Metricool\MetricoolApi;
12 use Metricool\Interfaces\SingleEndpointInterface;
13 use Metricool\Services\DashboardService;
14 use Metricool\Support\Helpers\Collection;
15 use Metricool\Support\Validation\Validator;
16 use Metricool\Traits\HasAllowlistControl;
17 use Metricool\Traits\HasRestAccess;
18
19 class DistributionEndpoint implements SingleEndpointInterface
20 {
21 use HasRestAccess;
22 use HasAllowlistControl;
23
24 public const ROUTE = 'distribution';
25
26 private const METRICS_RESPONSE_MAPPER = [
27 'countries' => CountriesResponse::class,
28 'referers' => RefererResponse::class,
29 ];
30
31 public MetricoolApi $metricoolApi;
32 public DashboardService $dashboard;
33
34 public function __construct(MetricoolApi $metricoolApi, DashboardService $dashboard)
35 {
36 $this->metricoolApi = $metricoolApi;
37 $this->dashboard = $dashboard;
38 }
39
40 /**
41 * @inheritDoc
42 */
43 public function registerRoute(): string
44 {
45 return self::ROUTE . '/(?P<metric>[^/]+)';
46 }
47
48 /**
49 * Only enable this endpoint when onboarding is completed
50 */
51 public function enabled(): bool
52 {
53 return $this->dashboard->isOnboardingCompleted();
54 }
55
56 /**
57 * @inheritDoc
58 */
59 public function registerArguments(): array
60 {
61 return [
62 'methods' => \WP_REST_Server::READABLE,
63 'callback' => [$this, 'callback'],
64 'middleware' => ['metricool:auth', 'metricool:blog_id'],
65 ];
66 }
67
68 /**
69 * Method will dynamically request the requested statistic. If the metric
70 * is filterable and filters are provided, it will apply them before
71 * retrieving the data.
72 *
73 * GET /wp-json/metricool/v1/distribution/countries?filters[start]=20250618&filters[end]=20250718&filters[country]=nl
74 */
75 public function callback(\WP_REST_Request $request): \WP_REST_Response
76 {
77 $validated = Validator::validate($request->get_params(), [
78 'metric' => 'required|string|in:countries,referers',
79 'filters' => 'array',
80 ]);
81
82 try {
83 $response = $this->buildResponse($validated);
84 } catch (\Exception $e) {
85 return $this->sendHttpErrorResponse(__('Failed to load Analytics data', 'metricool'), $e->getMessage(), $e->getCode());
86 }
87
88 return $this->sendHttpResponse($response);
89 }
90
91 /**
92 * Build the specific Analytics response for the endpoint. This is mainly
93 * used in the plugin Dashboard to reflect non-realtime statistics.
94 * Building it server side prevents client-side complexity.
95 *
96 * @throws \Exception
97 */
98 private function buildResponse(array $validated): array
99 {
100 $metric = $validated['metric'];
101 $requestFilters = $validated['filters'] ?? [];
102
103 // Load the statistics
104 $statistics = $this->getStatisticsForMetric($metric, $requestFilters);
105
106 // Find the associated response object for the metric
107 $response = $this->createResponseObjectFromMetric($metric, $statistics);
108
109 return $response->body();
110 }
111
112 /**
113 * Loads the results from the Metricool API
114 * @return Collection|DistributionDTO[]
115 */
116 protected function getStatisticsForMetric(string $metric, array $filters): Collection
117 {
118 $statisticsModule = $this->metricoolApi->statistics();
119
120 // Load the results
121 $metricModule = $statisticsModule->$metric();
122 if (!empty($filters)) {
123 $metricModule->filter($filters);
124 }
125
126 return $metricModule->get();
127 }
128
129 /**
130 * Find the response that matches the requested metric or throw an exception.
131 * Each metric has its own specific serialisation of the results and chartData
132 * @param Collection|DistributionDTO[] $statistics
133 */
134 protected function createResponseObjectFromMetric(string $metric, Collection $statistics): DistributionResponse
135 {
136 if (!array_key_exists($metric, self::METRICS_RESPONSE_MAPPER)) {
137 throw new \InvalidArgumentException(esc_html("Metric $metric is not accepted by this endpoint"));
138 }
139
140 $response = self::METRICS_RESPONSE_MAPPER[$metric];
141
142 return new $response($statistics);
143 }
144 }
145