get_date_range()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 6
nop 1
dl 0
loc 22
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * Helper for the date based controllers.
4
 *
5
 * @package GetPaid
6
 * @subpackage REST API
7
 * @since   2.0.0
8
 */
9
10
defined( 'ABSPATH' ) || exit;
11
12
/**
13
 * GetPaid REST date based controller class.
14
 *
15
 * @package Invoicing
16
 */
17
class GetPaid_REST_Date_Based_Controller extends GetPaid_REST_Controller {
18
19
	/**
20
	 * Group response items by day or month.
21
	 *
22
	 * @var string
23
	 */
24
	public $groupby = 'day';
25
26
	/**
27
	 * Returns an array with arguments to request the previous report.
28
	 *
29
	 * @var array
30
	 */
31
	public $previous_range = array();
32
33
	/**
34
	 * The period interval.
35
	 *
36
	 * @var int
37
	 */
38
	public $interval;
39
40
	/**
41
	 * Retrieves the before and after dates.
42
	 *
43
	 * @param WP_REST_Request $request Request object.
44
	 * @return array The appropriate date range.
45
	 */
46
	public function get_date_range( $request ) {
47
48
		// Check if the period is x_days.
49
		if ( preg_match( '/^(\d+)_days$/', $request['period'], $matches ) ) {
0 ignored issues
show
Bug introduced by
It seems like $request['period'] can also be of type null; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

49
		if ( preg_match( '/^(\d+)_days$/', /** @scrutinizer ignore-type */ $request['period'], $matches ) ) {
Loading history...
50
			$date_range = $this->get_x_days_date_range( absint( $matches[1] ) );
51
		} elseif ( is_callable( array( $this, 'get_' . $request['period'] . '_date_range' ) ) ) {
52
			$date_range = call_user_func( array( $this, 'get_' . $request['period'] . '_date_range' ), $request );
53
		} else {
54
			$request['period'] = '7_days';
55
			$date_range        = $this->get_x_days_date_range();
56
		}
57
58
		// 3 months max for day view.
59
		$before = strtotime( $date_range['before'] );
60
		$after  = strtotime( $date_range['after'] );
61
		if ( floor( ( $before - $after ) / MONTH_IN_SECONDS ) > 2 ) {
62
			$this->groupby = 'month';
63
		}
64
65
		$this->prepare_interval( $date_range );
66
67
		return $date_range;
68
69
	}
70
71
	/**
72
	 * Groups by month or days.
73
	 *
74
	 * @param array $range Date range.
75
	 * @return array The appropriate date range.
76
	 */
77
	public function prepare_interval( $range ) {
78
79
		$before = strtotime( $range['before'] );
80
		$after  = strtotime( $range['after'] );
81
		if ( 'day' === $this->groupby ) {
82
			$difference     = max( DAY_IN_SECONDS, ( DAY_IN_SECONDS + $before - $after ) ); // Prevent division by 0;
83
			$this->interval = absint( ceil( max( 1, $difference / DAY_IN_SECONDS ) ) );
84
			return;
85
		}
86
87
		$this->interval = 0;
88
		$min_date       = strtotime( gmdate( 'Y-m-01', $after ) );
89
90
		while ( $min_date <= $before ) {
91
			$this->interval ++;
92
			$min_date = strtotime( '+1 MONTH', $min_date );
93
		}
94
95
		$this->interval = max( 1, $this->interval );
96
97
	}
98
99
	/**
100
	 * Retrieves a custom date range.
101
	 *
102
	 * @param WP_REST_Request $request Request object.
103
	 * @return array The appropriate date range.
104
	 */
105
	public function get_custom_date_range( $request ) {
106
107
		$after  = max( strtotime( '-20 years' ), strtotime( sanitize_text_field( $request['after'] ) ) );
108
		$before = gmdate( 'Y-m-d' );
109
		
110
		if ( ! empty( $request['before'] ) ) {
111
			$before  = min( strtotime( $before ), strtotime( sanitize_text_field( $request['before'] ) ) );
112
		}
113
114
		// Set the previous date range.
115
		$difference           = $before - $after;
116
		$this->previous_range = array(
117
			'period' => 'custom',
118
			'before' => gmdate( 'Y-m-d', $before - $difference - DAY_IN_SECONDS ),
119
			'after'  => gmdate( 'Y-m-d', $after - $difference - DAY_IN_SECONDS ),
120
		);
121
122
		// Generate the report.
123
		return array(
124
			'before' => gmdate( 'Y-m-d', $before ),
0 ignored issues
show
Bug introduced by
It seems like $before can also be of type string; however, parameter $timestamp of gmdate() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
			'before' => gmdate( 'Y-m-d', /** @scrutinizer ignore-type */ $before ),
Loading history...
125
			'after'  => gmdate( 'Y-m-d', $after ),
126
		);
127
128
	}
129
130
	/**
131
	 * Retrieves todays date range.
132
	 *
133
	 * @return array The appropriate date range.
134
	 */
135
	public function get_today_date_range() {
136
137
		// Set the previous date range.
138
		$this->previous_range = array(
139
			'period' => 'yesterday',
140
		);
141
142
		// Generate the report.
143
		return array(
144
			'before' => gmdate( 'Y-m-d' ),
145
			'after'  => gmdate( 'Y-m-d' ),
146
		);
147
148
	}
149
150
	/**
151
	 * Retrieves yesterdays date range.
152
	 *
153
	 * @return array The appropriate date range.
154
	 */
155
	public function get_yesterday_date_range() {
156
157
		// Set the previous date range.
158
		$this->previous_range = array(
159
			'period' => 'custom',
160
			'before' => gmdate( 'Y-m-d', strtotime( '-2 days' ) ),
161
			'after'  => gmdate( 'Y-m-d', strtotime( '-2 days' ) ),
162
		);
163
164
		// Generate the report.
165
		return array(
166
			'before' => gmdate( 'Y-m-d', strtotime( '-1 day' ) ),
167
			'after'  => gmdate( 'Y-m-d', strtotime( '-1 day' ) ),
168
		);
169
170
	}
171
172
	/**
173
	 * Retrieves this week's date range.
174
	 *
175
	 * @return array The appropriate date range.
176
	 */
177
	public function get_week_date_range() {
178
179
		// Set the previous date range.
180
		$this->previous_range = array(
181
			'period' => 'last_week',
182
		);
183
184
		// Generate the report.
185
		$week_starts = absint( get_option( 'start_of_week' ) );
186
		return array(
187
			'before' => gmdate( 'Y-m-d' ),
188
			'after'  => gmdate( 'Y-m-d', strtotime( 'next Sunday -' . ( 7 - $week_starts ) . ' days' ) ),
189
		);
190
	}
191
192
	/**
193
	 * Retrieves last week's date range.
194
	 *
195
	 * @return array The appropriate date range.
196
	 */
197
	public function get_last_week_date_range() {
198
199
		$week_starts = absint( get_option( 'start_of_week' ) );
200
		$week_starts = strtotime( 'last Sunday -' . ( 7 - $week_starts ) . ' days' );
201
		$date_range  = array(
202
			'before' => gmdate( 'Y-m-d', $week_starts + 6 * DAY_IN_SECONDS ),
203
			'after'  => gmdate( 'Y-m-d', $week_starts ),
204
		);
205
206
		// Set the previous date range.
207
		$week_starts          = $week_starts - 7 * DAY_IN_SECONDS;
208
		$this->previous_range = array(
209
			'period' => 'custom',
210
			'before' => gmdate( 'Y-m-d', $week_starts + 6 * DAY_IN_SECONDS ),
211
			'after'  => gmdate( 'Y-m-d', $week_starts ),
212
		);
213
214
		// Generate the report.
215
		return $date_range;
216
	}
217
218
	/**
219
	 * Retrieves last x days date range.
220
	 *
221
	 * @return array The appropriate date range.
222
	 */
223
	public function get_x_days_date_range( $days = 7 ) {
224
225
		$days--;
226
227
		$date_range  = array(
228
			'before' => gmdate( 'Y-m-d' ),
229
			'after'  => gmdate( 'Y-m-d', strtotime( "-$days days" ) ),
230
		);
231
232
		$days++;
233
234
		// Set the previous date range.
235
		$this->previous_range = array(
236
			'period' => 'custom',
237
			'before' => gmdate( 'Y-m-d', strtotime( $date_range['before'] ) - $days * DAY_IN_SECONDS ),
238
			'after'  => gmdate( 'Y-m-d', strtotime( $date_range['after'] ) - $days * DAY_IN_SECONDS ),
239
		);
240
241
		// Generate the report.
242
		return $date_range;
243
	}
244
245
	/**
246
	 * Retrieves this month date range.
247
	 *
248
	 * @return array The appropriate date range.
249
	 */
250
	public function get_month_date_range() {
251
252
		// Set the previous date range.
253
		$this->previous_range = array(
254
			'period' => 'last_month',
255
		);
256
257
		// Generate the report.
258
		return array(
259
			'after'  => gmdate( 'Y-m-01' ),
260
			'before' => gmdate( 'Y-m-t' ),
261
		);
262
263
	}
264
265
	/**
266
	 * Retrieves last month's date range.
267
	 *
268
	 * @return array The appropriate date range.
269
	 */
270
	public function get_last_month_date_range() {
271
272
		// Set the previous date range.
273
		$this->previous_range = array(
274
			'period' => 'custom',
275
			'after'  => gmdate( 'Y-m-01', strtotime( '-2 months' ) ),
276
			'before' => gmdate( 'Y-m-t', strtotime( '-2 months' ) ),
277
		);
278
279
		// Generate the report.
280
		return array(
281
			'after'  => gmdate( 'Y-m-01', strtotime( 'last month' ) ),
282
			'before' => gmdate( 'Y-m-t', strtotime( 'last month' ) ),
283
		);
284
285
	}
286
287
	/**
288
	 * Retrieves this quarter date range.
289
	 *
290
	 * @return array The available quarters.
291
	 */
292
	public function get_quarters() {
293
294
		$year      = (int) gmdate( 'Y' );
295
		$last_year = (int) $year - 1;
296
		return array(
297
298
			// Third quarter of previous year: July 1st to September 30th
299
			array(
300
				'before' => "{$last_year}-09-30",
301
				'after'  => "{$last_year}-07-01",
302
			),
303
304
			// Last quarter of previous year: October 1st to December 31st
305
			array(
306
				'before' => "{$last_year}-12-31",
307
        		'after'  => "{$last_year}-10-01",
308
			),
309
310
			// First quarter: January 1st to March 31st
311
			array(
312
				'before' => "{$year}-03-31",
313
				'after'  => "{$year}-01-01",
314
			),
315
316
			// Second quarter: April 1st to June 30th
317
			array(
318
				'before' => "{$year}-06-30",
319
				'after'  => "{$year}-04-01",
320
			),
321
322
			// Third quarter: July 1st to September 30th
323
			array(
324
				'before' => "{$year}-09-30",
325
				'after'  => "{$year}-07-01",
326
			),
327
328
			// Fourth quarter: October 1st to December 31st
329
			array(
330
				'before' => "{$year}-12-31",
331
				'after'  => "{$year}-10-01",
332
			),
333
		);
334
	}
335
336
	/**
337
	 * Retrieves the current quater.
338
	 *
339
	 * @return int The current quarter.
340
	 */
341
	public function get_quarter() {
342
343
		$month    = (int) gmdate( 'n' );
344
		$quarters = array( 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 );
345
		return $quarters[ $month - 1 ];
346
347
	}
348
349
	/**
350
	 * Retrieves this quarter date range.
351
	 *
352
	 * @return array The appropriate date range.
353
	 */
354
	public function get_quarter_date_range() {
355
356
		// Set the previous date range.
357
		$this->previous_range = array(
358
			'period' => 'last_quarter',
359
		);
360
361
		// Generate the report.
362
		$quarter  = $this->get_quarter();
363
		$quarters = $this->get_quarters();
364
		return $quarters[ $quarter + 1 ];
365
366
	}
367
368
	/**
369
	 * Retrieves last quarter's date range.
370
	 *
371
	 * @return array The appropriate date range.
372
	 */
373
	public function get_last_quarter_date_range() {
374
375
		$quarters = $this->get_quarters();
376
		$quarter  = $this->get_quarter();
377
378
		// Set the previous date range.
379
		$this->previous_range = array_merge(
380
			$quarters[ $quarter - 1 ],
381
			array( 'period' => 'custom' )
382
		);
383
384
		// Generate the report.
385
		return $quarters[ $quarter ];
386
387
	}
388
389
	/**
390
	 * Retrieves this year date range.
391
	 *
392
	 * @return array The appropriate date range.
393
	 */
394
	public function get_year_date_range() {
395
396
		// Set the previous date range.
397
		$this->previous_range = array(
398
			'period' => 'last_year',
399
		);
400
401
		// Generate the report.
402
		return array(
403
			'after'  => gmdate( 'Y-01-01' ),
404
			'before' => gmdate( 'Y-12-31' ),
405
		);
406
407
	}
408
409
	/**
410
	 * Retrieves last year date range.
411
	 *
412
	 * @return array The appropriate date range.
413
	 */
414
	public function get_last_year_date_range() {
415
416
		// Set the previous date range.
417
		$this->previous_range = array(
418
			'period' => 'custom',
419
			'after'  => gmdate( 'Y-01-01', strtotime( '-2 years' ) ),
420
			'before' => gmdate( 'Y-12-31', strtotime( '-2 years' ) ),
421
		);
422
423
		// Generate the report.
424
		return array(
425
			'after'  => gmdate( 'Y-01-01', strtotime( 'last year' ) ),
426
			'before' => gmdate( 'Y-12-31', strtotime( 'last year' ) ),
427
		);
428
429
	}
430
431
	/**
432
	 * Prepare a the request date for SQL usage.
433
	 *
434
	 * @param WP_REST_Request $request Request object.
435
	 * @param string $date_field The date field.
436
	 * @return string The appropriate SQL.
437
	 */
438
	public function get_date_range_sql( $request, $date_field ) {
439
		global $wpdb;
440
441
		$sql = '1=1';
442
		$range = $this->get_date_range( $request );
443
444
		if ( ! empty( $range['after'] ) ) {
445
			$sql .= ' AND ' . $wpdb->prepare(
446
				"$date_field >= %s",
447
				$range['after']
448
			);
449
		}
450
451
		if ( ! empty( $range['before'] ) ) {
452
			$sql .= ' AND ' . $wpdb->prepare(
453
				"$date_field <= %s",
454
				$range['before']
455
			);
456
		}
457
458
		return $sql;
459
460
	}
461
462
	/**
463
	 * Prepares a group by query.
464
	 *
465
	 * @param string $date_field The date field.
466
	 * @return string The appropriate SQL.
467
	 */
468
	public function get_group_by_sql( $date_field ) {
469
470
		if ( 'day' === $this->groupby ) {
471
			return "YEAR($date_field), MONTH($date_field), DAY($date_field)";
472
		}
473
474
		return "YEAR($date_field), MONTH($date_field)";
475
	}
476
477
	/**
478
	 * Get the query params for collections.
479
	 *
480
	 * @return array
481
	 */
482
	public function get_collection_params() {
483
		return array(
484
			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
485
			'period'  => array(
486
				'description'       => __( 'Limit to results of a specific period.', 'invoicing' ),
487
				'type'              => 'string',
488
				'enum'              => array( 'custom', 'today', 'yesterday', 'week', 'last_week', '7_days', '30_days', '60_days', '90_days', '180_days', 'month', 'last_month', 'quarter', 'last_quarter', 'year', 'last_year', 'quarter', 'last_quarter' ),
489
				'validate_callback' => 'rest_validate_request_arg',
490
				'sanitize_callback' => 'sanitize_text_field',
491
				'default'           => '7_days',
492
			),
493
			'after'   => array(
494
				/* translators: %s: date format */
495
				'description'       => sprintf( __( 'Limit to results after a specific date, the date needs to be in the %s format.', 'invoicing' ), 'YYYY-MM-DD' ),
496
				'type'              => 'string',
497
				'validate_callback' => 'rest_validate_request_arg',
498
				'sanitize_callback' => 'sanitize_text_field',
499
				'default'           => gmdate( 'Y-m-d', strtotime( '-7 days' ) ),
500
			),
501
			'before'  => array(
502
				/* translators: %s: date format */
503
				'description'       => sprintf( __( 'Limit to results before a specific date, the date needs to be in the %s format.', 'invoicing' ), 'YYYY-MM-DD' ),
504
				'type'              => 'string',
505
				'validate_callback' => 'rest_validate_request_arg',
506
				'sanitize_callback' => 'sanitize_text_field',
507
				'default'           => gmdate( 'Y-m-d' ),
508
			),
509
		);
510
	}
511
}
512