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 EE_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 EE_Export, and based on these observations, apply Extract Interface, too.
1 | <?php if (!defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed'); |
||
15 | class EE_Export { |
||
16 | |||
17 | const option_prefix = 'ee_report_job_'; |
||
18 | |||
19 | |||
20 | // instance of the EE_Export object |
||
21 | private static $_instance = NULL; |
||
22 | |||
23 | // instance of the EE_CSV object |
||
24 | /** |
||
25 | * |
||
26 | * @var EE_CSV |
||
27 | */ |
||
28 | var $EE_CSV = NULL; |
||
29 | |||
30 | |||
31 | private $_req_data = array(); |
||
32 | |||
33 | |||
34 | |||
35 | /** |
||
36 | * private constructor to prevent direct creation |
||
37 | * |
||
38 | * @Constructor |
||
39 | * @access private |
||
40 | * @param array $request_data |
||
41 | */ |
||
42 | private function __construct( $request_data = array() ) { |
||
43 | $this->_req_data = $request_data; |
||
44 | $this->today = date("Y-m-d",time()); |
||
|
|||
45 | require_once( EE_CLASSES . 'EE_CSV.class.php' ); |
||
46 | $this->EE_CSV= EE_CSV::instance(); |
||
47 | } |
||
48 | |||
49 | |||
50 | |||
51 | /** |
||
52 | * @ singleton method used to instantiate class object |
||
53 | * @ access public |
||
54 | * |
||
55 | * @param array $request_data |
||
56 | * @return \EE_Export |
||
57 | */ |
||
58 | public static function instance( $request_data = array() ) { |
||
59 | // check if class object is instantiated |
||
60 | if ( self::$_instance === NULL or ! is_object( self::$_instance ) or ! ( self::$_instance instanceof EE_Export )) { |
||
61 | self::$_instance = new self( $request_data ); |
||
62 | } |
||
63 | return self::$_instance; |
||
64 | } |
||
65 | |||
66 | |||
67 | /** |
||
68 | * @Export Event Espresso data - routes export requests |
||
69 | * @access public |
||
70 | * @return void | bool |
||
71 | */ |
||
72 | public function export() { |
||
73 | |||
74 | // in case of bulk exports, the "actual" action will be in action2, but first check regular action for "export" keyword |
||
75 | if ( isset( $this->_req_data['action'] ) && strpos( $this->_req_data['action'], 'export' ) === FALSE ) { |
||
76 | // check if action2 has export action |
||
77 | if ( isset( $this->_req_data['action2'] ) && strpos( $this->_req_data['action2'], 'export' ) !== FALSE ) { |
||
78 | // whoop! there it is! |
||
79 | $this->_req_data['action'] = $this->_req_data['action2']; |
||
80 | } |
||
81 | } |
||
82 | |||
83 | $this->_req_data['export'] = isset( $this->_req_data['export'] ) ? $this->_req_data['export'] : ''; |
||
84 | |||
85 | switch ($this->_req_data['export']) { |
||
86 | case 'report': |
||
87 | switch ($this->_req_data['action']) { |
||
88 | |||
89 | |||
90 | case "event"; |
||
91 | case "export_events"; |
||
92 | case 'all_event_data' : |
||
93 | $this->export_all_event_data(); |
||
94 | break; |
||
95 | |||
96 | case 'registrations_report_for_event': |
||
97 | $this->report_registrations_for_event( $this->_req_data['EVT_ID'] ); |
||
98 | break; |
||
99 | |||
100 | case 'attendees': |
||
101 | $this->export_attendees(); |
||
102 | break; |
||
103 | |||
104 | case 'categories': |
||
105 | $this->export_categories(); |
||
106 | break; |
||
107 | |||
108 | default: |
||
109 | EE_Error::add_error(__('An error occurred! The requested export report could not be found.','event_espresso'), __FILE__, __FUNCTION__, __LINE__ ) ; |
||
110 | return FALSE; |
||
111 | break; |
||
112 | |||
113 | } |
||
114 | break; // end of switch export : report |
||
115 | default: |
||
116 | break; |
||
117 | } // end of switch export |
||
118 | |||
119 | exit; |
||
120 | |||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Downloads a CSV file with all the columns, but no data. This should be used for importing |
||
125 | * @return null kills execution |
||
126 | */ |
||
127 | function export_sample(){ |
||
128 | $event = EEM_Event::instance()->get_one(); |
||
129 | $this->_req_data['EVT_ID'] = $event->ID(); |
||
130 | $this->export_all_event_data(); |
||
131 | } |
||
132 | |||
133 | |||
134 | |||
135 | |||
136 | /** |
||
137 | * @Export data for ALL events |
||
138 | * @access public |
||
139 | * @return void |
||
140 | */ |
||
141 | function export_all_event_data() { |
||
142 | // are any Event IDs set? |
||
143 | $event_query_params = array(); |
||
144 | $related_models_query_params = array(); |
||
145 | $related_through_reg_query_params = array(); |
||
146 | $datetime_ticket_query_params = array(); |
||
147 | $price_query_params = array(); |
||
148 | $price_type_query_params = array(); |
||
149 | $term_query_params = array(); |
||
150 | $state_country_query_params = array(); |
||
151 | $question_group_query_params = array(); |
||
152 | $question_query_params = array(); |
||
153 | if ( isset( $this->_req_data['EVT_ID'] )) { |
||
154 | // do we have an array of IDs ? |
||
155 | |||
156 | if ( is_array( $this->_req_data['EVT_ID'] )) { |
||
157 | $EVT_IDs = array_map( 'sanitize_text_field', $this->_req_data['EVT_ID'] ); |
||
158 | $value_to_equal = array('IN',$EVT_IDs); |
||
159 | $filename = 'events'; |
||
160 | } else { |
||
161 | // generate regular where = clause |
||
162 | $EVT_ID = absint( $this->_req_data['EVT_ID'] ); |
||
163 | $value_to_equal = $EVT_ID; |
||
164 | $event = EE_Registry::instance()->load_model('Event')->get_one_by_ID($EVT_ID); |
||
165 | |||
166 | $filename = 'event-' . ( $event instanceof EE_Event ? $event->slug() : __( 'unknown', 'event_espresso' ) ); |
||
167 | |||
168 | } |
||
169 | $event_query_params[0]['EVT_ID'] =$value_to_equal; |
||
170 | $related_models_query_params[0]['Event.EVT_ID'] = $value_to_equal; |
||
171 | $related_through_reg_query_params[0]['Registration.EVT_ID'] = $value_to_equal; |
||
172 | $datetime_ticket_query_params[0]['Datetime.EVT_ID'] = $value_to_equal; |
||
173 | $price_query_params[0]['Ticket.Datetime.EVT_ID'] = $value_to_equal; |
||
174 | $price_type_query_params[0]['Price.Ticket.Datetime.EVT_ID'] = $value_to_equal; |
||
175 | $term_query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $value_to_equal; |
||
176 | $state_country_query_params[0]['Venue.Event.EVT_ID'] = $value_to_equal; |
||
177 | $question_group_query_params[0]['Event.EVT_ID'] = $value_to_equal; |
||
178 | $question_query_params[0]['Question_Group.Event.EVT_ID'] = $value_to_equal; |
||
179 | |||
180 | } else { |
||
181 | $filename = 'all-events'; |
||
182 | } |
||
183 | |||
184 | |||
185 | // array in the format: table name => query where clause |
||
186 | $models_to_export = array( |
||
187 | 'Event'=>$event_query_params, |
||
188 | 'Datetime'=>$related_models_query_params, |
||
189 | 'Ticket_Template'=>$price_query_params, |
||
190 | 'Ticket'=>$datetime_ticket_query_params, |
||
191 | 'Datetime_Ticket'=>$datetime_ticket_query_params, |
||
192 | 'Price_Type'=>$price_type_query_params, |
||
193 | 'Price'=>$price_query_params, |
||
194 | 'Ticket_Price'=>$price_query_params, |
||
195 | 'Term'=>$term_query_params, |
||
196 | 'Term_Taxonomy'=>$related_models_query_params, |
||
197 | 'Term_Relationship'=>$related_models_query_params, //model has NO primary key... |
||
198 | 'Country'=>$state_country_query_params, |
||
199 | 'State'=>$state_country_query_params, |
||
200 | 'Venue'=>$related_models_query_params, |
||
201 | 'Event_Venue'=>$related_models_query_params, |
||
202 | 'Question_Group'=>$question_group_query_params, |
||
203 | 'Event_Question_Group'=>$question_group_query_params, |
||
204 | 'Question'=>$question_query_params, |
||
205 | 'Question_Group_Question'=>$question_query_params, |
||
206 | // 'Transaction'=>$related_through_reg_query_params, |
||
207 | // 'Registration'=>$related_models_query_params, |
||
208 | // 'Attendee'=>$related_through_reg_query_params, |
||
209 | // 'Line_Item'=> |
||
210 | |||
211 | ); |
||
212 | |||
213 | $model_data = $this->_get_export_data_for_models( $models_to_export ); |
||
214 | |||
215 | $filename = $this->generate_filename ( $filename ); |
||
216 | |||
217 | View Code Duplication | if ( ! $this->EE_CSV->export_multiple_model_data_to_csv( $filename, $model_data )) { |
|
218 | EE_Error::add_error(__("'An error occurred and the Event details could not be exported from the database.'", "event_espresso"), __FILE__, __FUNCTION__, __LINE__ ); |
||
219 | } |
||
220 | } |
||
221 | |||
222 | function report_attendees(){ |
||
223 | $attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results( |
||
224 | array( |
||
225 | 'force_join' => array( 'State', 'Country' ), |
||
226 | 'caps' => EEM_Base::caps_read_admin |
||
227 | ) |
||
228 | ); |
||
229 | $csv_data = array(); |
||
230 | View Code Duplication | foreach( $attendee_rows as $attendee_row ){ |
|
231 | $csv_row = array(); |
||
232 | foreach( EEM_Attendee::instance()->field_settings() as $field_name => $field_obj ){ |
||
233 | if( $field_name == 'STA_ID' ){ |
||
234 | $state_name_field = EEM_State::instance()->field_settings_for( 'STA_name' ); |
||
235 | $csv_row[ __( 'State', 'event_espresso' ) ] = $attendee_row[ $state_name_field->get_qualified_column() ]; |
||
236 | }elseif( $field_name == 'CNT_ISO' ){ |
||
237 | $country_name_field = EEM_Country::instance()->field_settings_for( 'CNT_name' ); |
||
238 | $csv_row[ __( 'Country', 'event_espresso' ) ] = $attendee_row[ $country_name_field->get_qualified_column() ]; |
||
239 | }else{ |
||
240 | $csv_row[ $field_obj->get_nicename() ] = $attendee_row[ $field_obj->get_qualified_column() ]; |
||
241 | } |
||
242 | } |
||
243 | $csv_data[] = $csv_row; |
||
244 | } |
||
245 | |||
246 | $filename = $this->generate_filename ( 'contact-list-report' ); |
||
247 | |||
248 | $handle = $this->EE_CSV->begin_sending_csv( $filename); |
||
249 | $this->EE_CSV->write_data_array_to_csv($handle, $csv_data); |
||
250 | $this->EE_CSV->end_sending_csv($handle); |
||
251 | } |
||
252 | |||
253 | |||
254 | /** |
||
255 | * @Export data for ALL attendees |
||
256 | * @access public |
||
257 | * @return void |
||
258 | */ |
||
259 | function export_attendees() { |
||
260 | |||
261 | $states_that_have_an_attendee = EEM_State::instance()->get_all(array(0=>array('Attendee.ATT_ID'=>array('IS NOT NULL')))); |
||
262 | $countries_that_have_an_attendee = EEM_Country::instance()->get_all(array(0=>array('Attendee.ATT_ID'=>array('IS NOT NULL')))); |
||
263 | // $states_to_export_query_params |
||
264 | $models_to_export = array( |
||
265 | 'Country'=>array(array('CNT_ISO'=>array('IN',array_keys($countries_that_have_an_attendee)))), |
||
266 | 'State'=>array(array('STA_ID'=>array('IN',array_keys($states_that_have_an_attendee)))), |
||
267 | 'Attendee'=>array(), |
||
268 | ); |
||
269 | |||
270 | |||
271 | |||
272 | $model_data = $this->_get_export_data_for_models( $models_to_export ); |
||
273 | $filename = $this->generate_filename ( 'all-attendees' ); |
||
274 | |||
275 | View Code Duplication | if ( ! $this->EE_CSV->export_multiple_model_data_to_csv( $filename, $model_data )) { |
|
276 | EE_Error::add_error(__('An error occurred and the Attendee data could not be exported from the database.','event_espresso'), __FILE__, __FUNCTION__, __LINE__ ); |
||
277 | } |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * Shortcut for preparing a database result for display |
||
282 | * @param EEM_Base $model |
||
283 | * @param string $field_name |
||
284 | * @param string $raw_db_value |
||
285 | * @param boolean|string $pretty_schema true to display pretty, a string to use a specific "Schema", or false to NOT display pretty |
||
286 | * @return string |
||
287 | */ |
||
288 | View Code Duplication | protected function _prepare_value_from_db_for_display( $model, $field_name, $raw_db_value, $pretty_schema = true ) { |
|
289 | $field_obj = $model->field_settings_for( $field_name ); |
||
290 | $value_on_model_obj = $field_obj->prepare_for_set_from_db( $raw_db_value ); |
||
291 | if( $field_obj instanceof EE_Datetime_Field ) { |
||
292 | $field_obj->set_date_format( EE_CSV::instance()->get_date_format_for_csv( $field_obj->get_date_format( $pretty_schema ) ), $pretty_schema ); |
||
293 | $field_obj->set_time_format( EE_CSV::instance()->get_time_format_for_csv( $field_obj->get_time_format( $pretty_schema ) ), $pretty_schema ); |
||
294 | } |
||
295 | if( $pretty_schema === true){ |
||
296 | return $field_obj->prepare_for_pretty_echoing( $value_on_model_obj ); |
||
297 | }elseif( is_string( $pretty_schema ) ) { |
||
298 | return $field_obj->prepare_for_pretty_echoing($value_on_model_obj, $pretty_schema ); |
||
299 | }else{ |
||
300 | return $field_obj->prepare_for_get( $value_on_model_obj ); |
||
301 | } |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price name, |
||
306 | * and the questions associated with the registrations |
||
307 | * @param int $event_id |
||
308 | */ |
||
309 | function report_registrations_for_event( $event_id = NULL ){ |
||
521 | |||
522 | /** |
||
523 | * Gets the 'normal' column named for fields |
||
524 | * @param EE_Model_Field_Base $field |
||
525 | * @return string |
||
526 | */ |
||
527 | protected function _get_column_name_for_field(EE_Model_Field_Base $field){ |
||
528 | return $field->get_nicename()."[".$field->get_name()."]"; |
||
529 | } |
||
530 | |||
531 | |||
532 | /** |
||
533 | * @Export data for ALL events |
||
534 | * @access public |
||
535 | * @return void |
||
536 | */ |
||
537 | function export_categories() { |
||
569 | |||
570 | |||
571 | /** |
||
572 | * @process export name to create a suitable filename |
||
573 | * @access private |
||
574 | * @param string - export_name |
||
575 | * @return string on success, FALSE on fail |
||
576 | */ |
||
577 | private function generate_filename ( $export_name = '' ) { |
||
587 | |||
588 | |||
589 | |||
590 | /** |
||
591 | * @recursive function for exporting table data and merging the results with the next results |
||
592 | * @access private |
||
593 | * @param array $models_to_export keys are model names (eg 'Event', 'Attendee', etc.) and values are arrays of query params like on EEM_Base::get_all |
||
594 | * @return array on success, FALSE on fail |
||
595 | */ |
||
596 | private function _get_export_data_for_models( $models_to_export = array() ) { |
||
630 | } |
||
631 | /* End of file EE_Export.class.php */ |
||
633 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: