Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like RegistrationsReport 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 RegistrationsReport, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class RegistrationsReport extends JobHandlerFile { |
||
29 | |||
30 | /** |
||
31 | * Performs any necessary setup for starting the job. This is also a good |
||
32 | * place to setup the $job_arguments which will be used for subsequent HTTP requests |
||
33 | * when continue_job will be called |
||
34 | * @param JobParameters $job_parameters |
||
35 | * @throws BatchRequestException |
||
36 | * @return JobStepResponse |
||
37 | */ |
||
38 | public function create_job( JobParameters $job_parameters ) { |
||
64 | |||
65 | |||
66 | |||
67 | /** |
||
68 | * Creates teh filename form the event id (or lack thereof) |
||
69 | * @param int $event_id |
||
70 | * @return string |
||
71 | */ |
||
72 | protected function get_filename_from_event( $event_id ) { |
||
73 | View Code Duplication | if( $event_id ){ |
|
|
|||
74 | $event_slug = \EEM_Event::instance()->get_var( array( array( 'EVT_ID' => $event_id ) ), 'EVT_slug' ); |
||
75 | if( ! $event_slug ) { |
||
76 | $event_slug = __( 'unknown', 'event_espresso' ); |
||
77 | } |
||
78 | }else{ |
||
79 | $event_slug = __( 'all', 'event_espresso' ); |
||
80 | } |
||
81 | return sprintf( "registrations-for-%s.csv", $event_slug ); |
||
82 | } |
||
83 | |||
84 | |||
85 | |||
86 | /** |
||
87 | * Performs another step of the job |
||
88 | * @param JobParameters $job_parameters |
||
89 | * @return array{ |
||
90 | * @type string $status |
||
91 | * @type int $records_processed |
||
92 | * @type int $records_to_process |
||
93 | * @type string message |
||
94 | * } and anything more we want to add |
||
95 | * @throws |
||
96 | */ |
||
97 | View Code Duplication | public function continue_job( JobParameters $job_parameters, $batch_size = 50 ) { |
|
98 | $csv_data = $this->get_csv_data_for( $job_parameters->request_datum( 'EVT_ID', '0'), $job_parameters->units_processed(), $batch_size ); |
||
99 | \EE_Registry::instance()->load_helper( 'Export' ); |
||
100 | \EEH_Export::write_data_array_to_csv( $job_parameters->extra_datum( 'filepath' ), $csv_data, false ); |
||
101 | $units_processed = count( $csv_data ); |
||
102 | $job_parameters->mark_processed( $units_processed ); |
||
103 | $extra_response_data = array( |
||
104 | 'file_url' => '' |
||
105 | ); |
||
106 | if( $units_processed < $batch_size ) { |
||
107 | $job_parameters->set_status( JobParameters::status_complete ); |
||
108 | $extra_response_data[ 'file_url' ] = $this->get_url_to_file( $job_parameters->extra_datum( 'filepath' ) ); |
||
109 | } |
||
110 | return new JobStepResponse( |
||
111 | $job_parameters, |
||
112 | sprintf( |
||
113 | __( 'Wrote %1$s rows to report CSV file...', 'event_espresso' ), |
||
114 | count( $csv_data ) ), |
||
115 | $extra_response_data ); |
||
116 | } |
||
117 | |||
118 | function get_csv_data_for( $event_id, $offset, $limit ) { |
||
119 | \EE_Registry::instance()->load_helper( 'Export' ); |
||
120 | $reg_fields_to_include = array( |
||
121 | 'TXN_ID', |
||
122 | 'ATT_ID', |
||
123 | 'REG_ID', |
||
124 | 'REG_date', |
||
125 | 'REG_code', |
||
126 | 'REG_count', |
||
127 | 'REG_final_price' |
||
128 | |||
129 | ); |
||
130 | $att_fields_to_include = array( |
||
131 | 'ATT_fname', |
||
132 | 'ATT_lname', |
||
133 | 'ATT_email', |
||
134 | 'ATT_address', |
||
135 | 'ATT_address2', |
||
136 | 'ATT_city', |
||
137 | 'STA_ID', |
||
138 | 'CNT_ISO', |
||
139 | 'ATT_zip', |
||
140 | 'ATT_phone', |
||
141 | ); |
||
142 | |||
143 | $registrations_csv_ready_array = array(); |
||
144 | $reg_model = \EE_Registry::instance()->load_model('Registration'); |
||
145 | $query_params = apply_filters( |
||
146 | 'FHEE__EE_Export__report_registration_for_event', |
||
147 | array( |
||
148 | array( |
||
149 | 'OR' => array( |
||
150 | //don't include registrations from failed or abandoned transactions... |
||
151 | 'Transaction.STS_ID' => array( 'NOT IN', array( \EEM_Transaction::failed_status_code, \EEM_Transaction::abandoned_status_code ) ), |
||
152 | //unless the registration is approved, in which case include it regardless of transaction status |
||
153 | 'STS_ID' => \EEM_Registration::status_id_approved |
||
154 | ), |
||
155 | 'Ticket.TKT_deleted' => array( 'IN', array( true, false ) ) |
||
156 | ), |
||
157 | 'order_by' => array('Transaction.TXN_ID'=>'asc','REG_count'=>'asc'), |
||
158 | 'force_join' => array( 'Transaction', 'Ticket', 'Attendee' ), |
||
159 | 'limit' => array( $offset, $limit ), |
||
160 | ), |
||
161 | $event_id |
||
162 | ); |
||
163 | View Code Duplication | if( $event_id ){ |
|
164 | $query_params[0]['EVT_ID'] = $event_id; |
||
165 | }else{ |
||
166 | $query_params[ 'force_join' ][] = 'Event'; |
||
167 | } |
||
168 | $registration_rows = $reg_model->get_all_wpdb_results( $query_params ); |
||
169 | //get all questions which relate to someone in this group |
||
170 | $registration_ids = array(); |
||
171 | foreach( $registration_rows as $reg_row ) { |
||
172 | $registration_ids[] = intval( $reg_row[ 'Registration.REG_ID'] ); |
||
173 | } |
||
174 | // EEM_Question::instance()->show_next_x_db_queries(); |
||
175 | if( $event_id ) { |
||
176 | $questions_for_these_regs_rows = \EEM_Question::instance()->get_all_wpdb_results(array(array('Answer.Registration.EVT_ID'=> $event_id ) ) ); |
||
177 | } else { |
||
178 | $questions_for_these_regs_rows = \EEM_Question::instance()->get_all_wpdb_results(array(array('Answer.ANS_ID'=> array( 'IS_NOT_NULL' ) ) ) ); |
||
179 | } |
||
180 | |||
181 | foreach($registration_rows as $reg_row){ |
||
182 | if ( is_array( $reg_row ) ) { |
||
183 | $reg_csv_array = array(); |
||
184 | View Code Duplication | if( ! $event_id ){ |
|
185 | //get the event's name and Id |
||
186 | $reg_csv_array[ __( 'Event', 'event_espresso' ) ] = sprintf( __( '%1$s (%2$s)', 'event_espresso' ), \EEH_Export::prepare_value_from_db_for_display( \EEM_Event::instance(), 'EVT_name', $reg_row[ 'Event_CPT.post_title'] ), $reg_row[ 'Event_CPT.ID' ] ); |
||
187 | } |
||
188 | $is_primary_reg = $reg_row[ 'Registration.REG_count' ] == '1' ? true : false; |
||
189 | /*@var $reg_row EE_Registration */ |
||
190 | foreach($reg_fields_to_include as $field_name){ |
||
191 | $field = $reg_model->field_settings_for($field_name); |
||
192 | if($field_name == 'REG_final_price'){ |
||
193 | $value = \EEH_Export::prepare_value_from_db_for_display( $reg_model, $field_name, $reg_row[ 'Registration.REG_final_price'], 'localized_float' ); |
||
194 | }elseif( $field_name == 'REG_count' ){ |
||
195 | $value = sprintf( __( '%s of %s', 'event_espresso' ), \EEH_Export::prepare_value_from_db_for_display( $reg_model, 'REG_count', $reg_row['Registration.REG_count'] ), \EEH_Export::prepare_value_from_db_for_display( $reg_model, 'REG_group_size', $reg_row['Registration.REG_group_size' ] ) ); |
||
196 | }elseif( $field_name == 'REG_date' ) { |
||
197 | $value = \EEH_Export::prepare_value_from_db_for_display( $reg_model, $field_name, $reg_row[ 'Registration.REG_date'], 'no_html' ); |
||
198 | }else{ |
||
199 | $value = \EEH_Export::prepare_value_from_db_for_display( $reg_model, $field_name, $reg_row[ $field->get_qualified_column() ] ); |
||
200 | } |
||
201 | $reg_csv_array[\EEH_Export::get_column_name_for_field($field)] = $value; |
||
202 | if($field_name == 'REG_final_price'){ |
||
203 | //add a column named Currency after the final price |
||
204 | $reg_csv_array[__("Currency", "event_espresso")] = \EE_Config::instance()->currency->code; |
||
205 | } |
||
206 | } |
||
207 | //get pretty status |
||
208 | $stati = \EEM_Status::instance()->localized_status( array( |
||
209 | $reg_row[ 'Registration.STS_ID' ] => __( 'unknown', 'event_espresso' ), |
||
210 | $reg_row[ 'Transaction.STS_ID' ] => __( 'unknown', 'event_espresso' ) ), |
||
211 | FALSE, |
||
212 | 'sentence' ); |
||
213 | $reg_csv_array[__("Registration Status", 'event_espresso')] = $stati[ $reg_row[ 'Registration.STS_ID' ] ]; |
||
214 | //get pretty transaction status |
||
215 | $reg_csv_array[__("Transaction Status", 'event_espresso')] = $stati[ $reg_row[ 'Transaction.STS_ID' ] ]; |
||
216 | $reg_csv_array[ __( 'Transaction Amount Due', 'event_espresso' ) ] = $is_primary_reg ? \EEH_Export::prepare_value_from_db_for_display( \EEM_Transaction::instance(), 'TXN_total', $reg_row[ 'Transaction.TXN_total' ], 'localized_float' ) : '0.00'; |
||
217 | $reg_csv_array[ __( 'Amount Paid', 'event_espresso' )] = $is_primary_reg ? \EEH_Export::prepare_value_from_db_for_display( \EEM_Transaction::instance(), 'TXN_paid', $reg_row[ 'Transaction.TXN_paid' ], 'localized_float' ) : '0.00'; |
||
218 | $payment_methods = array(); |
||
219 | $gateway_txn_ids_etc = array(); |
||
220 | $payment_times = array(); |
||
221 | View Code Duplication | if( $is_primary_reg && $reg_row[ 'Transaction.TXN_ID' ] ){ |
|
222 | $payments_info = \EEM_Payment::instance()->get_all_wpdb_results( |
||
223 | array( |
||
224 | array( |
||
225 | 'TXN_ID' => $reg_row[ 'Transaction.TXN_ID' ], |
||
226 | 'STS_ID' => \EEM_Payment::status_id_approved |
||
227 | ), |
||
228 | 'force_join' => array( 'Payment_Method' ), |
||
229 | |||
230 | ), |
||
231 | ARRAY_A, |
||
232 | 'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time' ); |
||
233 | |||
234 | foreach( $payments_info as $payment_method_and_gateway_txn_id ){ |
||
235 | $payment_methods[] = isset( $payment_method_and_gateway_txn_id[ 'name' ] ) ? $payment_method_and_gateway_txn_id[ 'name' ] : __( 'Unknown', 'event_espresso' ); |
||
236 | $gateway_txn_ids_etc[] = isset( $payment_method_and_gateway_txn_id[ 'gateway_txn_id' ] ) ? $payment_method_and_gateway_txn_id[ 'gateway_txn_id' ] : ''; |
||
237 | $payment_times[] = isset( $payment_method_and_gateway_txn_id[ 'payment_time' ] ) ? $payment_method_and_gateway_txn_id[ 'payment_time' ] : ''; |
||
238 | } |
||
239 | |||
240 | } |
||
241 | $reg_csv_array[ __( 'Payment Date(s)', 'event_espresso' ) ] = implode( ',', $payment_times ); |
||
242 | $reg_csv_array[ __( 'Payment Method(s)', 'event_espresso' ) ] = implode( ",", $payment_methods ); |
||
243 | $reg_csv_array[ __( 'Gateway Transaction ID(s)', 'event_espresso' )] = implode( ',', $gateway_txn_ids_etc ); |
||
244 | |||
245 | //get whether or not the user has checked in |
||
246 | $reg_csv_array[__("Check-Ins", "event_espresso")] = $reg_model->count_related( $reg_row[ 'Registration.REG_ID'] , 'Checkin' ); |
||
247 | //get ticket of registration and its price |
||
248 | $ticket_model = \EE_Registry::instance()->load_model('Ticket'); |
||
249 | if( $reg_row[ 'Ticket.TKT_ID'] ) { |
||
250 | $ticket_name = \EEH_Export::prepare_value_from_db_for_display( $ticket_model, 'TKT_name', $reg_row[ 'Ticket.TKT_name' ] ); |
||
251 | $datetimes_strings = array(); |
||
252 | View Code Duplication | foreach( \EEM_Datetime::instance()->get_all_wpdb_results( array( array( 'Ticket.TKT_ID' => $reg_row[ 'Ticket.TKT_ID' ] ), 'order_by' => array( 'DTT_EVT_start' => 'ASC' ), 'default_where_conditions' => 'none' ) ) as $datetime){ |
|
253 | $datetimes_strings[] = \EEH_Export::prepare_value_from_db_for_display( \EEM_Datetime::instance(), 'DTT_EVT_start', $datetime[ 'Datetime.DTT_EVT_start'] ); |
||
254 | } |
||
255 | |||
256 | } else { |
||
257 | $ticket_name = __( 'Unknown', 'event_espresso' ); |
||
258 | $datetimes_strings = array( __( 'Unknown', 'event_espresso' ) ); |
||
259 | } |
||
260 | $reg_csv_array[$ticket_model->field_settings_for('TKT_name')->get_nicename()] = $ticket_name; |
||
261 | $reg_csv_array[__("Datetimes of Ticket", "event_espresso")] = implode(", ", $datetimes_strings); |
||
262 | //get datetime(s) of registration |
||
263 | |||
264 | //add attendee columns |
||
265 | foreach($att_fields_to_include as $att_field_name){ |
||
266 | $field_obj = \EEM_Attendee::instance()->field_settings_for($att_field_name); |
||
267 | View Code Duplication | if( $reg_row[ 'Attendee_CPT.ID' ]){ |
|
268 | if($att_field_name == 'STA_ID'){ |
||
269 | $value = \EEM_State::instance()->get_var( array( array( 'STA_ID' => $reg_row[ 'Attendee_Meta.STA_ID' ] ) ), 'STA_name' ); |
||
270 | }elseif($att_field_name == 'CNT_ISO'){ |
||
271 | $value = \EEM_Country::instance()->get_var( array( array( 'CNT_ISO' => $reg_row[ 'Attendee_Meta.CNT_ISO' ] ) ), 'CNT_name' ); |
||
272 | }else{ |
||
273 | $value = \EEH_Export::prepare_value_from_db_for_display( \EEM_Attendee::instance(), $att_field_name, $reg_row[ $field_obj->get_qualified_column() ] ); |
||
274 | } |
||
275 | }else{ |
||
276 | $value = ''; |
||
277 | } |
||
278 | |||
279 | $reg_csv_array[ \EEH_Export::get_column_name_for_field($field_obj) ] = $value; |
||
280 | } |
||
281 | |||
282 | //make sure each registration has the same questions in the same order |
||
283 | View Code Duplication | foreach($questions_for_these_regs_rows as $question_row){ |
|
284 | if( ! isset($reg_csv_array[$question_row[ 'Question.QST_admin_label']])){ |
||
285 | $reg_csv_array[$question_row[ 'Question.QST_admin_label' ] ] = null; |
||
286 | } |
||
287 | } |
||
288 | $answers = \EEM_Answer::instance()->get_all_wpdb_results( |
||
289 | array( |
||
290 | array( 'REG_ID' => $reg_row[ 'Registration.REG_ID' ] ), |
||
291 | 'force_join' => array( 'Question' ) |
||
292 | ) |
||
293 | ); |
||
294 | //now fill out the questions THEY answered |
||
295 | foreach( $answers as $answer_row ){ |
||
296 | View Code Duplication | if( $answer_row[ 'Question.QST_ID' ] ){ |
|
297 | $question_label = \EEH_Export::prepare_value_from_db_for_display( |
||
298 | \EEM_Question::instance(), |
||
299 | 'QST_admin_label', |
||
300 | $answer_row[ 'Question.QST_admin_label' ] |
||
301 | ); |
||
302 | } else { |
||
303 | $question_label = sprintf( __( 'Question $s', 'event_espresso' ), $answer_row[ 'Answer.QST_ID' ] ); |
||
304 | } |
||
305 | View Code Duplication | if ( isset( $answer_row[ 'Question.QST_type' ] ) |
|
306 | && $answer_row[ 'Question.QST_type' ] == \EEM_Question::QST_type_state |
||
307 | ) { |
||
308 | $reg_csv_array[ $question_label ] = \EEM_State::instance()->get_state_name_by_ID( |
||
309 | $answer_row[ 'Answer.ANS_value' ] |
||
310 | ); |
||
311 | } else { |
||
312 | $reg_csv_array[ $question_label ] = \EEH_Export::prepare_value_from_db_for_display( |
||
313 | \EEM_Answer::instance(), |
||
314 | 'ANS_value', |
||
315 | $answer_row[ 'Answer.ANS_value' ] |
||
316 | ); |
||
317 | } |
||
318 | } |
||
319 | $registrations_csv_ready_array[] = apply_filters( |
||
320 | 'FHEE__EE_Export__report_registrations__reg_csv_array', |
||
321 | $reg_csv_array, $reg_row |
||
322 | ); |
||
323 | } |
||
324 | } |
||
325 | //if we couldn't export anything, we want to at least show the column headers |
||
326 | View Code Duplication | if ( empty( $registrations_csv_ready_array ) ) { |
|
327 | $reg_csv_array = array(); |
||
328 | $model_and_fields_to_include = array( |
||
329 | 'Registration' => $reg_fields_to_include, |
||
330 | 'Attendee' => $att_fields_to_include |
||
331 | ); |
||
332 | foreach ( $model_and_fields_to_include as $model_name => $field_list ) { |
||
333 | $model = \EE_Registry::instance()->load_model( $model_name ); |
||
334 | foreach ( $field_list as $field_name ) { |
||
335 | $field = $model->field_settings_for( $field_name ); |
||
336 | $reg_csv_array[ \EEH_Export::get_column_name_for_field( $field ) ] = null; |
||
337 | } |
||
338 | } |
||
339 | $registrations_csv_ready_array[] = $reg_csv_array; |
||
340 | } |
||
341 | return $registrations_csv_ready_array; |
||
342 | } |
||
343 | |||
344 | |||
345 | |||
346 | /** |
||
347 | * Counts total unit to process |
||
348 | * |
||
349 | * @param int $event_id |
||
350 | * @return int |
||
351 | */ |
||
352 | public function count_units_to_process( $event_id ) { |
||
353 | //use the legacy filter |
||
354 | $query_params = apply_filters( |
||
355 | 'FHEE__EE_Export__report_registration_for_event', |
||
356 | array( |
||
357 | array( |
||
358 | 'OR' => array( |
||
359 | //don't include registrations from failed or abandoned transactions... |
||
360 | 'Transaction.STS_ID' => array( 'NOT IN', array( \EEM_Transaction::failed_status_code, \EEM_Transaction::abandoned_status_code ) ), |
||
361 | //unless the registration is approved, in which case include it regardless of transaction status |
||
362 | 'STS_ID' => \EEM_Registration::status_id_approved |
||
363 | ), |
||
364 | 'Ticket.TKT_deleted' => array( 'IN', array( true, false ) ) |
||
365 | ), |
||
366 | 'order_by' => array('Transaction.TXN_ID'=>'asc','REG_count'=>'asc'), |
||
367 | 'force_join' => array( 'Transaction', 'Ticket', 'Attendee' ) |
||
368 | ), |
||
369 | $event_id |
||
370 | ); |
||
371 | View Code Duplication | if( $event_id ){ |
|
372 | $query_params[0]['EVT_ID'] = $event_id; |
||
373 | } else { |
||
374 | $query_params[ 'force_join' ][] = 'Event'; |
||
375 | } |
||
376 | return \EEM_Registration::instance()->count( $query_params ); |
||
377 | } |
||
378 | |||
379 | |||
380 | |||
381 | /** |
||
382 | * Performs any clean-up logic when we know the job is completed. |
||
383 | * In this case, we delete the temporary file |
||
384 | * @param JobParameters $job_parameters |
||
385 | * @return boolean |
||
386 | */ |
||
387 | View Code Duplication | public function cleanup_job( JobParameters $job_parameters ){ |
|
395 | } |
||
396 | |||
397 | |||
398 |
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.