1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) exit;
4
5 6 7 8 9 10 11 12 13 14 15
16 class WC_Product_Variation extends WC_Product {
17
18
19 public $variation_id;
20
21
22 public $parent;
23
24
25 public $variation_data = array();
26
27
28 public $variation_has_length = false;
29
30
31 public $variation_has_width = false;
32
33
34 public $variation_has_height = false;
35
36
37 public $variation_has_weight = false;
38
39
40 public $variation_has_stock = false;
41
42
43 public $variation_has_sku = false;
44
45
46 public $variation_shipping_class = false;
47
48
49 public $variation_shipping_class_id = false;
50
51
52 public $variation_has_tax_class = false;
53
54
55 public $variation_has_downloadable_files = false;
56
57 58 59 60 61 62 63 64
65 public function __construct( $variation, $args = array() ) {
66
67 $this->product_type = 'variation';
68
69 if ( is_object( $variation ) ) {
70 $this->variation_id = absint( $variation->ID );
71 } else {
72 $this->variation_id = absint( $variation );
73 }
74
75
76 $this->id = ! empty( $args['parent_id'] ) ? intval( $args['parent_id'] ) : wp_get_post_parent_id( $this->variation_id );
77
78
79 if ( empty( $this->id ) )
80 return;
81
82
83 $this->parent = ! empty( $args['parent'] ) ? $args['parent'] : get_product( $this->id );
84 $this->post = ! empty( $this->parent->post ) ? $this->parent->post : array();
85 $this->product_custom_fields = get_post_meta( $this->variation_id );
86
87
88 foreach ( $this->product_custom_fields as $name => $value ) {
89 if ( ! strstr( $name, 'attribute_' ) )
90 continue;
91
92 $this->variation_data[ $name ] = sanitize_title( $value[0] );
93 }
94
95
96 if ( ! empty( $this->product_custom_fields['_sku'][0] ) ) {
97 $this->variation_has_sku = true;
98 $this->sku = $this->product_custom_fields['_sku'][0];
99 }
100
101 if ( ! empty( $this->product_custom_fields['_downloadable_files'][0] ) ) {
102 $this->variation_has_downloadable_files = true;
103 $this->downloadable_files = $this->product_custom_fields['_downloadable_files'][0];
104 }
105
106 if ( isset( $this->product_custom_fields['_stock'][0] ) && '' !== $this->product_custom_fields['_stock'][0] && 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
107 $this->variation_has_stock = true;
108 $this->manage_stock = 'yes';
109 $this->stock = $this->product_custom_fields['_stock'][0];
110 }
111
112 if ( isset( $this->product_custom_fields['_weight'][0] ) && $this->product_custom_fields['_weight'][0] !== '' ) {
113 $this->variation_has_weight = true;
114 $this->weight = $this->product_custom_fields['_weight'][0];
115 }
116
117 if ( isset( $this->product_custom_fields['_length'][0] ) && $this->product_custom_fields['_length'][0] !== '' ) {
118 $this->variation_has_length = true;
119 $this->length = $this->product_custom_fields['_length'][0];
120 }
121
122 if ( isset( $this->product_custom_fields['_width'][0] ) && $this->product_custom_fields['_width'][0] !== '' ) {
123 $this->variation_has_width = true;
124 $this->width = $this->product_custom_fields['_width'][0];
125 }
126
127 if ( isset( $this->product_custom_fields['_height'][0] ) && $this->product_custom_fields['_height'][0] !== '' ) {
128 $this->variation_has_height = true;
129 $this->height = $this->product_custom_fields['_height'][0];
130 }
131
132 if ( isset( $this->product_custom_fields['_downloadable'][0] ) && $this->product_custom_fields['_downloadable'][0] == 'yes' ) {
133 $this->downloadable = 'yes';
134 } else {
135 $this->downloadable = 'no';
136 }
137
138 if ( isset( $this->product_custom_fields['_virtual'][0] ) && $this->product_custom_fields['_virtual'][0] == 'yes' ) {
139 $this->virtual = 'yes';
140 } else {
141 $this->virtual = 'no';
142 }
143
144 if ( isset( $this->product_custom_fields['_tax_class'][0] ) ) {
145 $this->variation_has_tax_class = true;
146 $this->tax_class = $this->product_custom_fields['_tax_class'][0];
147 }
148
149 if ( isset( $this->product_custom_fields['_sale_price_dates_from'][0] ) )
150 $this->sale_price_dates_from = $this->product_custom_fields['_sale_price_dates_from'][0];
151
152 if ( isset( $this->product_custom_fields['_sale_price_dates_to'][0] ) )
153 $this->sale_price_dates_to = $this->product_custom_fields['_sale_price_dates_to'][0];
154
155
156 $this->price = isset( $this->product_custom_fields['_price'][0] ) ? $this->product_custom_fields['_price'][0] : '';
157 $this->regular_price = isset( $this->product_custom_fields['_regular_price'][0] ) ? $this->product_custom_fields['_regular_price'][0] : '';
158 $this->sale_price = isset( $this->product_custom_fields['_sale_price'][0] ) ? $this->product_custom_fields['_sale_price'][0] : '';
159
160
161 if ( $this->price !== '' && $this->regular_price == '' ) {
162 update_post_meta( $this->variation_id, '_regular_price', $this->price );
163 $this->regular_price = $this->price;
164
165 if ( $this->sale_price !== '' && $this->sale_price < $this->regular_price ) {
166 update_post_meta( $this->variation_id, '_price', $this->sale_price );
167 $this->price = $this->sale_price;
168 }
169 }
170
171 $this->total_stock = $this->stock;
172 }
173
174 175 176 177 178 179
180 public function exists() {
181 return empty( $this->id ) ? false : true;
182 }
183
184 185 186 187
188 public function get_permalink() {
189 return add_query_arg( $this->variation_data, get_permalink( $this->id ) );
190 }
191
192 193 194 195 196 197
198 public function add_to_cart_url() {
199 $url = $this->is_purchasable() && $this->is_in_stock() ? remove_query_arg( 'added-to-cart', add_query_arg( array_merge( array( 'variation_id' => $this->variation_id, 'add-to-cart' => $this->id ), $this->variation_data ) ) ) : get_permalink( $this->id );
200
201 return apply_filters( 'woocommerce_product_add_to_cart_url', $url, $this );
202 }
203
204 205 206 207 208 209
210 public function add_to_cart_text() {
211 $text = $this->is_purchasable() && $this->is_in_stock() ? __( 'Add to cart', 'woocommerce' ) : __( 'Read More', 'woocommerce' );
212
213 return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this );
214 }
215
216 217 218 219 220
221 public function variation_is_visible() {
222 $visible = true;
223
224
225 if ( get_post_status( $this->variation_id ) != 'publish' )
226 $visible = false;
227
228
229 elseif ( get_option('woocommerce_hide_out_of_stock_items') == 'yes' && ! $this->is_in_stock() )
230 $visible = false;
231
232
233 elseif ( $this->get_price() === "" )
234 $visible = false;
235
236 return apply_filters( 'woocommerce_variation_is_visible', $visible, $this->variation_id, $this->id );
237 }
238
239 240 241 242 243 244
245 public function is_purchasable() {
246
247
248 if ( get_post_status( $this->variation_id ) != 'publish' )
249 $purchasable = false;
250
251 else
252 $purchasable = parent::is_purchasable();
253
254 return $purchasable;
255 }
256
257 258 259 260 261 262
263 public function parent_is_visible() {
264 return $this->is_visible();
265 }
266
267 268 269 270 271
272 public function get_variation_id() {
273 return absint( $this->variation_id );
274 }
275
276 277 278 279 280
281 public function get_variation_attributes() {
282 return $this->variation_data;
283 }
284
285 286 287 288 289
290 public function get_price_html( $price = '' ) {
291
292 $tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
293 $display_price = $tax_display_mode == 'incl' ? $this->get_price_including_tax() : $this->get_price_excluding_tax();
294 $display_regular_price = $tax_display_mode == 'incl' ? $this->get_price_including_tax( 1, $this->get_regular_price() ) : $this->get_price_excluding_tax( 1, $this->get_regular_price() );
295 $display_sale_price = $tax_display_mode == 'incl' ? $this->get_price_including_tax( 1, $this->get_sale_price() ) : $this->get_price_excluding_tax( 1, $this->get_sale_price() );
296
297 if ( $this->get_price() !== '' ) {
298 if ( $this->is_on_sale() ) {
299
300 $price = '<del>' . wc_price( $display_regular_price ) . '</del> <ins>' . wc_price( $display_sale_price ) . '</ins>' . $this->get_price_suffix();
301
302 $price = apply_filters( 'woocommerce_variation_sale_price_html', $price, $this );
303
304 } elseif ( $this->get_price() > 0 ) {
305
306 $price = wc_price( $display_price ) . $this->get_price_suffix();
307
308 $price = apply_filters( 'woocommerce_variation_price_html', $price, $this );
309
310 } else {
311
312 $price = __( 'Free!', 'woocommerce' );
313
314 $price = apply_filters( 'woocommerce_variation_free_price_html', $price, $this );
315
316 }
317 } else {
318 $price = apply_filters( 'woocommerce_variation_empty_price_html', '', $this );
319 }
320
321 return apply_filters( 'woocommerce_get_variation_price_html', $price, $this );
322 }
323
324 325 326 327
328 public function get_image_id() {
329 if ( $this->variation_id && has_post_thumbnail( $this->variation_id ) ) {
330 $image_id = get_post_thumbnail_id( $this->variation_id );
331 } elseif ( has_post_thumbnail( $this->id ) ) {
332 $image_id = get_post_thumbnail_id( $this->id );
333 } elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) {
334 $image_id = get_post_thumbnail_id( $parent_id );
335 } else {
336 $image_id = 0;
337 }
338 return $image_id;
339 }
340
341 342 343 344 345 346 347
348 public function get_image( $size = 'shop_thumbnail', $attr = array() ) {
349 if ( $this->variation_id && has_post_thumbnail( $this->variation_id ) ) {
350 $image = get_the_post_thumbnail( $this->variation_id, $size, $attr );
351 } elseif ( has_post_thumbnail( $this->id ) ) {
352 $image = get_the_post_thumbnail( $this->id, $size, $attr );
353 } elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) {
354 $image = get_the_post_thumbnail( $parent_id, $size , $attr);
355 } else {
356 $image = wc_placeholder_img( $size );
357 }
358
359 return $image;
360 }
361
362 363 364 365 366 367 368
369 function set_stock( $amount = null, $force_variation_stock = false ) {
370 if ( is_null( $amount ) )
371 return;
372
373 if ( $amount === '' && $force_variation_stock ) {
374
375
376 $this->variation_has_stock = false;
377 $this->stock = '';
378 unset( $this->manage_stock );
379
380
381 update_post_meta( $this->variation_id, '_stock', '' );
382
383
384 WC_Product_Variable::sync( $this->id );
385
386 } elseif ( $this->variation_has_stock || $force_variation_stock ) {
387
388
389 $this->stock = intval( $amount );
390 $this->variation_has_stock = true;
391 $this->manage_stock = 'yes';
392
393
394 update_post_meta( $this->variation_id, '_stock', $this->stock );
395
396
397 delete_transient( 'wc_product_total_stock_' . $this->id );
398
399
400 if ( ! $this->is_in_stock() ) {
401
402
403 $parent_product = get_product( $this->id );
404
405
406 if ( ! $parent_product->backorders_allowed() && $parent_product->get_total_stock() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
407
408 $all_managed = true;
409
410 if ( sizeof( $parent_product->get_children() ) > 0 ) {
411 foreach ( $parent_product->get_children() as $child_id ) {
412 $stock = get_post_meta( $child_id, '_stock', true );
413 if ( $stock == '' ) {
414 $all_managed = false;
415 break;
416 }
417 }
418 }
419
420 if ( $all_managed ) {
421 $this->set_stock_status( 'outofstock' );
422 }
423 }
424
425 } elseif ( $this->is_in_stock() ) {
426 $this->set_stock_status( 'instock' );
427 }
428
429
430 WC_Product_Variable::sync( $this->id );
431
432
433 do_action( 'woocommerce_product_set_stock', $this );
434
435 return $this->get_stock_quantity();
436
437 } else {
438
439 return parent::set_stock( $amount );
440
441 }
442 }
443
444 445 446 447 448 449
450 public function reduce_stock( $by = 1 ) {
451 if ( $this->variation_has_stock ) {
452 return $this->set_stock( $this->stock - $by );
453 } else {
454 return parent::reduce_stock( $by );
455 }
456 }
457
458 459 460 461 462 463
464 public function increase_stock( $by = 1 ) {
465 if ( $this->variation_has_stock ) {
466 return $this->set_stock( $this->stock + $by );
467 } else {
468 return parent::increase_stock( $by );
469 }
470 }
471
472 473 474 475 476 477
478 public function get_shipping_class() {
479 if ( ! $this->variation_shipping_class ) {
480 $classes = get_the_terms( $this->variation_id, 'product_shipping_class' );
481
482 if ( $classes && ! is_wp_error( $classes ) ) {
483 $this->variation_shipping_class = esc_attr( current( $classes )->slug );
484 } else {
485 $this->variation_shipping_class = parent::get_shipping_class();
486 }
487 }
488
489 return $this->variation_shipping_class;
490 }
491
492 493 494 495 496 497
498 public function get_shipping_class_id() {
499 if ( ! $this->variation_shipping_class_id ) {
500
501 $classes = get_the_terms( $this->variation_id, 'product_shipping_class' );
502
503 if ( $classes && ! is_wp_error( $classes ) )
504 $this->variation_shipping_class_id = current( $classes )->term_id;
505 else
506 $this->variation_shipping_class_id = parent::get_shipping_class_id();
507
508 }
509 return absint( $this->variation_shipping_class_id );
510 }
511
512 513 514 515 516 517 518
519 public function get_formatted_name() {
520
521 if ( $this->get_sku() )
522 $identifier = $this->get_sku();
523 else
524 $identifier = '#' . $this->variation_id;
525
526 $attributes = $this->get_variation_attributes();
527 $extra_data = ' – ' . implode( ', ', $attributes ) . ' – ' . wc_price( $this->get_price() );
528
529 return sprintf( __( '%s – %s%s', 'woocommerce' ), $identifier, $this->get_title(), $extra_data );
530 }
531 }
532