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: