Completed
Push — master ( e4c91a...311c54 )
by Mike
22:41
created

WC_Report_Sales_By_Category::get_main_chart()   C

Complexity

Conditions 12
Paths 56

Size

Total Lines 162
Code Lines 57

Duplication

Lines 9
Ratio 5.56 %

Importance

Changes 0
Metric Value
cc 12
eloc 57
c 0
b 0
f 0
nc 56
nop 0
dl 9
loc 162
rs 5.034

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * WC_Report_Sales_By_Category
9
 *
10
 * @author      WooThemes
11
 * @category    Admin
12
 * @package     WooCommerce/Admin/Reports
13
 * @version     2.1.0
14
 */
15
class WC_Report_Sales_By_Category extends WC_Admin_Report {
16
17
	/**
18
	 * Chart colours.
19
	 *
20
	 * @var array
21
	 */
22
	public $chart_colours         = array();
23
24
	/**
25
	 * Categories ids.
26
	 *
27
	 * @var array
28
	 */
29
	public $show_categories       = array();
30
31
	/**
32
	 * Item sales.
33
	 *
34
	 * @var array
35
	 */
36
	private $item_sales           = array();
37
38
	/**
39
	 * Item sales and times.
40
	 *
41
	 * @var array
42
	 */
43
	private $item_sales_and_times = array();
44
45
	/**
46
	 * Constructor.
47
	 */
48
	public function __construct() {
49
		if ( isset( $_GET['show_categories'] ) ) {
50
			$this->show_categories = is_array( $_GET['show_categories'] ) ? array_map( 'absint', $_GET['show_categories'] ) : array( absint( $_GET['show_categories'] ) );
51
		}
52
	}
53
54
	/**
55
	 * Get all product ids in a category (and its children).
56
	 *
57
	 * @param  int $category_id
58
	 * @return array
59
	 */
60
	public function get_products_in_category( $category_id ) {
61
		$term_ids    = get_term_children( $category_id, 'product_cat' );
62
		$term_ids[]  = $category_id;
63
		$product_ids = get_objects_in_term( $term_ids, 'product_cat' );
64
65
		return array_unique( apply_filters( 'woocommerce_report_sales_by_category_get_products_in_category', $product_ids, $category_id ) );
66
	}
67
68
	/**
69
	 * Get the legend for the main chart sidebar.
70
	 *
71
	 * @return array
72
	 */
73
	public function get_chart_legend() {
74
75
		if ( empty( $this->show_categories ) ) {
76
			return array();
77
		}
78
79
		$legend = array();
80
		$index  = 0;
81
82
		foreach ( $this->show_categories as $category ) {
83
84
			$category    = get_term( $category, 'product_cat' );
85
			$total       = 0;
86
			$product_ids = $this->get_products_in_category( $category->term_id );
87
88
			foreach ( $product_ids as $id ) {
89
90
				if ( isset( $this->item_sales[ $id ] ) ) {
91
					$total += $this->item_sales[ $id ];
92
				}
93
			}
94
95
			$legend[] = array(
96
				'title'            => sprintf( __( '%1$s sales in %2$s', 'woocommerce' ), '<strong>' . wc_price( $total ) . '</strong>', $category->name ),
97
				'color'            => isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0],
98
				'highlight_series' => $index,
99
			);
100
101
			$index++;
102
		}
103
104
		return $legend;
105
	}
106
107
	/**
108
	 * Output the report.
109
	 */
110
	public function output_report() {
111
112
		$ranges = array(
113
			'year'         => __( 'Year', 'woocommerce' ),
114
			'last_month'   => __( 'Last Month', 'woocommerce' ),
115
			'month'        => __( 'This Month', 'woocommerce' ),
116
			'7day'         => __( 'Last 7 Days', 'woocommerce' ),
117
		);
118
119
		$this->chart_colours = array( '#3498db', '#34495e', '#1abc9c', '#2ecc71', '#f1c40f', '#e67e22', '#e74c3c', '#2980b9', '#8e44ad', '#2c3e50', '#16a085', '#27ae60', '#f39c12', '#d35400', '#c0392b' );
120
121
		$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : '7day';
122
123
		if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
124
			$current_range = '7day';
125
		}
