2011-08-09 15:16:18 +00:00
< ? php
2014-06-24 12:05:13 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
2016-11-02 18:50:42 +00:00
exit ;
2014-06-24 12:05:13 +00:00
}
2013-02-20 17:14:46 +00:00
2011-08-09 15:16:18 +00:00
/**
2016-11-03 12:03:19 +00:00
* Product Variation Class . @ todo needs new getters / setters / changes code
*
* @ todo removed filters need to be mapped via add_action to the product actions of similar naming .
* woocommerce_variation_is_in_stock
* woocommerce_variation_sale_price_html
* woocommerce_variation_price_html
* woocommerce_variation_free_price_html
* woocommerce_get_variation_price_html
2012-08-10 13:21:10 +00:00
*
2016-11-02 18:50:42 +00:00
* @ todo removed filters need to be mapped via add_action to the product actions of similar naming .
* woocommerce_variation_is_in_stock
* woocommerce_variation_sale_price_html
* woocommerce_variation_price_html
* woocommerce_variation_free_price_html
* woocommerce_get_variation_price_html
*
2011-08-10 17:11:11 +00:00
* The WooCommerce product variation class handles product variation data .
2011-08-09 15:16:18 +00:00
*
2014-09-26 10:45:15 +00:00
* @ class WC_Product_Variation
2016-11-02 18:50:42 +00:00
* @ version 2.7 . 0
2014-09-26 10:45:15 +00:00
* @ package WooCommerce / Classes
* @ category Class
* @ author WooThemes
2011-08-09 15:16:18 +00:00
*/
2016-11-02 18:50:42 +00:00
class WC_Product_Variation extends WC_Product_Simple {
2013-09-20 16:01:09 +00:00
2011-08-09 15:16:18 +00:00
/**
2016-11-02 18:50:42 +00:00
* Post type .
* @ var string
2011-08-09 15:16:18 +00:00
*/
2016-11-02 18:50:42 +00:00
protected $post_type = 'product_variation' ;
2016-08-01 15:24:31 +00:00
2016-11-02 18:50:42 +00:00
/**
* Initialize simple product .
*
* @ param mixed $product
*/
public function __construct ( $product = 0 ) {
$this -> internal_meta_keys [] = '_variation_description' ;
parent :: __construct ( $product );
2014-06-24 12:05:13 +00:00
}
2012-08-10 13:21:10 +00:00
2016-10-17 11:22:23 +00:00
/**
* Get internal type .
* @ return string
*/
public function get_type () {
return 'variation' ;
}
2013-01-16 12:10:51 +00:00
/**
* Returns whether or not the product post exists .
*
2016-11-02 18:50:42 +00:00
* @ return array of attributes and their values for this variation
2013-01-16 12:10:51 +00:00
*/
2016-11-02 18:50:42 +00:00
public function get_variation_attributes () {
return wc_get_product_variation_attributes ( $this -> get_id () );
2013-01-16 12:10:51 +00:00
}
2012-08-14 19:42:38 +00:00
2013-09-23 14:47:47 +00:00
/**
* Wrapper for get_permalink . Adds this variations attributes to the URL .
2014-09-26 10:45:15 +00:00
*
2016-04-21 09:36:46 +00:00
* @ param $item_object item array If a cart or order item is passed , we can get a link containing the exact attributes selected for the variation , rather than the default attributes .
2013-09-23 14:47:47 +00:00
* @ return string
*/
2016-04-21 09:36:46 +00:00
public function get_permalink ( $item_object = null ) {
if ( ! empty ( $item_object [ 'variation' ] ) ) {
$data = $item_object [ 'variation' ];
} elseif ( ! empty ( $item_object [ 'item_meta_array' ] ) ) {
$data_keys = array_map ( 'wc_variation_attribute_name' , wp_list_pluck ( $item_object [ 'item_meta_array' ], 'key' ) );
$data_values = wp_list_pluck ( $item_object [ 'item_meta_array' ], 'value' );
2016-11-02 18:50:42 +00:00
$data = array_intersect_key ( array_combine ( $data_keys , $data_values ), $this -> get_attributes () );
2016-04-21 09:36:46 +00:00
} else {
2016-11-02 18:50:42 +00:00
$data = $this -> get_attributes ();
2016-04-21 09:36:46 +00:00
}
2016-11-02 18:50:42 +00:00
return add_query_arg ( array_map ( 'urlencode' , array_filter ( $data ) ), $this -> get_permalink () );
2013-09-23 14:47:47 +00:00
}
2013-09-25 11:35:06 +00:00
/**
* Get the add to url used mainly in loops .
*
* @ return string
*/
public function add_to_cart_url () {
2016-11-02 18:50:42 +00:00
$variation_data = array_map ( 'urlencode' , $this -> get_attributes () );
$url = $this -> is_purchasable () ? remove_query_arg ( 'added-to-cart' , add_query_arg ( array ( 'variation_id' => $this -> get_id (), 'add-to-cart' => $this -> get_parent_id () ), $this -> get_permalink () ) ) : $this -> get_permalink ();
2013-09-25 11:35:06 +00:00
return apply_filters ( 'woocommerce_product_add_to_cart_url' , $url , $this );
}
2016-11-02 18:50:42 +00:00
/*
|--------------------------------------------------------------------------
| CRUD methods
|--------------------------------------------------------------------------
*/
2013-09-25 11:35:06 +00:00
2012-08-14 19:42:38 +00:00
/**
2016-11-02 18:50:42 +00:00
* Callback to remove unwanted meta data .
2012-08-14 19:42:38 +00:00
*
2016-11-02 18:50:42 +00:00
* @ param object $meta
* @ return bool false if excluded .
2012-08-14 19:42:38 +00:00
*/
2016-11-02 18:50:42 +00:00
protected function exclude_internal_meta_keys ( $meta ) {
return ! in_array ( $meta -> meta_key , $this -> get_internal_meta_keys () ) && 0 !== stripos ( $meta -> meta_key , 'attribute_' );
2012-06-29 17:56:42 +00:00
}
2012-08-10 13:21:10 +00:00
2011-08-22 11:57:50 +00:00
/**
2016-11-02 18:50:42 +00:00
* Set attributes . Unlike the parent product which uses terms , variations are assigned
* specific attributes using name value pairs .
* @ param array
2014-09-26 10:45:15 +00:00
*/
2016-11-02 18:50:42 +00:00
public function set_attributes ( $attributes ) {
$this -> data [ 'attributes' ] = ( array ) $attributes ;
2014-09-26 10:45:15 +00:00
}
2012-08-10 13:21:10 +00:00
2011-08-22 11:57:50 +00:00
/**
2016-11-02 18:50:42 +00:00
* Returns array of attribute name value pairs .
* @ return array
2014-09-26 10:45:15 +00:00
*/
2016-11-02 18:50:42 +00:00
public function get_attributes () {
return $this -> data [ 'attributes' ];
2014-09-26 10:45:15 +00:00
}
2014-10-15 07:11:41 +00:00
/**
2016-11-02 18:50:42 +00:00
* Reads a product from the database and sets its data to the class .
2014-10-15 07:11:41 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
* @ param int $id Variation ID .
2014-10-15 07:11:41 +00:00
*/
2016-11-02 18:50:42 +00:00
public function read ( $id ) {
$this -> set_defaults ();
2014-11-19 17:46:05 +00:00
2016-11-02 18:50:42 +00:00
if ( ! $id || ! ( $post_object = get_post ( $id ) ) ) {
return ;
2014-10-15 07:11:41 +00:00
}
2016-11-02 18:50:42 +00:00
$this -> set_id ( $id );
$this -> set_parent_id ( $post_object -> post_parent );
2014-10-15 07:11:41 +00:00
2016-11-02 18:50:42 +00:00
// The post doesn't have a parent id, therefore its invalid and we should prevent this being created.
if ( empty ( $this -> get_parent_id () ) ) {
throw new Exception ( sprintf ( 'No parent product set for variation #%d' , $this -> get_id () ), 422 );
}
2014-12-03 09:28:04 +00:00
2016-11-02 18:50:42 +00:00
// The post parent is not a valid variable product so we should prevent this being created.
if ( 'product' !== get_post_type ( $this -> get_parent_id () ) ) {
throw new Exception ( sprintf ( 'Invalid parent for variation #%d' , $this -> get_id () ), 422 );
}
2013-09-19 15:31:54 +00:00
2016-11-02 18:50:42 +00:00
// Variation data.
$this -> set_props ( array (
'name' => get_the_title ( $post_object ),
'slug' => $post_object -> post_name ,
'status' => $post_object -> post_status ,
'date_created' => $post_object -> post_date ,
'date_modified' => $post_object -> post_modified ,
'description' => get_post_meta ( $id , '_variation_description' , true ),
'regular_price' => get_post_meta ( $id , '_regular_price' , true ),
'sale_price' => get_post_meta ( $id , '_sale_price' , true ),
'date_on_sale_from' => get_post_meta ( $id , '_sale_price_dates_from' , true ),
'date_on_sale_to' => get_post_meta ( $id , '_sale_price_dates_to' , true ),
'tax_status' => get_post_meta ( $id , '_tax_status' , true ),
'manage_stock' => get_post_meta ( $id , '_manage_stock' , true ),
'stock_quantity' => get_post_meta ( $id , '_stock' , true ),
'stock_status' => get_post_meta ( $id , '_stock_status' , true ),
'menu_order' => $post_object -> menu_order ,
'shipping_class_id' => current ( $this -> get_term_ids ( 'product_shipping_class' ) ),
'virtual' => get_post_meta ( $id , '_virtual' , true ),
'downloadable' => get_post_meta ( $id , '_downloadable' , true ),
'downloads' => array_filter ( ( array ) get_post_meta ( $id , '_downloadable_files' , true ) ),
'gallery_image_ids' => array_filter ( explode ( ',' , get_post_meta ( $id , '_product_image_gallery' , true ) ) ),
'download_limit' => get_post_meta ( $id , '_download_limit' , true ),
'download_expiry' => get_post_meta ( $id , '_download_expiry' , true ),
'image_id' => get_post_thumbnail_id ( $id ),
'backorders' => get_post_meta ( $this -> get_id (), '_backorders' , true ),
) );
// Data that can be inherited from the parent product.
$inherit_on_empty = array (
'_sku' => 'sku' ,
'_stock' => 'stock_quantity' , // @todo test this
'_weight' => 'weight' ,
'_length' => 'length' ,
'_width' => 'width' ,
'_height' => 'height' ,
);
$parent_props = array ();
foreach ( $inherit_on_empty as $meta_key => $prop ) {
$value = get_post_meta ( $this -> get_id (), $meta_key , true );
if ( '' !== $value ) {
$inherit_props [ $prop ] = $value ;
2013-01-16 12:10:51 +00:00
} else {
2016-11-02 18:50:42 +00:00
$inherit_props [ $prop ] = get_post_meta ( $this -> get_parent_id (), $meta_key , true );
2013-01-16 12:10:51 +00:00
}
2012-10-08 11:51:00 +00:00
}
2013-01-16 12:10:51 +00:00
2016-11-02 18:50:42 +00:00
$tax_class = get_post_meta ( $this -> get_id (), '_tax_class' , true );
2012-08-10 13:21:10 +00:00
2016-11-02 18:50:42 +00:00
if ( 'parent' === $tax_class || ! metadata_exists ( 'post' , $this -> get_id (), '_tax_class' ) ) {
$inherit_props [ 'tax_class' ] = get_post_meta ( $this -> get_parent_id (), '_tax_class' , true );
2014-04-08 14:02:11 +00:00
} else {
2016-11-02 18:50:42 +00:00
$inherit_props [ 'tax_class' ] = $tax_class ;
2014-04-08 14:02:11 +00:00
}
2014-09-26 10:45:15 +00:00
2016-11-02 18:50:42 +00:00
$this -> set_props ( $inherit_props );
if ( $this -> is_on_sale () ) {
$this -> set_price ( $this -> get_sale_price () );
2016-03-21 14:58:14 +00:00
} else {
2016-11-02 18:50:42 +00:00
$this -> set_price ( $this -> get_regular_price () );
2012-08-12 11:41:26 +00:00
}
2012-08-10 13:21:10 +00:00
2016-11-02 18:50:42 +00:00
$this -> read_meta_data ();
$this -> read_attributes ();
2014-08-04 09:51:50 +00:00
}
/**
2016-11-02 18:50:42 +00:00
* Create a new product .
2014-08-04 09:51:50 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
2014-08-04 09:51:50 +00:00
*/
2016-11-02 18:50:42 +00:00
public function create () {
$this -> set_date_created ( current_time ( 'timestamp' ) );
2014-08-04 09:51:50 +00:00
2016-11-02 18:50:42 +00:00
$id = wp_insert_post ( apply_filters ( 'woocommerce_new_product_variation_data' , array (
'post_type' => $this -> post_type ,
'post_status' => $this -> get_status () ? $this -> get_status () : 'publish' ,
'post_author' => get_current_user_id (),
'post_title' => get_the_title ( $this -> get_parent_id () ) . ' –' . wc_get_formatted_variation ( $this -> get_attributes (), true ),
'post_content' => '' ,
'post_parent' => $this -> get_parent_id (),
'comment_status' => 'closed' ,
'ping_status' => 'closed' ,
'menu_order' => $this -> get_menu_order (),
'post_date' => date ( 'Y-m-d H:i:s' , $this -> get_date_created () ),
'post_date_gmt' => get_gmt_from_date ( date ( 'Y-m-d H:i:s' , $this -> get_date_created () ) ),
) ), true );
2016-06-23 10:24:15 +00:00
2016-11-02 18:50:42 +00:00
if ( $id && ! is_wp_error ( $id ) ) {
$this -> set_id ( $id );
$this -> update_post_meta ();
$this -> update_terms ();
$this -> update_attributes ();
$this -> save_meta_data ();
do_action ( 'woocommerce_create_' . $this -> post_type , $id );
}
2015-07-15 18:12:12 +00:00
}
2014-06-24 12:05:13 +00:00
/**
2016-11-02 18:50:42 +00:00
* Updates an existing product .
2014-06-24 12:05:13 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
2014-06-24 12:05:13 +00:00
*/
2016-11-02 18:50:42 +00:00
public function update () {
$post_data = array (
'ID' => $this -> get_id (),
'post_title' => get_the_title ( $this -> get_parent_id () ) . ' –' . wc_get_formatted_variation ( $this -> get_attributes (), true ),
'post_parent' => $this -> get_parent_id (),
'comment_status' => 'closed' ,
'post_status' => $this -> get_status () ? $this -> get_status () : 'publish' ,
'menu_order' => $this -> get_menu_order (),
);
wp_update_post ( $post_data );
$this -> update_post_meta ();
$this -> update_terms ();
$this -> update_attributes ();
$this -> save_meta_data ();
do_action ( 'woocommerce_update_' . $this -> post_type , $this -> get_id () );
2014-06-24 12:05:13 +00:00
}
2012-12-28 09:59:20 +00:00
/**
2016-11-02 18:50:42 +00:00
* Helper method that updates all the post meta for a product based on it ' s settings in the WC_Product class .
2014-04-25 14:27:58 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
2012-12-28 09:59:20 +00:00
*/
2016-11-02 18:50:42 +00:00
public function update_post_meta () {
update_post_meta ( $this -> get_id (), '_variation_description' , $this -> get_description () );
parent :: update_post_meta ();
2014-06-24 12:05:13 +00:00
}
2013-08-13 15:56:09 +00:00
2014-06-24 12:05:13 +00:00
/**
2016-11-02 18:50:42 +00:00
* Save data ( either create or update depending on if we are working on an existing product ) .
2016-01-06 15:15:00 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
2014-06-24 12:05:13 +00:00
*/
2016-11-02 18:50:42 +00:00
public function save () {
if ( $this -> get_id () ) {
$this -> update ();
} else {
$this -> create ();
2012-12-28 09:59:20 +00:00
}
2016-11-02 18:50:42 +00:00
WC_Product_Variable :: sync ( $this -> get_parent_id () );
return $this -> get_id ();
2012-12-28 09:59:20 +00:00
}
2011-08-09 15:16:18 +00:00
/**
2016-11-02 18:50:42 +00:00
* For all stored terms in all taxonomies , save them to the DB .
2011-08-09 15:16:18 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
2011-08-09 15:16:18 +00:00
*/
2016-11-02 18:50:42 +00:00
protected function update_terms () {
wp_set_post_terms ( $this -> get_id (), array ( $this -> data [ 'shipping_class_id' ] ), 'product_shipping_class' , false );
2011-08-09 15:16:18 +00:00
}
2012-08-10 13:21:10 +00:00
2011-08-09 15:16:18 +00:00
/**
2016-11-02 18:50:42 +00:00
* Read attributes from post meta .
2011-08-09 15:16:18 +00:00
*
2016-11-02 18:50:42 +00:00
* @ since 2.7 . 0
2011-08-09 15:16:18 +00:00
*/
2016-11-02 18:50:42 +00:00
protected function read_attributes () {
$this -> set_attributes ( wc_get_product_variation_attributes ( $this -> get_id () ) );
2011-08-09 15:16:18 +00:00
}
2012-08-10 13:21:10 +00:00
2014-06-24 12:05:13 +00:00
/**
2016-11-02 18:50:42 +00:00
* Update attribute meta values .
* @ since 2.7 . 0
2014-06-24 12:05:13 +00:00
*/
2016-11-02 18:50:42 +00:00
protected function update_attributes () {
global $wpdb ;
$attributes = $this -> get_attributes ();
$updated_attribute_keys = array ();
foreach ( $attributes as $key => $value ) {
update_post_meta ( $this -> get_id (), 'attribute_' . $key , $value );
$updated_attribute_keys [] = 'attribute_' . $key ;
2014-06-24 12:05:13 +00:00
}
2016-11-02 18:50:42 +00:00
// Remove old taxonomies attributes so data is kept up to date - first get attribute key names.
$delete_attribute_keys = $wpdb -> get_col ( $wpdb -> prepare ( " SELECT meta_key FROM { $wpdb -> postmeta } WHERE meta_key LIKE 'attribute_%%' AND meta_key NOT IN ( ' " . implode ( " ',' " , array_map ( 'esc_sql' , $updated_attribute_keys ) ) . " ' ) AND post_id = %d; " , $this -> get_id () ) );
foreach ( $delete_attribute_keys as $key ) {
delete_post_meta ( $this -> get_id (), $key );
2014-08-04 18:31:40 +00:00
}
2014-08-04 09:51:50 +00:00
}
2014-06-24 12:05:13 +00:00
2016-11-02 18:50:42 +00:00
/*
|--------------------------------------------------------------------------
| Conditionals
|--------------------------------------------------------------------------
*/
2014-06-24 12:05:13 +00:00
/**
* Returns whether or not the product has enough stock for the order .
*
* @ param mixed $quantity
* @ return bool
*/
public function has_enough_stock ( $quantity ) {
2016-11-02 18:50:42 +00:00
if ( $this -> managing_stock () ) {
return $this -> backorders_allowed () || $this -> get_stock_quantity () >= $quantity ;
2014-06-24 12:05:13 +00:00
} else {
2016-11-02 18:50:42 +00:00
$parent = wc_get_product ( $this -> get_parent_id () );
return $parent -> has_enough_stock ( $quantity );
2012-06-26 12:17:08 +00:00
}
}
2012-08-10 13:21:10 +00:00
2015-08-13 10:12:24 +00:00
/**
2016-11-02 18:50:42 +00:00
* Returns false if the product cannot be bought .
* Override abstract method so that : i ) Disabled variations are not be purchasable by admins . ii ) Enabled variations are not purchasable if the parent product is not purchasable .
2015-08-13 10:12:24 +00:00
*
2016-11-02 18:50:42 +00:00
* @ return bool
2015-08-13 10:12:24 +00:00
*/
2016-11-02 18:50:42 +00:00
public function is_purchasable () {
return apply_filters ( 'woocommerce_variation_is_purchasable' , $this -> variation_is_visible () && parent :: is_purchasable (), $this );
2015-08-13 10:12:24 +00:00
}
2013-03-27 07:57:52 +00:00
/**
2016-11-02 18:50:42 +00:00
* Controls whether this particular variation will appear greyed - out ( inactive ) or not ( active ) .
* Used by extensions to make incompatible variations appear greyed - out , etc .
* Other possible uses : prevent out - of - stock variations from being selected .
2013-03-27 07:57:52 +00:00
*
2016-11-02 18:50:42 +00:00
* @ return bool
2013-03-27 07:57:52 +00:00
*/
2016-11-02 18:50:42 +00:00
public function variation_is_active () {
return apply_filters ( 'woocommerce_variation_is_active' , true , $this );
2013-03-27 07:57:52 +00:00
}
2015-05-14 17:56:26 +00:00
/**
2016-11-02 18:50:42 +00:00
* Checks if this particular variation is visible . Invisible variations are enabled and can be selected , but no price / stock info is displayed .
* Instead , a suitable 'unavailable' message is displayed .
* Invisible by default : Disabled variations and variations with an empty price .
2015-05-14 17:56:26 +00:00
*
2016-11-02 18:50:42 +00:00
* @ return bool
2015-05-14 17:56:26 +00:00
*/
2016-11-02 18:50:42 +00:00
public function variation_is_visible () {
return apply_filters ( 'woocommerce_variation_is_visible' , 'publish' === get_post_status ( $this -> get_id () ) && '' !== $this -> get_price (), $this -> get_id (), $this -> get_parent_id (), $this );
2015-05-14 17:56:26 +00:00
}
2013-08-07 14:38:31 +00:00
}