Completed
Pull Request — master (#664)
by Devin
19:01
created

Give_Batch_Customers_Export   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 230
rs 8.2769
wmc 41
lcom 1
cbo 3

5 Methods

Rating   Name   Duplication   Size   Complexity  
A set_properties() 0 12 4
F csv_cols() 0 38 10
B get_data() 0 58 6
B get_percentage_complete() 0 23 4
F set_donor_data() 0 42 17

How to fix   Complexity   

Complex Class

Complex classes like Give_Batch_Customers_Export often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Give_Batch_Customers_Export, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 24 and the first side effect is on line 16.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Batch Customers Export Class
4
 *
5
 * This class handles customer export
6
 *
7
 * @package     Give
8
 * @subpackage  Admin/Reports
9
 * @copyright   Copyright (c) 2016, WordImpress
10
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
11
 * @since       1.5
12
 */
13
14
// Exit if accessed directly
15
if ( ! defined( 'ABSPATH' ) ) {
16
	exit;
17
}
18
19
/**
20
 * Give_Batch_Customers_Export Class
21
 *
22
 * @since 1.5
23
 */
24
class Give_Batch_Customers_Export extends Give_Batch_Export {
25
26
	/**
27
	 * Our export type. Used for export-type specific filters/actions
28
	 *
29
	 * @var string
30
	 * @since 1.5
31
	 */
32
	public $export_type = 'customers';
33
34
	/**
35
	 * Form submission data
36
	 *
37
	 * @var array
38
	 * @since 1.5
39
	 */
40
	private $data = array();
41
42
	/**
43
	 * Set the properties specific to the Customers export
44
	 *
45
	 * @since 1.5
46
	 *
47
	 * @param array $request The Form Data passed into the batch processing
48
	 */
49
	public function set_properties( $request ) {
50
51
		//Set data from form submission
52
		if ( isset( $_POST['form'] ) ) {
53
			parse_str( $_POST['form'], $this->data );
54
		}
55
56
		$this->form = $this->data['forms'];
57
58
		$this->price_id = ! empty( $request['give_price_option'] ) && 0 !== $request['give_price_option'] ? absint( $request['give_price_option'] ) : null;
59
60
	}
61
62
	/**
63
	 * Set the CSV columns
64
	 *
65
	 * @access public
66
	 * @since 1.5
67
	 * @return array $cols All the columns
68
	 */
69
	public function csv_cols() {
70
71
		$cols = array();
72
73
		$columns = isset( $this->data['give_export_option'] ) ? $this->data['give_export_option'] : array();
74
75
		if ( empty( $columns ) ) {
76
			return false;
77
		}
78
		if ( ! empty( $columns['full_name'] ) ) {
79
			$cols['full_name'] = __( 'Full Name', 'give' );
80
		}
81
		if ( ! empty( $columns['email'] ) ) {
82
			$cols['email'] = __( 'Email Address', 'give' );
83
		}
84
		if ( ! empty( $columns['address'] ) ) {
85
			$cols['address_line1']   = __( 'Address', 'give' );
86
			$cols['address_line2']   = __( 'Address (Line 2)', 'give' );
87
			$cols['address_city']    = __( 'City', 'give' );
88
			$cols['address_state']   = __( 'State', 'give' );
89
			$cols['address_zip']     = __( 'Zip', 'give' );
90
			$cols['address_country'] = __( 'Country', 'give' );
91
		}
92
		if ( ! empty( $columns['userid'] ) ) {
93
			$cols['userid'] = __( 'User ID', 'give' );
94
		}
95
		if ( ! empty( $columns['date_first_donated'] ) ) {
96
			$cols['date_first_donated'] = __( 'First Donation Date', 'give' );
97
		}
98
		if ( ! empty( $columns['donations'] ) ) {
99
			$cols['donations'] = __( 'Number of Donations', 'give' );
100
		}
101
		if ( ! empty( $columns['donation_sum'] ) ) {
102
			$cols['donation_sum'] = __( 'Sum of Donations', 'give' );
103
		}
104
105
		return $cols;
106
	}
107
108
	/**
109
	 * Get the Export Data
110
	 *
111
	 * @access public
112
	 * @since  1.0
113
	 * @global object $give_logs Give Logs Object
114
	 * @return array $data The data for the CSV file
115
	 */
116
	public function get_data() {
117
118
		$data = array();
119
120
		$i = 0;
121
122
		if ( ! empty( $this->form ) ) {
123
124
			// Export donors of a specific product
125
			global $give_logs;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
126
127
			$args = array(
128
				'post_parent'    => absint( $this->form ),
129
				'log_type'       => 'sale',
130
				'posts_per_page' => 30,
131
				'paged'          => $this->step
132
			);
133
134
			//Check for price option
135
			if ( null !== $this->price_id ) {
136
				$args['meta_query'] = array(
137
					array(
138
						'key'   => '_give_log_price_id',
139
						'value' => (int) $this->price_id
140
					)
141
				);
142
			}
143
144
			$logs = $give_logs->get_connected_logs( $args );
145
146
			if ( $logs ) {
147
				foreach ( $logs as $log ) {
148
					$payment_id = get_post_meta( $log->ID, '_give_log_payment_id', true );
149
					$payment    = new Give_Payment( $payment_id );
150
					$donor      = Give()->customers->get_customer_by( 'id', $payment->customer_id );
0 ignored issues
show
Documentation introduced by
The property $customer_id is declared protected in Give_Payment. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
151
					$data[]     = $this->set_donor_data( $i, $data, $donor );
152
					$i ++;
153
				}
154
			}
155
156
		} else {
157
158
			// Export all customers
159
			$offset = 30 * ( $this->step - 1 );
160
			$donors = Give()->customers->get_customers( array( 'number' => 30, 'offset' => $offset ) );
161
162
			foreach ( $donors as $donor ) {
163
164
				$data[] = $this->set_donor_data( $i, $data, $donor );
165
				$i ++;
166
			}
167
		}
168
169
		$data = apply_filters( 'give_export_get_data', $data );
170
		$data = apply_filters( 'give_export_get_data_' . $this->export_type, $data );
171
172
		return $data;
173
	}
174
175
	/**
176
	 * Return the calculated completion percentage
177
	 *
178
	 * @since 1.5
179
	 * @return int
180
	 */
181
	public function get_percentage_complete() {
182
183
		$percentage = 0;
184
185
		// We can't count the number when getting them for a specific form
186
		if ( empty( $this->form ) ) {
187
188
			$total = Give()->customers->count();
189
190
			if ( $total > 0 ) {
191
192
				$percentage = ( ( 30 * $this->step ) / $total ) * 100;
193
194
			}
195
196
		}
197
198
		if ( $percentage > 100 ) {
199
			$percentage = 100;
200
		}
201
202
		return $percentage;
203
	}
204
205
	/**
206
	 * Set Donor Data
207
	 *
208
	 * @param $donor
209
	 */
210
	private function set_donor_data( $i, $data, $donor ) {
211
212
		$columns = $this->csv_cols();
213
214
		//Set address variable
215
		$address = '';
216
		if ( isset( $donor->user_id ) && $donor->user_id > 0 ) {
217
			$address = give_get_donor_address( $donor->user_id );
218
		}
219
220
		//Set columns
221
		if ( ! empty( $columns['full_name'] ) ) {
222
			$data[ $i ]['full_name'] = $donor->name;
223
		}
224
		if ( ! empty( $columns['email'] ) ) {
225
			$data[ $i ]['email'] = $donor->email;
226
		}
227
		if ( ! empty( $columns['address_line1'] ) ) {
228
229
			$data[ $i ]['address_line1']   = isset( $address['line1'] ) ? $address['line1'] : '';
230
			$data[ $i ]['address_line2']   = isset( $address['line2'] ) ? $address['line2'] : '';
231
			$data[ $i ]['address_city']    = isset( $address['city'] ) ? $address['city'] : '';
232
			$data[ $i ]['address_state']   = isset( $address['state'] ) ? $address['state'] : '';
233
			$data[ $i ]['address_zip']     = isset( $address['zip'] ) ? $address['zip'] : '';
234
			$data[ $i ]['address_country'] = isset( $address['country'] ) ? $address['country'] : '';
235
		}
236
		if ( ! empty( $columns['userid'] ) ) {
237
			$data[ $i ]['userid'] = ! empty( $donor->user_id ) ? $donor->user_id : '';
238
		}
239
		if ( ! empty( $columns['date_first_donated'] ) ) {
240
			$data[ $i ]['date_first_donated'] = date_i18n( get_option( 'date_format' ), strtotime( $donor->date_created ) );
241
		}
242
		if ( ! empty( $columns['donations'] ) ) {
243
			$data[ $i ]['donations'] = $donor->purchase_count;
244
		}
245
		if ( ! empty( $columns['donation_sum'] ) ) {
246
			$data[ $i ]['donation_sum'] = give_format_amount( $donor->purchase_value );
247
		}
248
249
		return $data[ $i ];
250
251
	}
252
253
}
254