PluginProbe
Stripe Payment Forms by WP Simple Pay – Accept Credit Card Payments + Subscriptions with Stripe / trunk
Stripe Payment Forms by WP Simple Pay – Accept Credit Card Payments + Subscriptions with Stripe vtrunk
4.17.3 trunk 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 4.10.0 4.11.1 4.12.2 4.14.1 4.14.2 4.14.3 4.15.0 4.16.0 All 59 releases
stripe / src / Report / Chart / PeriodOverPeriodChartTrait.php

PeriodOverPeriodChartTrait.php in Stripe Payment Forms by WP Simple Pay – Accept Credit Card Payments + Subscriptions with Stripe trunk, at src/Report/Chart/PeriodOverPeriodChartTrait.php

490 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Report: Period over Period Chart
4 *
5 * @package SimplePay
6 * @subpackage Core
7 * @copyright Copyright (c) 2023, Sandhills Development, LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 4.6.7
10 */
11
12 namespace SimplePay\Core\Report\Chart;
13
14 use DateInterval;
15 use DatePeriod;
16 use DateTimeImmutable;
17
18 /**
19 * PeriodOverPeriodTrait trait.
20 *
21 * @template TStart of \DateTimeImmutable
22 * @template TEnd of \DateTimeImmutable
23 * @template _DatePeriod of \DatePeriod<TStart, TEnd, null>
24 *
25 * @since 4.6.7
26 */
27 trait PeriodOverPeriodChartTrait {
28
29 use ChartTrait;
30
31 /**
32 * Returns the period over period chart's date periods and information.
33 *
34 * @since 4.6.7
35 *
36 * @param \SimplePay\Core\Report\DateRange $range The date range to use.
37 * @param string $interval The interval to use.
38 * @return array<string, _DatePeriod|int|string>
39 */
40 protected function get_chart_period_over_period_date_periods( $range, $interval ) {
41 $time = 'H' === $interval ? 'T' : '';
42
43 // Create a DatePeriod for the current period.
44 $current_period = new DatePeriod(
45 $range->start,
46 new DateInterval( "P{$time}1{$interval}" ),
47 $range->end
48 );
49
50 // Determine how many intervals are in the current period.
51 $count = iterator_count( $current_period );
52
53 /** @var \DateTimeImmutable $current_period_start */
54 $current_period_start = $current_period->getStartDate();
55
56 /** @var \DateTimeImmutable $current_period_end */
57 $current_period_end = $current_period->getEndDate();
58
59 // Determine the start date of the previous period by subtracting the
60 // number of intervals in the current period. The end date of the
61 // previous period is not needed.
62 /** @var \DateTime $previous_period_start */
63 $previous_period_start = $current_period_start->sub(
64 new DateInterval( 'P' . $time . $count . $interval )
65 );
66
67 /** @var \DateTime $previous_period_end */
68 $previous_period_end = $previous_period_start->add(
69 new DateInterval( 'P' . $time . $count . $interval )
70 );
71
72 // Create a DatePeriod for the previous period.
73 $previous_period = new DatePeriod(
74 $previous_period_start,
75 new DateInterval( "P{$time}1{$interval}" ),
76 $previous_period_end
77 );
78
79 // Create a DatePeriod that covers the entire range of the current and
80 // previous periods.
81 $full_period = new DatePeriod(
82 $previous_period_start,
83 new DateInterval( "P{$time}1{$interval}" ),
84 $current_period_end
85 );
86
87 return array(
88 'interval' => $interval,
89 'interval_count' => $count,
90 'full_period' => $full_period,
91 'current_period' => $current_period,
92 'previous_period' => $previous_period,
93 );
94 }
95
96 /**
97 * Returns datasets with formatted data points for a given period and currency.
98 *
99 * @since 4.6.7
100 *
101 * @param array<string, _DatePeriod|string|int> $dates The date periods and information.
102 * @param array<string, \stdClass> $data The results from the database query, keyed
103 * by date (formatted based on the interval).
104 * @param callable $value_formatter A function that formats a datapoint value.
105 * @return array<int, array<int, array<string, mixed>>>
106 */
107 private function get_chart_period_over_period_datasets( $dates, $data, $value_formatter ) {
108 /** @var int $count */
109 $count = $dates['interval_count'];
110
111 /** @var string $interval */
112 $interval = $dates['interval'];
113
114 // Determine how many intervals are in the full period.
115 /** @var _DatePeriod $full_period */
116 $full_period = $dates['full_period'];
117 $iterable_items = iterator_to_array( $full_period );
118
119 // Create two datasets: one for the current period and one for the
120 // previous period. Each dataset will have the same number of data points.
121 $datasets = array(
122 array_slice( $iterable_items, 0, $count ),
123 array_slice( $iterable_items, $count, $count ),
124 );
125
126 $formatted_datasets = array();
127
128 // Format each dataset.
129 foreach ( $datasets as $dataset ) {
130 array_push(
131 $formatted_datasets,
132 $this->format_chart_period_over_period_dataset(
133 $dataset,
134 $datasets,
135 $interval,
136 $data,
137 $value_formatter
138 )
139 );
140 }
141
142 return $formatted_datasets;
143 }
144
145 /**
146 * Formats a "Period over Period" dataset.
147 *
148 * @since 4.6.7
149 *
150 * @param array<int, \DateTimeInterface> $dataset The dataset to format.
151 * @param array<array<int, \DateTimeInterface>> $datasets The full list of datasets.
152 * @param string $interval The date interval used for the dataset.
153 * @param array<string, \stdClass> $data The results from the database query.
154 * @param callable $value_formatter A function that formats a datapoint value.
155 * @return array<int, array<string, mixed>>
156 */
157 protected function format_chart_period_over_period_dataset(
158 $dataset,
159 $datasets,
160 $interval,
161 $data,
162 $value_formatter
163 ) {
164 $datapoints = array();
165
166 foreach ( $dataset as $x => $datapoint ) {
167 $datapoints[] = $this->format_chart_period_over_period_datapoint(
168 $datapoint,
169 $x,
170 $datasets,
171 $interval,
172 $data,
173 $value_formatter
174 );
175 }
176
177 return $datapoints;
178 }
179
180 /**
181 * Formats a datapoint in a dataset.
182 *
183 * @since 4.6.7
184 *
185 * @param \DateTimeInterface $datapoint The datapoint to format.
186 * @param int $x Iteration key, used for the x-axis of each dataset.
187 * Each dataset is the same length so this value allows
188 * for the x-axis to be the same for each dataset.
189 * @param array<array<int, \DateTimeInterface>> $datasets The full list of datasets.
190 * @param string $interval The date interval used for the dataset.
191 * @param array<string, \stdClass> $data The results from the database query, keyed
192 * by date (formatted based on the interval).
193 * @param callable $value_formatter A function that formats a datapoint value.
194 * @return array<string, mixed>
195 */
196 public function format_chart_period_over_period_datapoint(
197 $datapoint,
198 $x,
199 $datasets,
200 $interval,
201 $data,
202 $value_formatter
203 ) {
204 // Generate X axis data.
205 $x_axis = $this->get_chart_period_over_period_datapoint_x_axis(
206 $datapoint,
207 $x,
208 $datasets,
209 $interval
210 );
211
212 // Pull the total from the SQL query results. The results are grouped/keyed
213 // by the date formatted based on the interval.
214 $date_format = $this->get_php_date_format( $interval );
215 $date = $datapoint->format( $date_format );
216
217 $y = isset( $data[ $date ] ) ? (float) $data[ $date ]->value : 0;
218
219 return array(
220 // Simplified x/y values for plotting the chart.
221 'x' => $x_axis['x'],
222 'y' => $y,
223
224 // Formatted values for display.
225 'label' => $x_axis['label'],
226 'value' => call_user_func( $value_formatter, $y ),
227 );
228 }
229
230 /**
231 * Generates X-axis data for a datapoint in a dataset.
232 *
233 * A "true" x-axis value is determined by using the iteration key ($x) and
234 * pulling the corresponding value from the current period dataset. This
235 * value remains static for both datasets, so they overlap when displayed.
236 *
237 * A "label" value is determined by using the iteration key ($x) and pulling
238 * the corresponding value from the current period dataset.
239 *
240 * @since 4.6.7
241 *
242 * @param \DateTimeInterface $datapoint The datapoint to format.
243 * @param int $x Iteration key, used for the x-axis of each dataset.
244 * Each dataset is the same length so this value allows
245 * for the x-axis to be the same for each dataset.
246 * @param array<array<int, \DateTimeInterface>> $datasets The full list of datasets.
247 * @param string $interval The date interval used for the dataset.
248 * @return array<string, string>
249 */
250 private function get_chart_period_over_period_datapoint_x_axis(
251 $datapoint,
252 $x,
253 $datasets,
254 $interval
255 ) {
256 /** @var int $dynamic_period_datapoint_timestamp */
257 $dynamic_period_datapoint_timestamp = $datapoint->getTimestamp();
258
259 /** @var int $fixed_period_datapoint_timestamp */
260 $fixed_period_datapoint_timestamp = $datasets[1][ $x ]->getTimestamp();
261
262 /** @var string $date_i18n_format */
263 $date_i18n_format = get_option( 'date_format', 'F jS' );
264
265 /** @var string $time_i18n_format */
266 $time_i18n_format = get_option( 'time_format', 'g:i a' );
267
268 switch ( $interval ) {
269 // Hourly.
270 case 'H':
271 $x = date_i18n(
272 $date_i18n_format . ' ' . $time_i18n_format,
273 $fixed_period_datapoint_timestamp
274 );
275
276 $label = date_i18n(
277 $date_i18n_format . ' ' . $time_i18n_format,
278 $dynamic_period_datapoint_timestamp
279 );
280 break;
281 // Weekly.
282 case 'W':
283 $days = array(
284 0 => 'sunday',
285 1 => 'monday',
286 2 => 'tuesday',
287 3 => 'wednesday',
288 4 => 'thursday',
289 5 => 'friday',
290 6 => 'saturday',
291 );
292
293 /** @var string|bool $start_of_week */
294 $start_of_week = get_option( 'start_of_week', '0' );
295 $first_day_of_week_no = absint( (string) $start_of_week );
296 $last_day_of_week_no = 0 === $first_day_of_week_no
297 ? 6
298 : $first_day_of_week_no - 1;
299
300 $first_day_of_week = $days[ $first_day_of_week_no ];
301 $last_day_of_week = $days[ $last_day_of_week_no ];
302
303 /** @var int $fixed_period_start_timestamp */
304 $fixed_period_start_timestamp = strtotime(
305 "$first_day_of_week -1 week",
306 $fixed_period_datapoint_timestamp
307 );
308
309 /** @var int $fixed_period_end_timestamp */
310 $fixed_period_end_timestamp = strtotime(
311 "next $last_day_of_week",
312 $fixed_period_datapoint_timestamp
313 );
314
315 $x = sprintf(
316 '%s - %s',
317 gmdate(
318 $date_i18n_format,
319 $fixed_period_start_timestamp
320 ),
321 gmdate(
322 $date_i18n_format,
323 $fixed_period_end_timestamp
324 )
325 );
326
327 /** @var int $dynamic_period_start_timestamp */
328 $dynamic_period_start_timestamp = strtotime(
329 "$first_day_of_week -1 week",
330 $dynamic_period_datapoint_timestamp
331 );
332
333 /** @var int $dynamic_period_start_timestamp */
334 $dynamic_period_end_timestamp = strtotime(
335 "next $last_day_of_week",
336 $dynamic_period_datapoint_timestamp
337 );
338
339 $label = sprintf(
340 '%s - %s',
341 gmdate(
342 $date_i18n_format,
343 $dynamic_period_start_timestamp
344 ),
345 gmdate(
346 $date_i18n_format,
347 $dynamic_period_end_timestamp
348 )
349 );
350
351 break;
352 case 'M':
353 /** @var string $fixed_period_date */
354 $fixed_period_date = gmdate(
355 '01-m-Y',
356 $fixed_period_datapoint_timestamp
357 );
358
359 /** @var int $fixed_period_timestamp */
360 $fixed_period_timestamp = strtotime( $fixed_period_date );
361
362 /** @var string $dynamic_period_date */
363 $dynamic_period_date = gmdate(
364 '01-m-Y',
365 $dynamic_period_datapoint_timestamp
366 );
367
368 /** @var int $dynamic_period_timestamp */
369 $dynamic_period_timestamp = strtotime( $dynamic_period_date );
370
371 $x = gmdate( 'F Y', $fixed_period_timestamp );
372 $label = gmdate( 'F Y', $dynamic_period_timestamp );
373
374 break;
375 case 'Y':
376 /** @var string $fixed_period_date */
377 $fixed_period_date = gmdate(
378 '31-12-Y',
379 $fixed_period_datapoint_timestamp
380 );
381
382 /** @var int $fixed_period_timestamp */
383 $fixed_period_timestamp = strtotime( $fixed_period_date );
384
385 /** @var string $dynamic_period_date */
386 $dynamic_period_date = gmdate(
387 '31-12-Y',
388 $dynamic_period_datapoint_timestamp
389 );
390
391 /** @var int $dynamic_period_timestamp */
392 $dynamic_period_timestamp = strtotime( $dynamic_period_date );
393
394 $x = gmdate( 'Y', $fixed_period_timestamp );
395 $label = gmdate( 'Y', $dynamic_period_timestamp );
396
397 break;
398 default:
399 $x = gmdate(
400 $date_i18n_format,
401 $fixed_period_datapoint_timestamp
402 );
403
404 $label = gmdate(
405 $date_i18n_format,
406 $dynamic_period_datapoint_timestamp
407 );
408 }
409
410 return array(
411 'x' => $x,
412 'label' => $label,
413 );
414 }
415
416 /**
417 * Returns a value representing the "total" for the current period.
418 *
419 * @since 4.6.7
420 *
421 * @param array<int, array<int, array<string, mixed>>> $datasets The formatted datasets to use for the chart.
422 * @return int The total for the current period.
423 */
424 private function get_chart_period_over_period_current_period_total( $datasets ) {
425 if ( ! isset( $datasets[1] ) || ! is_array( $datasets[1] ) ) {
426 return 0;
427 }
428
429 return $this->get_chart_formatted_dataset_total( $datasets[1] );
430 }
431
432 /**
433 * Returns a value representing the "total" for the previous period.
434 *
435 * @since 4.6.7
436 *
437 * @param array<int, array<int, array<string, mixed>>> $datasets The formatted datasets to use for the chart.
438 * @return int The total for the previous period.
439 */
440 private function get_chart_period_over_period_previous_period_total( $datasets ) {
441 if ( ! isset( $datasets[0] ) || ! is_array( $datasets[0] ) ) {
442 return 0;
443 }
444
445 return $this->get_chart_formatted_dataset_total( $datasets[0] );
446 }
447
448 /**
449 * Calculates the delta/change between the current and previous periods.
450 *
451 * @since 4.6.7
452 *
453 * @param array<int, array<int, array<string, mixed>>> $datasets The formatted datasets to use for the chart.
454 * @return float
455 */
456 private function get_chart_period_over_period_delta( $datasets ) {
457 $curr_total = $this->get_chart_period_over_period_current_period_total(
458 $datasets
459 );
460
461 $prev_total = $this->get_chart_period_over_period_previous_period_total(
462 $datasets
463 );
464
465 return $this->get_delta( $curr_total, $prev_total );
466 }
467
468 /**
469 * Returns the primary color for the current period.
470 *
471 * @since 4.6.7
472 *
473 * @return array<int> RGB color values.
474 */
475 protected function get_chart_period_over_period_current_period_primary_color() {
476 return $this->get_user_color_scheme_pref_primary_color();
477 }
478
479 /**
480 * Returns the primary color for the previous period.
481 *
482 * @since 4.6.7
483 *
484 * @return array<int> RGB color values.
485 */
486 protected function get_chart_period_over_period_previous_period_primary_color() {
487 return array( 220, 220, 220 );
488 }
489 }
490