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 WC_Meta_Box_Product_Data 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 WC_Meta_Box_Product_Data, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class WC_Meta_Box_Product_Data { |
||
19 | |||
20 | /** |
||
21 | * Output the metabox. |
||
22 | * |
||
23 | * @param WP_Post $post Post object. |
||
24 | */ |
||
25 | public static function output( $post ) { |
||
26 | global $thepostid, $product_object; |
||
27 | |||
28 | $thepostid = $post->ID; |
||
29 | $product_object = $thepostid ? wc_get_product( $thepostid ) : new WC_Product(); |
||
30 | |||
31 | wp_nonce_field( 'woocommerce_save_data', 'woocommerce_meta_nonce' ); |
||
32 | |||
33 | include 'views/html-product-data-panel.php'; |
||
34 | } |
||
35 | |||
36 | /** |
||
37 | * Show tab content/settings. |
||
38 | */ |
||
39 | private static function output_tabs() { |
||
|
|||
40 | global $post, $thepostid, $product_object; |
||
41 | |||
42 | include 'views/html-product-data-general.php'; |
||
43 | include 'views/html-product-data-inventory.php'; |
||
44 | include 'views/html-product-data-shipping.php'; |
||
45 | include 'views/html-product-data-linked-products.php'; |
||
46 | include 'views/html-product-data-attributes.php'; |
||
47 | include 'views/html-product-data-advanced.php'; |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * Return array of product type options. |
||
52 | * |
||
53 | * @return array |
||
54 | */ |
||
55 | private static function get_product_type_options() { |
||
56 | return apply_filters( |
||
57 | 'product_type_options', |
||
58 | array( |
||
59 | 'virtual' => array( |
||
60 | 'id' => '_virtual', |
||
61 | 'wrapper_class' => 'show_if_simple', |
||
62 | 'label' => __( 'Virtual', 'woocommerce' ), |
||
63 | 'description' => __( 'Virtual products are intangible and are not shipped.', 'woocommerce' ), |
||
64 | 'default' => 'no', |
||
65 | ), |
||
66 | 'downloadable' => array( |
||
67 | 'id' => '_downloadable', |
||
68 | 'wrapper_class' => 'show_if_simple', |
||
69 | 'label' => __( 'Downloadable', 'woocommerce' ), |
||
70 | 'description' => __( 'Downloadable products give access to a file upon purchase.', 'woocommerce' ), |
||
71 | 'default' => 'no', |
||
72 | ), |
||
73 | ) |
||
74 | ); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Return array of tabs to show. |
||
79 | * |
||
80 | * @return array |
||
81 | */ |
||
82 | private static function get_product_data_tabs() { |
||
83 | $tabs = apply_filters( |
||
84 | 'woocommerce_product_data_tabs', |
||
85 | array( |
||
86 | 'general' => array( |
||
87 | 'label' => __( 'General', 'woocommerce' ), |
||
88 | 'target' => 'general_product_data', |
||
89 | 'class' => array( 'hide_if_grouped' ), |
||
90 | 'priority' => 10, |
||
91 | ), |
||
92 | 'inventory' => array( |
||
93 | 'label' => __( 'Inventory', 'woocommerce' ), |
||
94 | 'target' => 'inventory_product_data', |
||
95 | 'class' => array( 'show_if_simple', 'show_if_variable', 'show_if_grouped', 'show_if_external' ), |
||
96 | 'priority' => 20, |
||
97 | ), |
||
98 | 'shipping' => array( |
||
99 | 'label' => __( 'Shipping', 'woocommerce' ), |
||
100 | 'target' => 'shipping_product_data', |
||
101 | 'class' => array( 'hide_if_virtual', 'hide_if_grouped', 'hide_if_external' ), |
||
102 | 'priority' => 30, |
||
103 | ), |
||
104 | 'linked_product' => array( |
||
105 | 'label' => __( 'Linked Products', 'woocommerce' ), |
||
106 | 'target' => 'linked_product_data', |
||
107 | 'class' => array(), |
||
108 | 'priority' => 40, |
||
109 | ), |
||
110 | 'attribute' => array( |
||
111 | 'label' => __( 'Attributes', 'woocommerce' ), |
||
112 | 'target' => 'product_attributes', |
||
113 | 'class' => array(), |
||
114 | 'priority' => 50, |
||
115 | ), |
||
116 | 'variations' => array( |
||
117 | 'label' => __( 'Variations', 'woocommerce' ), |
||
118 | 'target' => 'variable_product_options', |
||
119 | 'class' => array( 'variations_tab', 'show_if_variable' ), |
||
120 | 'priority' => 60, |
||
121 | ), |
||
122 | 'advanced' => array( |
||
123 | 'label' => __( 'Advanced', 'woocommerce' ), |
||
124 | 'target' => 'advanced_product_data', |
||
125 | 'class' => array(), |
||
126 | 'priority' => 70, |
||
127 | ), |
||
128 | ) |
||
129 | ); |
||
130 | |||
131 | // Sort tabs based on priority. |
||
132 | uasort( $tabs, array( __CLASS__, 'product_data_tabs_sort' ) ); |
||
133 | |||
134 | return $tabs; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Callback to sort product data tabs on priority. |
||
139 | * |
||
140 | * @since 3.1.0 |
||
141 | * @param int $a First item. |
||
142 | * @param int $b Second item. |
||
143 | * |
||
144 | * @return bool |
||
145 | */ |
||
146 | private static function product_data_tabs_sort( $a, $b ) { |
||
147 | if ( ! isset( $a['priority'], $b['priority'] ) ) { |
||
148 | return -1; |
||
149 | } |
||
150 | |||
151 | if ( $a['priority'] === $b['priority'] ) { |
||
152 | return 0; |
||
153 | } |
||
154 | |||
155 | return $a['priority'] < $b['priority'] ? -1 : 1; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Filter callback for finding variation attributes. |
||
160 | * |
||
161 | * @param WC_Product_Attribute $attribute Product attribute. |
||
162 | * @return bool |
||
163 | */ |
||
164 | private static function filter_variation_attributes( $attribute ) { |
||
165 | return true === $attribute->get_variation(); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Show options for the variable product type. |
||
170 | */ |
||
171 | public static function output_variations() { |
||
182 | |||
183 | /** |
||
184 | * Prepare downloads for save. |
||
185 | * |
||
186 | * @param array $file_names File names. |
||
187 | * @param array $file_urls File urls. |
||
188 | * @param array $file_hashes File hashes. |
||
189 | * |
||
190 | * @return array |
||
191 | */ |
||
192 | private static function prepare_downloads( $file_names, $file_urls, $file_hashes ) { |
||
193 | $downloads = array(); |
||
194 | |||
195 | if ( ! empty( $file_urls ) ) { |
||
196 | $file_url_size = count( $file_urls ); |
||
197 | |||
198 | for ( $i = 0; $i < $file_url_size; $i ++ ) { |
||
199 | if ( ! empty( $file_urls[ $i ] ) ) { |
||
200 | $downloads[] = array( |
||
201 | 'name' => wc_clean( $file_names[ $i ] ), |
||
202 | 'file' => wp_unslash( trim( $file_urls[ $i ] ) ), |
||
203 | 'download_id' => wc_clean( $file_hashes[ $i ] ), |
||
204 | ); |
||
205 | } |
||
206 | } |
||
207 | } |
||
208 | return $downloads; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Prepare children for save. |
||
213 | * |
||
214 | * @return array |
||
215 | */ |
||
216 | private static function prepare_children() { |
||
217 | return isset( $_POST['grouped_products'] ) ? array_filter( array_map( 'intval', (array) $_POST['grouped_products'] ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * Prepare attributes for save. |
||
222 | * |
||
223 | * @param array $data Attribute data. |
||
224 | * |
||
225 | * @return array |
||
226 | */ |
||
227 | public static function prepare_attributes( $data = false ) { |
||
228 | $attributes = array(); |
||
229 | |||
230 | if ( ! $data ) { |
||
231 | $data = stripslashes_deep( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
||
232 | } |
||
233 | |||
234 | if ( isset( $data['attribute_names'], $data['attribute_values'] ) ) { |
||
235 | $attribute_names = $data['attribute_names']; |
||
236 | $attribute_values = $data['attribute_values']; |
||
237 | $attribute_visibility = isset( $data['attribute_visibility'] ) ? $data['attribute_visibility'] : array(); |
||
238 | $attribute_variation = isset( $data['attribute_variation'] ) ? $data['attribute_variation'] : array(); |
||
239 | $attribute_position = $data['attribute_position']; |
||
240 | $attribute_names_max_key = max( array_keys( $attribute_names ) ); |
||
241 | |||
242 | for ( $i = 0; $i <= $attribute_names_max_key; $i++ ) { |
||
243 | if ( empty( $attribute_names[ $i ] ) || ! isset( $attribute_values[ $i ] ) ) { |
||
244 | continue; |
||
245 | } |
||
246 | $attribute_id = 0; |
||
247 | $attribute_name = wc_clean( $attribute_names[ $i ] ); |
||
248 | |||
249 | if ( 'pa_' === substr( $attribute_name, 0, 3 ) ) { |
||
250 | $attribute_id = wc_attribute_taxonomy_id_by_name( $attribute_name ); |
||
251 | } |
||
252 | |||
253 | $options = isset( $attribute_values[ $i ] ) ? $attribute_values[ $i ] : ''; |
||
254 | |||
255 | if ( is_array( $options ) ) { |
||
256 | // Term ids sent as array. |
||
257 | $options = wp_parse_id_list( $options ); |
||
258 | } else { |
||
259 | // Terms or text sent in textarea. |
||
260 | $options = 0 < $attribute_id ? wc_sanitize_textarea( wc_sanitize_term_text_based( $options ) ) : wc_sanitize_textarea( $options ); |
||
261 | $options = wc_get_text_attributes( $options ); |
||
262 | } |
||
263 | |||
264 | if ( empty( $options ) ) { |
||
265 | continue; |
||
266 | } |
||
267 | |||
268 | $attribute = new WC_Product_Attribute(); |
||
269 | $attribute->set_id( $attribute_id ); |
||
270 | $attribute->set_name( $attribute_name ); |
||
271 | $attribute->set_options( $options ); |
||
272 | $attribute->set_position( $attribute_position[ $i ] ); |
||
273 | $attribute->set_visible( isset( $attribute_visibility[ $i ] ) ); |
||
274 | $attribute->set_variation( isset( $attribute_variation[ $i ] ) ); |
||
275 | $attributes[] = apply_filters( 'woocommerce_admin_meta_boxes_prepare_attribute', $attribute, $data, $i ); |
||
276 | } |
||
277 | } |
||
278 | return $attributes; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Prepare attributes for a specific variation or defaults. |
||
283 | * |
||
284 | * @param array $all_attributes List of attribute keys. |
||
285 | * @param string $key_prefix Attribute key prefix. |
||
286 | * @param int $index Attribute array index. |
||
287 | * @return array |
||
288 | */ |
||
289 | private static function prepare_set_attributes( $all_attributes, $key_prefix = 'attribute_', $index = null ) { |
||
290 | $attributes = array(); |
||
291 | |||
292 | if ( $all_attributes ) { |
||
293 | foreach ( $all_attributes as $attribute ) { |
||
294 | if ( $attribute->get_variation() ) { |
||
295 | $attribute_key = sanitize_title( $attribute->get_name() ); |
||
296 | |||
297 | if ( ! is_null( $index ) ) { |
||
298 | $value = isset( $_POST[ $key_prefix . $attribute_key ][ $index ] ) ? wp_unslash( $_POST[ $key_prefix . $attribute_key ][ $index ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
||
299 | } else { |
||
300 | $value = isset( $_POST[ $key_prefix . $attribute_key ] ) ? wp_unslash( $_POST[ $key_prefix . $attribute_key ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
||
301 | } |
||
302 | |||
303 | if ( $attribute->is_taxonomy() ) { |
||
304 | // Don't use wc_clean as it destroys sanitized characters. |
||
305 | $value = sanitize_title( $value ); |
||
306 | } else { |
||
307 | $value = html_entity_decode( wc_clean( $value ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // WPCS: sanitization ok. |
||
308 | } |
||
309 | |||
310 | $attributes[ $attribute_key ] = $value; |
||
311 | } |
||
312 | } |
||
313 | } |
||
314 | |||
315 | return $attributes; |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * Save meta box data. |
||
320 | * |
||
321 | * @param int $post_id WP post id. |
||
322 | * @param WP_Post $post Post object. |
||
323 | */ |
||
324 | public static function save( $post_id, $post ) { |
||
432 | |||
433 | /** |
||
434 | * Save variation meta box data. |
||
435 | * |
||
436 | * @param int $post_id WP post id. |
||
437 | * @param WP_Post $post Post object. |
||
438 | */ |
||
439 | public static function save_variations( $post_id, $post ) { |
||
537 | } |
||
538 |