126
127
		$this->calculate_current_range( $current_range );
128
129
		// Get item sales data
130
		if ( ! empty( $this->show_categories ) ) {
131
			$order_items = $this->get_order_report_data( array(
132
				'data' => array(
133
					'_product_id' => array(
134
						'type'            => 'order_item_meta',
135
						'order_item_type' => 'line_item',
136
						'function'        => '',
137
						'name'            => 'product_id',
138
					),
139
					'_line_total' => array(
140
						'type'            => 'order_item_meta',
141
						'order_item_type' => 'line_item',
142
						'function'        => 'SUM',
143
						'name'            => 'order_item_amount',
144
					),
145
					'post_date' => array(
146
						'type'     => 'post_data',
147
						'function' => '',
148
						'name'     => 'post_date',
149
					),
150
				),
151
				'group_by'     => 'ID, product_id, post_date',
152
				'query_type'   => 'get_results',
153
				'filter_range' => true,
154
			) );
155
156
			$this->item_sales           = array();
157
			$this->item_sales_and_times = array();
158
159
			if ( is_array( $order_items ) ) {
160
161
				foreach ( $order_items as $order_item ) {
162
163 View Code Duplication
					switch ( $this->chart_groupby ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
						case 'day' :
165
							$time = strtotime( date( 'Ymd', strtotime( $order_item->post_date ) ) ) * 1000;
166
						break;
167
						case 'month' :
168
						default :
169
							$time = strtotime( date( 'Ym', strtotime( $order_item->post_date ) ) . '01' ) * 1000;
170
						break;
171
					}
172
173
					$this->item_sales_and_times[ $time ][ $order_item->product_id ] = isset( $this->item_sales_and_times[ $time ][ $order_item->product_id ] ) ? $this->item_sales_and_times[ $time ][ $order_item->product_id ] + $order_item->order_item_amount : $order_item->order_item_amount;
174
175
					$this->item_sales[ $order_item->product_id ] = isset( $this->item_sales[ $order_item->product_id ] ) ? $this->item_sales[ $order_item->product_id ] + $order_item->order_item_amount : $order_item->order_item_amount;
176
				}
177
			}
178
		}
179
180
		include( WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php' );
181
	}
182
183
	/**
184
	 * Get chart widgets.
185
	 *
186
	 * @return array
187
	 */
188
	public function get_chart_widgets() {
189
190
		return array(
191
			array(
192
				'title'    => __( 'Categories', 'woocommerce' ),
193
				'callback' => array( $this, 'category_widget' ),
194
			),
195
		);
196
	}
197
198
	/**
199
	 * Output category widget.
200
	 */
201
	public function category_widget() {
202
203
		$categories = get_terms( 'product_cat', array( 'orderby' => 'name' ) );
204
		?>
205
		<form method="GET">
206
			<div>
207
				<select multiple="multiple" data-placeholder="<?php esc_attr_e( 'Select categories&hellip;', 'woocommerce' ); ?>" class="wc-enhanced-select" id="show_categories" name="show_categories[]" style="width: 205px;">
208
					<?php
209
						$r = array();
210
						$r['pad_counts'] 	= 1;
211
						$r['hierarchical'] 	= 1;
212
						$r['hide_empty'] 	= 1;
213
						$r['value']			= 'id';
214
						$r['selected'] 		= $this->show_categories;
215
216
						include_once( WC()->plugin_path() . '/includes/walkers/class-product-cat-dropdown-walker.php' );
217
218
						echo wc_walk_category_dropdown_tree( $categories, 0, $r );
219
					?>
220
				</select>
221
				<a href="#" class="select_none"><?php _e( 'None', 'woocommerce' ); ?></a>
222
				<a href="#" class="select_all"><?php _e( 'All', 'woocommerce' ); ?></a>
223
				<input type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>" />
224
				<input type="hidden" name="range" value="<?php if ( ! empty( $_GET['range'] ) ) echo esc_attr( $_GET['range'] ) ?>" />
225
				<input type="hidden" name="start_date" value="<?php if ( ! empty( $_GET['start_date'] ) ) echo esc_attr( $_GET['start_date'] ) ?>" />
226
				<input type="hidden" name="end_date" value="<?php if ( ! empty( $_GET['end_date'] ) ) echo esc_attr( $_GET['end_date'] ) ?>" />
227
				<input type="hidden" name="page" value="<?php if ( ! empty( $_GET['page'] ) ) echo esc_attr( $_GET['page'] ) ?>" />
228
				<input type="hidden" name="tab" value="<?php if ( ! empty( $_GET['tab'] ) ) echo esc_attr( $_GET['tab'] ) ?>" />
229
				<input type="hidden" name="report" value="<?php if ( ! empty( $_GET['report'] ) ) echo esc_attr( $_GET['report'] ) ?>" />
230
			</div>
231
			<script type="text/javascript">
232
				jQuery(function(){
233
					// Select all/none
234
					jQuery( '.chart-widget' ).on( 'click', '.select_all', function() {
235
						jQuery(this).closest( 'div' ).find( 'select option' ).attr( 'selected', 'selected' );
236
						jQuery(this).closest( 'div' ).find('select').change();
237
						return false;
238
					});
239
240
					jQuery( '.chart-widget').on( 'click', '.select_none', function() {
241
						jQuery(this).closest( 'div' ).find( 'select option' ).removeAttr( 'selected' );
242
						jQuery(this).closest( 'div' ).find('select').change();
243
						return false;
244
					});
245
				});
246
			</script>
247
		</form>
248
		<?php
249
	}
250
251
	/**
252
	 * Output an export link.
253
	 */
254 View Code Duplication
	public function get_export_button() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
255
256
		$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : '7day';
257
		?>
258
		<a
259
			href="#"
260
			download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo date_i18n( 'Y-m-d', current_time( 'timestamp' ) ); ?>.csv"
261
			class="export_csv"
262
			data-export="chart"
263
			data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
264
			data-groupby="<?php echo $this->chart_groupby; ?>"
265
		>
266
			<?php _e( 'Export CSV', 'woocommerce' ); ?>
267
		</a>
268
		<?php
269
	}
270
271
	/**
272
	 * Get the main chart.
273
	 *
274
	 * @return string
275
	 */
276
	public function get_main_chart() {
277
		global $wp_locale;
278
279
		if ( empty( $this->show_categories ) ) {
280
			?>
281
			<div class="chart-container">
282
				<p class="chart-prompt"><?php _e( '&larr; Choose a category to view stats', 'woocommerce' ); ?></p>
283
			</div>
284
			<?php
285
		} else {
286
			$chart_data = array();
287
			$index      = 0;
288
289
			foreach ( $this->show_categories as $category ) {
290
291
				$category            = get_term( $category, 'product_cat' );
292
				$product_ids         = $this->get_products_in_category( $category->term_id );
293
				$category_chart_data = array();
294
295
				for ( $i = 0; $i <= $this->chart_interval; $i ++ ) {
296
297
					$interval_total = 0;
298
299 View Code Duplication
					switch ( $this->chart_groupby ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
						case 'day' :
301
							$time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $this->start_date ) ) ) * 1000;
302
						break;
303
						case 'month' :
304
						default :
305
							$time = strtotime( date( 'Ym', strtotime( "+{$i} MONTH", $this->start_date ) ) . '01' ) * 1000;
306
						break;
307
					}
308
309
					foreach ( $product_ids as $id ) {
310
311
						if ( isset( $this->item_sales_and_times[ $time ][ $id ] ) ) {
312
							$interval_total += $this->item_sales_and_times[ $time ][ $id ];
313
						}
314
					}
315
316
					$category_chart_data[] = array( $time, (float) wc_format_decimal( $interval_total, wc_get_price_decimals() ) );
317
				}
318
319
				$chart_data[ $category->term_id ]['category'] = $category->name;
320
				$chart_data[ $category->term_id ]['data'] = $category_chart_data;
321
322
				$index++;
323
			}
324
			?>
325
			<div class="chart-container">
326
				<div class="chart-placeholder main"></div>
327
			</div>
328
			<script type="text/javascript">
329
				var main_chart;
330
331
				jQuery(function(){
332
					var drawGraph = function( highlight ) {
333
						var series = [
334
							<?php
335
								$index = 0;
336
								foreach ( $chart_data as $data ) {
337
									$color  = isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0];
338
									$width  = $this->barwidth / sizeof( $chart_data );
339
									$offset = ( $width * $index );
340
									$series = $data['data'];
341
									foreach ( $series as $key => $series_data ) {
342
										$series[ $key ][0] = $series_data[0] + $offset;
343
									}
344
									echo '{
345
										label: "' . esc_js( $data['category'] ) . '",
346
										data: jQuery.parseJSON( "' . json_encode( $series ) . '" ),
347
										color: "' . $color . '",
348
										bars: {
349
											fillColor: "' . $color . '",
350
											fill: true,
351
											show: true,
352
											lineWidth: 1,
353
											align: "center",
354
											barWidth: ' . $width * 0.75 . ',
355
											stack: false
356
										},
357
										' . $this->get_currency_tooltip() . ',
358
										enable_tooltip: true,
359
										prepend_label: true
360
									},';
361
									$index++;
362
								}
363
							?>
364
						];
365
366
						if ( highlight !== 'undefined' && series[ highlight ] ) {
367
							highlight_series = series[ highlight ];
368
369
							highlight_series.color = '#9c5d90';
370
371
							if ( highlight_series.bars ) {
372
								highlight_series.bars.fillColor = '#9c5d90';
373
							}
374
375
							if ( highlight_series.lines ) {
376
								highlight_series.lines.lineWidth = 5;
377
							}
378
						}
379
380
						main_chart = jQuery.plot(
381
							jQuery('.chart-placeholder.main'),
382
							series,
383
							{
384
								legend: {
385
									show: false
386
								},
387
								grid: {
388
									color: '#aaa',
389
									borderColor: 'transparent',
390
									borderWidth: 0,
391
									hoverable: true
392
								},
393
								xaxes: [ {
394
									color: '#aaa',
395
									reserveSpace: true,
396
									position: "bottom",
397
									tickColor: 'transparent',
398
									mode: "time",
399
									timeformat: "<?php echo ( $this->chart_groupby == 'day' ) ? '%d %b' : '%b'; ?>",
400
									monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,
401
									tickLength: 1,
402
									minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
403
									tickSize: [1, "<?php echo $this->chart_groupby; ?>"],
404
									font: {
405
										color: "#aaa"
406
									}
407
								} ],
408
								yaxes: [
409
									{
410
										min: 0,
411
										tickDecimals: 2,
412
										color: 'transparent',
413
										font: { color: "#aaa" }
414
									}
415
								],
416
							}
417
						);
418
419
						jQuery('.chart-placeholder').resize();
420
421
					}
422
423
					drawGraph();
424
425
					jQuery('.highlight_series').hover(
426
						function() {
427
							drawGraph( jQuery(this).data('series') );
428
						},
429
						function() {
430
							drawGraph();
431
						}
432
					);
433
				});
434
			</script>
435
			<?php
436
		}
437
	}
438
}
439