2016-11-11 14:31:15 +00:00
< ? php
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
/**
* WC Product Data Store : Stored in CPT .
*
2017-03-15 16:36:53 +00:00
* @ version 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ category Class
* @ author WooThemes
*/
2016-11-22 13:54:51 +00:00
class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Data_Store_Interface , WC_Product_Data_Store_Interface {
2016-11-21 23:48:49 +00:00
/**
* Data stored in meta keys , but not considered " meta " .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-21 23:48:49 +00:00
* @ var array
*/
protected $internal_meta_keys = array (
'_visibility' ,
'_sku' ,
'_price' ,
'_regular_price' ,
'_sale_price' ,
'_sale_price_dates_from' ,
'_sale_price_dates_to' ,
'total_sales' ,
'_tax_status' ,
'_tax_class' ,
'_manage_stock' ,
'_stock' ,
'_stock_status' ,
'_backorders' ,
'_sold_individually' ,
'_weight' ,
'_length' ,
'_width' ,
'_height' ,
'_upsell_ids' ,
'_crosssell_ids' ,
'_purchase_note' ,
'_default_attributes' ,
'_product_attributes' ,
'_virtual' ,
'_downloadable' ,
'_featured' ,
'_downloadable_files' ,
'_wc_rating_count' ,
'_wc_average_rating' ,
'_wc_review_count' ,
'_variation_description' ,
2017-03-17 20:30:38 +00:00
'_thumbnail_id' ,
'_file_paths' ,
'_product_image_gallery' ,
'_product_version' ,
2017-03-17 20:37:38 +00:00
'_wp_old_slug' ,
'_edit_last' ,
'_edit_lock' ,
2016-11-21 23:48:49 +00:00
);
2016-11-11 14:31:15 +00:00
/**
* If we have already saved our extra data , don ' t do automatic / default handling .
*/
protected $extra_data_saved = false ;
2017-02-14 16:14:37 +00:00
/**
* Stores updated props .
* @ var array
*/
protected $updated_props = array ();
2016-11-11 14:31:15 +00:00
/*
|--------------------------------------------------------------------------
| CRUD Methods
|--------------------------------------------------------------------------
*/
/**
* Method to create a new product in the database .
2016-12-13 14:22:59 +00:00
*
2017-05-15 11:50:52 +00:00
* @ param WC_Product $product
2016-11-11 14:31:15 +00:00
*/
public function create ( & $product ) {
2016-12-13 14:22:59 +00:00
if ( ! $product -> get_date_created () ) {
2017-03-13 19:52:44 +00:00
$product -> set_date_created ( current_time ( 'timestamp' , true ) );
2016-12-13 14:22:59 +00:00
}
2016-11-11 14:31:15 +00:00
$id = wp_insert_post ( apply_filters ( 'woocommerce_new_product_data' , array (
'post_type' => 'product' ,
'post_status' => $product -> get_status () ? $product -> get_status () : 'publish' ,
'post_author' => get_current_user_id (),
'post_title' => $product -> get_name () ? $product -> get_name () : __ ( 'Product' , 'woocommerce' ),
'post_content' => $product -> get_description (),
'post_excerpt' => $product -> get_short_description (),
'post_parent' => $product -> get_parent_id (),
'comment_status' => $product -> get_reviews_allowed () ? 'open' : 'closed' ,
'ping_status' => 'closed' ,
'menu_order' => $product -> get_menu_order (),
2017-03-22 12:01:38 +00:00
'post_date' => gmdate ( 'Y-m-d H:i:s' , $product -> get_date_created ( 'edit' ) -> getOffsetTimestamp () ),
'post_date_gmt' => gmdate ( 'Y-m-d H:i:s' , $product -> get_date_created ( 'edit' ) -> getTimestamp () ),
2017-03-29 12:41:23 +00:00
'post_name' => $product -> get_slug ( 'edit' ),
2016-11-11 14:31:15 +00:00
) ), true );
if ( $id && ! is_wp_error ( $id ) ) {
$product -> set_id ( $id );
2017-02-14 16:14:37 +00:00
2017-02-15 14:40:57 +00:00
$this -> update_post_meta ( $product , true );
$this -> update_terms ( $product , true );
$this -> update_visibility ( $product , true );
$this -> update_attributes ( $product , true );
2016-11-11 14:31:15 +00:00
$this -> update_version_and_type ( $product );
2017-02-15 14:40:57 +00:00
$this -> handle_updated_props ( $product );
2017-02-14 16:14:37 +00:00
2016-12-19 16:40:53 +00:00
$product -> save_meta_data ();
$product -> apply_changes ();
2017-02-14 16:14:37 +00:00
2016-11-11 14:31:15 +00:00
$this -> clear_caches ( $product );
2016-12-19 16:40:53 +00:00
2016-12-20 13:37:20 +00:00
do_action ( 'woocommerce_new_product' , $id );
2016-11-11 14:31:15 +00:00
}
}
/**
* Method to read a product from the database .
2017-05-15 11:50:52 +00:00
* @ param WC_Product $product
2017-05-12 08:52:26 +00:00
* @ throws Exception
2016-11-11 14:31:15 +00:00
*/
public function read ( & $product ) {
$product -> set_defaults ();
2017-01-10 22:43:33 +00:00
if ( ! $product -> get_id () || ! ( $post_object = get_post ( $product -> get_id () ) ) || 'product' !== $post_object -> post_type ) {
2016-11-11 14:31:15 +00:00
throw new Exception ( __ ( 'Invalid product.' , 'woocommerce' ) );
}
$id = $product -> get_id ();
$product -> set_props ( array (
2017-02-09 16:43:48 +00:00
'name' => $post_object -> post_title ,
2016-11-11 14:31:15 +00:00
'slug' => $post_object -> post_name ,
2017-03-23 00:10:29 +00:00
'date_created' => 0 < $post_object -> post_date_gmt ? wc_string_to_timestamp ( $post_object -> post_date_gmt ) : null ,
'date_modified' => 0 < $post_object -> post_modified_gmt ? wc_string_to_timestamp ( $post_object -> post_modified_gmt ) : null ,
2016-11-11 14:31:15 +00:00
'status' => $post_object -> post_status ,
'description' => $post_object -> post_content ,
'short_description' => $post_object -> post_excerpt ,
'parent_id' => $post_object -> post_parent ,
'menu_order' => $post_object -> menu_order ,
'reviews_allowed' => 'open' === $post_object -> comment_status ,
) );
$this -> read_attributes ( $product );
2016-11-11 15:31:00 +00:00
$this -> read_downloads ( $product );
2016-12-08 10:56:45 +00:00
$this -> read_visibility ( $product );
2016-11-11 14:31:15 +00:00
$this -> read_product_data ( $product );
2017-01-24 20:18:35 +00:00
$this -> read_extra_data ( $product );
2016-11-11 14:31:15 +00:00
$product -> set_object_read ( true );
}
/**
* Method to update a product in the database .
2017-02-11 15:26:13 +00:00
*
2017-05-15 11:50:52 +00:00
* @ param WC_Product $product
2016-11-11 14:31:15 +00:00
*/
public function update ( & $product ) {
2017-04-05 21:39:41 +00:00
$product -> save_meta_data ();
2017-02-11 15:26:13 +00:00
$changes = $product -> get_changes ();
// Only update the post when the post data changes.
2017-03-29 12:41:23 +00:00
if ( array_intersect ( array ( 'description' , 'short_description' , 'name' , 'parent_id' , 'reviews_allowed' , 'status' , 'menu_order' , 'date_created' , 'date_modified' , 'slug' ), array_keys ( $changes ) ) ) {
2017-03-31 11:38:18 +00:00
$post_data = array (
'post_content' => $product -> get_description ( 'edit' ),
'post_excerpt' => $product -> get_short_description ( 'edit' ),
'post_title' => $product -> get_name ( 'edit' ),
'post_parent' => $product -> get_parent_id ( 'edit' ),
'comment_status' => $product -> get_reviews_allowed ( 'edit' ) ? 'open' : 'closed' ,
'post_status' => $product -> get_status ( 'edit' ) ? $product -> get_status ( 'edit' ) : 'publish' ,
'menu_order' => $product -> get_menu_order ( 'edit' ),
'post_name' => $product -> get_slug ( 'edit' ),
2017-05-25 11:09:59 +00:00
'post_type' => 'product' ,
2017-03-31 11:38:18 +00:00
);
if ( $product -> get_date_created ( 'edit' ) ) {
$post_data [ 'post_date' ] = gmdate ( 'Y-m-d H:i:s' , $product -> get_date_created ( 'edit' ) -> getOffsetTimestamp () );
$post_data [ 'post_date_gmt' ] = gmdate ( 'Y-m-d H:i:s' , $product -> get_date_created ( 'edit' ) -> getTimestamp () );
}
if ( isset ( $changes [ 'date_modified' ] ) && $product -> get_date_modified ( 'edit' ) ) {
$post_data [ 'post_modified' ] = gmdate ( 'Y-m-d H:i:s' , $product -> get_date_modified ( 'edit' ) -> getOffsetTimestamp () );
$post_data [ 'post_modified_gmt' ] = gmdate ( 'Y-m-d H:i:s' , $product -> get_date_modified ( 'edit' ) -> getTimestamp () );
} else {
$post_data [ 'post_modified' ] = current_time ( 'mysql' );
$post_data [ 'post_modified_gmt' ] = current_time ( 'mysql' , 1 );
}
2017-04-15 20:04:41 +00:00
/**
2017-04-15 20:18:24 +00:00
* When updating this object , to prevent infinite loops , use $wpdb
* to update data , since wp_update_post spawns more calls to the
* save_post action .
2017-04-15 20:04:41 +00:00
*
2017-04-15 20:18:24 +00:00
* This ensures hooks are fired by either WP itself ( admin screen save ),
* or an update purely from CRUD .
2017-04-15 20:04:41 +00:00
*/
if ( doing_action ( 'save_post' ) ) {
2017-04-15 20:18:24 +00:00
$GLOBALS [ 'wpdb' ] -> update ( $GLOBALS [ 'wpdb' ] -> posts , $post_data , array ( 'ID' => $product -> get_id () ) );
2017-04-15 20:04:41 +00:00
clean_post_cache ( $product -> get_id () );
} else {
wp_update_post ( array_merge ( array ( 'ID' => $product -> get_id () ), $post_data ) );
}
2017-04-05 21:39:41 +00:00
$product -> read_meta_data ( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook.
2017-02-11 15:26:13 +00:00
}
2016-11-11 14:31:15 +00:00
$this -> update_post_meta ( $product );
$this -> update_terms ( $product );
2016-12-08 10:56:45 +00:00
$this -> update_visibility ( $product );
2016-11-11 14:31:15 +00:00
$this -> update_attributes ( $product );
$this -> update_version_and_type ( $product );
2017-02-15 14:40:57 +00:00
$this -> handle_updated_props ( $product );
2017-02-14 16:14:37 +00:00
2016-12-19 16:40:53 +00:00
$product -> apply_changes ();
2017-02-14 16:14:37 +00:00
2016-11-11 14:31:15 +00:00
$this -> clear_caches ( $product );
2016-12-19 16:40:53 +00:00
2016-12-20 12:48:13 +00:00
do_action ( 'woocommerce_update_product' , $product -> get_id () );
2016-11-11 14:31:15 +00:00
}
/**
* Method to delete a product from the database .
2017-05-15 11:50:52 +00:00
* @ param WC_Product $product
2016-11-16 12:17:00 +00:00
* @ param array $args Array of args to pass to the delete method .
2016-11-11 14:31:15 +00:00
*/
2016-11-16 12:17:00 +00:00
public function delete ( & $product , $args = array () ) {
$id = $product -> get_id ();
2016-11-11 14:31:15 +00:00
$post_type = $product -> is_type ( 'variation' ) ? 'product_variation' : 'product' ;
2016-11-16 12:17:00 +00:00
$args = wp_parse_args ( $args , array (
'force_delete' => false ,
) );
2017-05-30 13:44:28 +00:00
if ( ! $id ) {
return ;
}
2016-11-16 12:17:00 +00:00
if ( $args [ 'force_delete' ] ) {
2017-05-30 13:44:28 +00:00
wp_delete_post ( $id );
2016-11-11 14:31:15 +00:00
$product -> set_id ( 0 );
2016-12-29 22:22:12 +00:00
do_action ( 'woocommerce_delete_' . $post_type , $id );
2016-11-11 14:31:15 +00:00
} else {
2017-05-30 13:44:28 +00:00
wp_trash_post ( $id );
2016-11-11 14:31:15 +00:00
$product -> set_status ( 'trash' );
2016-12-29 22:22:12 +00:00
do_action ( 'woocommerce_trash_' . $post_type , $id );
2016-11-11 14:31:15 +00:00
}
}
/*
|--------------------------------------------------------------------------
| Additional Methods
|--------------------------------------------------------------------------
*/
/**
* Read product data . Can be overridden by child classes to load other props .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
protected function read_product_data ( & $product ) {
2016-11-16 12:17:00 +00:00
$id = $product -> get_id ();
2016-11-11 15:31:00 +00:00
2016-11-16 12:17:00 +00:00
if ( '' === ( $review_count = get_post_meta ( $id , '_wc_review_count' , true ) ) ) {
WC_Comments :: get_review_count_for_product ( $product );
} else {
$product -> set_review_count ( $review_count );
2016-11-11 15:31:00 +00:00
}
2016-11-16 12:17:00 +00:00
if ( '' === ( $rating_counts = get_post_meta ( $id , '_wc_rating_count' , true ) ) ) {
WC_Comments :: get_rating_counts_for_product ( $product );
} else {
$product -> set_rating_counts ( $rating_counts );
2016-11-11 15:31:00 +00:00
}
2016-11-16 12:17:00 +00:00
if ( '' === ( $average_rating = get_post_meta ( $id , '_wc_average_rating' , true ) ) ) {
WC_Comments :: get_average_rating_for_product ( $product );
} else {
$product -> set_average_rating ( $average_rating );
2016-11-11 15:31:00 +00:00
}
2016-11-11 14:31:15 +00:00
$product -> set_props ( array (
'sku' => get_post_meta ( $id , '_sku' , true ),
'regular_price' => get_post_meta ( $id , '_regular_price' , true ),
'sale_price' => get_post_meta ( $id , '_sale_price' , true ),
'price' => get_post_meta ( $id , '_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 ),
'total_sales' => get_post_meta ( $id , 'total_sales' , true ),
'tax_status' => get_post_meta ( $id , '_tax_status' , true ),
'tax_class' => get_post_meta ( $id , '_tax_class' , 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 ),
'backorders' => get_post_meta ( $id , '_backorders' , true ),
'sold_individually' => get_post_meta ( $id , '_sold_individually' , true ),
'weight' => get_post_meta ( $id , '_weight' , true ),
'length' => get_post_meta ( $id , '_length' , true ),
'width' => get_post_meta ( $id , '_width' , true ),
'height' => get_post_meta ( $id , '_height' , true ),
'upsell_ids' => get_post_meta ( $id , '_upsell_ids' , true ),
'cross_sell_ids' => get_post_meta ( $id , '_crosssell_ids' , true ),
'purchase_note' => get_post_meta ( $id , '_purchase_note' , true ),
'default_attributes' => get_post_meta ( $id , '_default_attributes' , true ),
'category_ids' => $this -> get_term_ids ( $product , 'product_cat' ),
'tag_ids' => $this -> get_term_ids ( $product , 'product_tag' ),
'shipping_class_id' => current ( $this -> get_term_ids ( $product , 'product_shipping_class' ) ),
'virtual' => get_post_meta ( $id , '_virtual' , true ),
'downloadable' => get_post_meta ( $id , '_downloadable' , 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 ),
) );
2017-01-24 20:18:35 +00:00
}
2016-11-11 14:31:15 +00:00
2017-01-24 20:18:35 +00:00
/**
* Read extra data associated with the product , like button text or product URL for external products .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-01-24 20:18:35 +00:00
*/
protected function read_extra_data ( & $product ) {
2016-11-11 14:31:15 +00:00
foreach ( $product -> get_extra_data_keys () as $key ) {
$function = 'set_' . $key ;
if ( is_callable ( array ( $product , $function ) ) ) {
$product -> { $function }( get_post_meta ( $product -> get_id (), '_' . $key , true ) );
}
}
}
2016-12-08 10:56:45 +00:00
/**
* Convert visibility terms to props .
* Catalog visibility valid values are 'visible' , 'catalog' , 'search' , and 'hidden' .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-12-08 10:56:45 +00:00
*/
protected function read_visibility ( & $product ) {
$terms = get_the_terms ( $product -> get_id (), 'product_visibility' );
$term_names = is_array ( $terms ) ? wp_list_pluck ( $terms , 'name' ) : array ();
$featured = in_array ( 'featured' , $term_names );
$exclude_search = in_array ( 'exclude-from-search' , $term_names );
$exclude_catalog = in_array ( 'exclude-from-catalog' , $term_names );
if ( $exclude_search && $exclude_catalog ) {
$catalog_visibility = 'hidden' ;
} elseif ( $exclude_search ) {
$catalog_visibility = 'catalog' ;
} elseif ( $exclude_catalog ) {
$catalog_visibility = 'search' ;
} else {
$catalog_visibility = 'visible' ;
}
$product -> set_props ( array (
'featured' => $featured ,
'catalog_visibility' => $catalog_visibility ,
) );
}
2016-11-11 14:31:15 +00:00
/**
* Read attributes from post meta .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
protected function read_attributes ( & $product ) {
2017-03-31 11:10:20 +00:00
$meta_values = get_post_meta ( $product -> get_id (), '_product_attributes' , true );
2016-11-11 14:31:15 +00:00
2017-04-07 13:44:10 +00:00
if ( ! empty ( $meta_values ) && is_array ( $meta_values ) ) {
2016-11-11 14:31:15 +00:00
$attributes = array ();
foreach ( $meta_values as $meta_value ) {
2017-05-03 19:30:50 +00:00
$id = 0 ;
2017-04-07 09:59:59 +00:00
$meta_value = array_merge ( array (
'name' => '' ,
'value' => '' ,
'position' => 0 ,
'is_visible' => 0 ,
'is_variation' => 0 ,
'is_taxonomy' => 0 ,
), ( array ) $meta_value );
2017-05-03 19:30:50 +00:00
// Check if is a taxonomy attribute.
2016-11-11 14:31:15 +00:00
if ( ! empty ( $meta_value [ 'is_taxonomy' ] ) ) {
if ( ! taxonomy_exists ( $meta_value [ 'name' ] ) ) {
continue ;
}
2017-05-03 19:30:50 +00:00
$id = wc_attribute_taxonomy_id_by_name ( $meta_value [ 'name' ] );
2016-11-17 19:02:05 +00:00
$options = wc_get_object_terms ( $product -> get_id (), $meta_value [ 'name' ], 'term_id' );
2016-11-11 14:31:15 +00:00
} else {
$options = wc_get_text_attributes ( $meta_value [ 'value' ] );
}
2017-04-07 09:59:59 +00:00
2016-11-11 14:31:15 +00:00
$attribute = new WC_Product_Attribute ();
2017-05-03 19:30:50 +00:00
$attribute -> set_id ( $id );
2016-11-11 14:31:15 +00:00
$attribute -> set_name ( $meta_value [ 'name' ] );
$attribute -> set_options ( $options );
$attribute -> set_position ( $meta_value [ 'position' ] );
$attribute -> set_visible ( $meta_value [ 'is_visible' ] );
$attribute -> set_variation ( $meta_value [ 'is_variation' ] );
$attributes [] = $attribute ;
}
$product -> set_attributes ( $attributes );
}
}
2016-11-11 15:31:00 +00:00
/**
* Read downloads from post meta .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 15:31:00 +00:00
*/
protected function read_downloads ( & $product ) {
2017-03-31 11:10:20 +00:00
$meta_values = array_filter ( ( array ) get_post_meta ( $product -> get_id (), '_downloadable_files' , true ) );
2016-11-11 15:31:00 +00:00
if ( $meta_values ) {
$downloads = array ();
foreach ( $meta_values as $key => $value ) {
2017-04-12 13:11:04 +00:00
if ( ! isset ( $value [ 'name' ], $value [ 'file' ] ) ) {
continue ;
}
2016-11-11 15:31:00 +00:00
$download = new WC_Product_Download ();
$download -> set_id ( $key );
$download -> set_name ( $value [ 'name' ] ? $value [ 'name' ] : wc_get_filename_from_url ( $value [ 'file' ] ) );
$download -> set_file ( apply_filters ( 'woocommerce_file_download_path' , $value [ 'file' ], $product , $key ) );
$downloads [] = $download ;
}
$product -> set_downloads ( $downloads );
}
}
2016-11-11 14:31:15 +00:00
/**
* Helper method that updates all the post meta for a product based on it ' s settings in the WC_Product class .
*
* @ param WC_Product
2017-02-15 14:40:57 +00:00
* @ param bool Force update . Used during create .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
2017-02-15 14:40:57 +00:00
protected function update_post_meta ( & $product , $force = false ) {
2016-11-11 14:31:15 +00:00
$meta_key_to_props = array (
'_sku' => 'sku' ,
'_regular_price' => 'regular_price' ,
'_sale_price' => 'sale_price' ,
'_sale_price_dates_from' => 'date_on_sale_from' ,
'_sale_price_dates_to' => 'date_on_sale_to' ,
'total_sales' => 'total_sales' ,
'_tax_status' => 'tax_status' ,
'_tax_class' => 'tax_class' ,
'_manage_stock' => 'manage_stock' ,
'_backorders' => 'backorders' ,
'_sold_individually' => 'sold_individually' ,
'_weight' => 'weight' ,
'_length' => 'length' ,
'_width' => 'width' ,
'_height' => 'height' ,
'_upsell_ids' => 'upsell_ids' ,
'_crosssell_ids' => 'cross_sell_ids' ,
'_purchase_note' => 'purchase_note' ,
'_default_attributes' => 'default_attributes' ,
'_virtual' => 'virtual' ,
'_downloadable' => 'downloadable' ,
'_product_image_gallery' => 'gallery_image_ids' ,
'_download_limit' => 'download_limit' ,
'_download_expiry' => 'download_expiry' ,
'_thumbnail_id' => 'image_id' ,
'_stock' => 'stock_quantity' ,
'_stock_status' => 'stock_status' ,
2016-11-11 15:31:00 +00:00
'_wc_average_rating' => 'average_rating' ,
'_wc_rating_count' => 'rating_counts' ,
'_wc_review_count' => 'review_count' ,
2016-11-11 14:31:15 +00:00
);
2017-01-23 20:20:29 +00:00
// Make sure to take extra data (like product url or text for external products) into account.
$extra_data_keys = $product -> get_extra_data_keys ();
2017-02-15 14:40:57 +00:00
2017-01-23 20:20:29 +00:00
foreach ( $extra_data_keys as $key ) {
$meta_key_to_props [ '_' . $key ] = $key ;
}
2017-02-15 14:40:57 +00:00
$props_to_update = $force ? $meta_key_to_props : $this -> get_props_to_update ( $product , $meta_key_to_props );
2017-02-11 15:26:13 +00:00
2017-01-23 20:20:29 +00:00
foreach ( $props_to_update as $meta_key => $prop ) {
2016-11-11 14:31:15 +00:00
$value = $product -> { " get_ $prop " }( 'edit' );
switch ( $prop ) {
case 'virtual' :
case 'downloadable' :
case 'manage_stock' :
case 'sold_individually' :
$updated = update_post_meta ( $product -> get_id (), $meta_key , wc_bool_to_string ( $value ) );
break ;
case 'gallery_image_ids' :
$updated = update_post_meta ( $product -> get_id (), $meta_key , implode ( ',' , $value ) );
break ;
case 'image_id' :
if ( ! empty ( $value ) ) {
set_post_thumbnail ( $product -> get_id (), $value );
} else {
delete_post_meta ( $product -> get_id (), '_thumbnail_id' );
}
$updated = true ;
break ;
2017-03-13 21:36:31 +00:00
case 'date_on_sale_from' :
case 'date_on_sale_to' :
$updated = update_post_meta ( $product -> get_id (), $meta_key , $value ? $value -> getTimestamp () : '' );
break ;
2016-11-11 14:31:15 +00:00
default :
$updated = update_post_meta ( $product -> get_id (), $meta_key , $value );
break ;
}
if ( $updated ) {
2017-02-14 16:14:37 +00:00
$this -> updated_props [] = $prop ;
2016-11-11 14:31:15 +00:00
}
}
2017-02-11 15:26:13 +00:00
// Update extra data associated with the product like button text or product URL for external products.
if ( ! $this -> extra_data_saved ) {
foreach ( $extra_data_keys as $key ) {
if ( ! array_key_exists ( $key , $props_to_update ) ) {
continue ;
}
$function = 'get_' . $key ;
if ( is_callable ( array ( $product , $function ) ) ) {
if ( update_post_meta ( $product -> get_id (), '_' . $key , $product -> { $function }( 'edit' ) ) ) {
2017-02-14 16:14:37 +00:00
$this -> updated_props [] = $key ;
2017-02-11 15:26:13 +00:00
}
}
}
}
2017-02-15 14:40:57 +00:00
if ( $this -> update_downloads ( $product , $force ) ) {
2017-02-14 16:14:37 +00:00
$this -> updated_props [] = 'downloads' ;
2017-02-11 15:26:13 +00:00
}
}
/**
2017-02-15 14:40:57 +00:00
* Handle updated meta props after updating meta data .
2017-02-11 15:26:13 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-02-11 15:26:13 +00:00
* @ param WC_Product $product
*/
2017-02-14 16:26:40 +00:00
protected function handle_updated_props ( & $product ) {
2017-02-14 16:14:37 +00:00
if ( in_array ( 'date_on_sale_from' , $this -> updated_props ) || in_array ( 'date_on_sale_to' , $this -> updated_props ) || in_array ( 'regular_price' , $this -> updated_props ) || in_array ( 'sale_price' , $this -> updated_props ) ) {
2017-01-16 15:33:30 +00:00
if ( $product -> is_on_sale ( 'edit' ) ) {
update_post_meta ( $product -> get_id (), '_price' , $product -> get_sale_price ( 'edit' ) );
$product -> set_price ( $product -> get_sale_price ( 'edit' ) );
2016-11-11 14:31:15 +00:00
} else {
2017-01-16 15:33:30 +00:00
update_post_meta ( $product -> get_id (), '_price' , $product -> get_regular_price ( 'edit' ) );
$product -> set_price ( $product -> get_regular_price ( 'edit' ) );
2016-11-11 14:31:15 +00:00
}
}
2017-02-14 16:14:37 +00:00
if ( in_array ( 'stock_quantity' , $this -> updated_props ) ) {
2016-11-11 14:31:15 +00:00
do_action ( $product -> is_type ( 'variation' ) ? 'woocommerce_variation_set_stock' : 'woocommerce_product_set_stock' , $product );
}
2017-02-14 16:14:37 +00:00
if ( in_array ( 'stock_status' , $this -> updated_props ) ) {
2016-11-28 17:03:33 +00:00
do_action ( $product -> is_type ( 'variation' ) ? 'woocommerce_variation_set_stock_status' : 'woocommerce_product_set_stock_status' , $product -> get_id (), $product -> get_stock_status (), $product );
2016-11-11 14:31:15 +00:00
}
2017-02-15 14:40:57 +00:00
// Trigger action so 3rd parties can deal with updated props.
2017-02-14 16:14:37 +00:00
do_action ( 'woocommerce_product_object_updated_props' , $product , $this -> updated_props );
2017-02-15 14:40:57 +00:00
// After handling, we can reset the props array.
$this -> updated_props = array ();
2016-11-11 14:31:15 +00:00
}
/**
* For all stored terms in all taxonomies , save them to the DB .
*
* @ param WC_Product
2017-02-15 14:40:57 +00:00
* @ param bool Force update . Used during create .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
2017-02-15 14:40:57 +00:00
protected function update_terms ( & $product , $force = false ) {
2017-02-11 15:26:13 +00:00
$changes = $product -> get_changes ();
2017-02-15 14:40:57 +00:00
if ( $force || array_key_exists ( 'category_ids' , $changes ) ) {
2017-02-11 15:26:13 +00:00
wp_set_post_terms ( $product -> get_id (), $product -> get_category_ids ( 'edit' ), 'product_cat' , false );
}
2017-02-15 14:40:57 +00:00
if ( $force || array_key_exists ( 'tag_ids' , $changes ) ) {
2017-02-11 15:26:13 +00:00
wp_set_post_terms ( $product -> get_id (), $product -> get_tag_ids ( 'edit' ), 'product_tag' , false );
}
2017-02-15 14:40:57 +00:00
if ( $force || array_key_exists ( 'shipping_class_id' , $changes ) ) {
2017-02-11 15:26:13 +00:00
wp_set_post_terms ( $product -> get_id (), array ( $product -> get_shipping_class_id ( 'edit' ) ), 'product_shipping_class' , false );
}
2016-11-11 14:31:15 +00:00
}
2016-12-08 10:56:45 +00:00
/**
* Update visibility terms based on props .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-05-15 11:50:52 +00:00
*
* @ param WC_Product $product
* @ param bool $force Force update . Used during create .
2016-12-08 10:56:45 +00:00
*/
2017-02-15 14:40:57 +00:00
protected function update_visibility ( & $product , $force = false ) {
2017-02-11 15:35:20 +00:00
$changes = $product -> get_changes ();
2016-12-08 10:56:45 +00:00
2017-02-15 14:40:57 +00:00
if ( $force || array_intersect ( array ( 'featured' , 'stock_status' , 'average_rating' , 'catalog_visibility' ), array_keys ( $changes ) ) ) {
2017-02-11 15:35:20 +00:00
$terms = array ();
2016-12-08 10:56:45 +00:00
2017-02-11 15:35:20 +00:00
if ( $product -> get_featured () ) {
$terms [] = 'featured' ;
}
2016-12-08 10:56:45 +00:00
2017-02-11 15:35:20 +00:00
if ( 'outofstock' === $product -> get_stock_status () ) {
$terms [] = 'outofstock' ;
}
2017-05-10 18:21:38 +00:00
$rating = min ( 5 , round ( $product -> get_average_rating (), 0 ) );
if ( $rating > 0 ) {
$terms [] = 'rated-' . $rating ;
}
2016-12-08 10:56:45 +00:00
2017-02-11 15:35:20 +00:00
switch ( $product -> get_catalog_visibility () ) {
case 'hidden' :
$terms [] = 'exclude-from-search' ;
$terms [] = 'exclude-from-catalog' ;
break ;
case 'catalog' :
$terms [] = 'exclude-from-search' ;
break ;
case 'search' :
$terms [] = 'exclude-from-catalog' ;
break ;
}
if ( ! is_wp_error ( wp_set_post_terms ( $product -> get_id (), $terms , 'product_visibility' , false ) ) ) {
delete_transient ( 'wc_featured_products' );
do_action ( 'woocommerce_product_set_visibility' , $product -> get_id (), $product -> get_catalog_visibility () );
}
2016-12-08 10:56:45 +00:00
}
}
2016-11-11 14:31:15 +00:00
/**
* Update attributes which are a mix of terms and meta data .
*
* @ param WC_Product
2017-02-15 14:40:57 +00:00
* @ param bool Force update . Used during create .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
2017-02-15 14:40:57 +00:00
protected function update_attributes ( & $product , $force = false ) {
2017-02-11 15:35:20 +00:00
$changes = $product -> get_changes ();
2016-11-11 14:31:15 +00:00
2017-02-15 14:40:57 +00:00
if ( $force || array_key_exists ( 'attributes' , $changes ) ) {
2017-02-11 15:35:20 +00:00
$attributes = $product -> get_attributes ();
$meta_values = array ();
2016-11-11 14:31:15 +00:00
2017-02-11 15:35:20 +00:00
if ( $attributes ) {
foreach ( $attributes as $attribute_key => $attribute ) {
$value = '' ;
2016-11-11 14:31:15 +00:00
2017-02-11 15:35:20 +00:00
if ( is_null ( $attribute ) ) {
if ( taxonomy_exists ( $attribute_key ) ) {
// Handle attributes that have been unset.
wp_set_object_terms ( $product -> get_id (), array (), $attribute_key );
}
continue ;
2016-11-11 14:31:15 +00:00
2017-02-11 15:35:20 +00:00
} elseif ( $attribute -> is_taxonomy () ) {
wp_set_object_terms ( $product -> get_id (), wp_list_pluck ( $attribute -> get_terms (), 'term_id' ), $attribute -> get_name () );
} else {
$value = wc_implode_text_attributes ( $attribute -> get_options () );
}
// Store in format WC uses in meta.
$meta_values [ $attribute_key ] = array (
'name' => $attribute -> get_name (),
'value' => $value ,
'position' => $attribute -> get_position (),
'is_visible' => $attribute -> get_visible () ? 1 : 0 ,
'is_variation' => $attribute -> get_variation () ? 1 : 0 ,
'is_taxonomy' => $attribute -> is_taxonomy () ? 1 : 0 ,
);
}
2016-11-11 14:31:15 +00:00
}
2017-02-11 15:35:20 +00:00
update_post_meta ( $product -> get_id (), '_product_attributes' , $meta_values );
2016-11-11 14:31:15 +00:00
}
}
2016-11-11 15:31:00 +00:00
/**
* Update downloads .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-02-11 15:35:20 +00:00
* @ param WC_Product $product
2017-02-15 14:40:57 +00:00
* @ param bool Force update . Used during create .
2017-02-11 15:35:20 +00:00
* @ return bool If updated or not .
2016-11-11 15:31:00 +00:00
*/
2017-02-15 14:40:57 +00:00
protected function update_downloads ( & $product , $force = false ) {
2017-02-11 15:35:20 +00:00
$changes = $product -> get_changes ();
2016-11-11 15:31:00 +00:00
2017-02-15 14:40:57 +00:00
if ( $force || array_key_exists ( 'downloads' , $changes ) ) {
2017-02-11 15:35:20 +00:00
$downloads = $product -> get_downloads ();
$meta_values = array ();
if ( $downloads ) {
foreach ( $downloads as $key => $download ) {
// Store in format WC uses in meta.
$meta_values [ $key ] = $download -> get_data ();
}
2016-11-11 15:31:00 +00:00
}
2016-11-18 19:29:37 +00:00
2017-02-11 15:35:20 +00:00
if ( $product -> is_type ( 'variation' ) ) {
do_action ( 'woocommerce_process_product_file_download_paths' , $product -> get_parent_id (), $product -> get_id (), $downloads );
} else {
do_action ( 'woocommerce_process_product_file_download_paths' , $product -> get_id (), 0 , $downloads );
}
2016-11-18 19:29:37 +00:00
2017-02-11 15:35:20 +00:00
return update_post_meta ( $product -> get_id (), '_downloadable_files' , $meta_values );
}
return false ;
2016-11-11 15:31:00 +00:00
}
2016-11-11 14:31:15 +00:00
/**
* Make sure we store the product type and version ( to track data changes ) .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
protected function update_version_and_type ( & $product ) {
2017-01-11 11:48:31 +00:00
$old_type = WC_Product_Factory :: get_product_type ( $product -> get_id () );
$new_type = $product -> get_type ();
wp_set_object_terms ( $product -> get_id (), $new_type , 'product_type' );
2016-11-11 14:31:15 +00:00
update_post_meta ( $product -> get_id (), '_product_version' , WC_VERSION );
2017-01-11 11:48:31 +00:00
// Action for the transition.
if ( $old_type !== $new_type ) {
do_action ( 'woocommerce_product_type_changed' , $product , $old_type , $new_type );
}
2016-11-11 14:31:15 +00:00
}
/**
* Clear any caches .
*
* @ param WC_Product
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
protected function clear_caches ( & $product ) {
wc_delete_product_transients ( $product -> get_id () );
}
/*
|--------------------------------------------------------------------------
| wc - product - functions . php methods
|--------------------------------------------------------------------------
*/
/**
* Returns an array of on sale products , as an array of objects with an
* ID and parent_id present . Example : $return [ 0 ] -> id , $return [ 0 ] -> parent_id .
*
* @ return array
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
public function get_on_sale_products () {
global $wpdb ;
2017-02-08 17:04:13 +00:00
$decimals = absint ( wc_get_price_decimals () );
2016-11-11 14:31:15 +00:00
return $wpdb -> get_results ( "
SELECT post . ID as id , post . post_parent as parent_id FROM `$wpdb->posts` AS post
LEFT JOIN `$wpdb->postmeta` AS meta ON post . ID = meta . post_id
LEFT JOIN `$wpdb->postmeta` AS meta2 ON post . ID = meta2 . post_id
WHERE post . post_type IN ( 'product' , 'product_variation' )
AND post . post_status = 'publish'
AND meta . meta_key = '_sale_price'
AND meta2 . meta_key = '_price'
AND CAST ( meta . meta_value AS DECIMAL ) >= 0
AND CAST ( meta . meta_value AS CHAR ) != ''
2017-02-08 17:04:13 +00:00
AND CAST ( meta . meta_value AS DECIMAL ( 10 , { $decimals } ) ) = CAST ( meta2 . meta_value AS DECIMAL ( 10 , { $decimals } ) )
2016-11-11 14:31:15 +00:00
GROUP BY post . ID ;
" );
}
/**
* Returns a list of product IDs ( id as key => parent as value ) that are
* featured . Uses get_posts instead of wc_get_products since we want
* some extra meta queries and ALL products ( posts_per_page = - 1 ) .
*
* @ return array
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
*/
public function get_featured_product_ids () {
2016-12-08 17:01:39 +00:00
$product_visibility_term_ids = wc_get_product_visibility_term_ids ();
2016-11-11 14:31:15 +00:00
return get_posts ( array (
'post_type' => array ( 'product' , 'product_variation' ),
'posts_per_page' => - 1 ,
'post_status' => 'publish' ,
2016-12-08 10:56:45 +00:00
'tax_query' => array (
'relation' => 'AND' ,
2016-11-11 14:31:15 +00:00
array (
2016-12-08 10:56:45 +00:00
'taxonomy' => 'product_visibility' ,
2016-12-08 17:01:39 +00:00
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'featured' ] ),
2016-11-11 14:31:15 +00:00
),
array (
2016-12-08 10:56:45 +00:00
'taxonomy' => 'product_visibility' ,
2016-12-08 17:01:39 +00:00
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'exclude-from-catalog' ] ),
2016-12-08 10:56:45 +00:00
'operator' => 'NOT IN' ,
2016-11-11 14:31:15 +00:00
),
),
'fields' => 'id=>parent' ,
) );
}
/**
* Check if product sku is found for any other product IDs .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ param int $product_id
* @ param string $sku Will be slashed to work around https :// core . trac . wordpress . org / ticket / 27421
* @ return bool
*/
public function is_existing_sku ( $product_id , $sku ) {
global $wpdb ;
return $wpdb -> get_var ( $wpdb -> prepare ( "
SELECT $wpdb -> posts . ID
FROM $wpdb -> posts
LEFT JOIN $wpdb -> postmeta ON ( $wpdb -> posts . ID = $wpdb -> postmeta . post_id )
WHERE $wpdb -> posts . post_type IN ( 'product' , 'product_variation' )
2017-04-19 10:32:49 +00:00
AND $wpdb -> posts . post_status != 'trash'
2016-11-11 14:31:15 +00:00
AND $wpdb -> postmeta . meta_key = '_sku' AND $wpdb -> postmeta . meta_value = '%s'
AND $wpdb -> postmeta . post_id <> % d LIMIT 1
" , wp_slash( $sku ), $product_id ) );
}
/**
* Return product ID based on SKU .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ param string $sku
* @ return int
*/
public function get_product_id_by_sku ( $sku ) {
global $wpdb ;
return $wpdb -> get_var ( $wpdb -> prepare ( "
SELECT posts . ID
FROM $wpdb -> posts AS posts
LEFT JOIN $wpdb -> postmeta AS postmeta ON ( posts . ID = postmeta . post_id )
WHERE posts . post_type IN ( 'product' , 'product_variation' )
2017-04-19 17:27:19 +00:00
AND posts . post_status != 'trash'
2017-04-19 00:32:52 +00:00
AND postmeta . meta_key = '_sku'
AND postmeta . meta_value = '%s'
2016-11-11 14:31:15 +00:00
LIMIT 1
" , $sku ) );
}
/**
* Returns an array of IDs of products that have sales starting soon .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ return array
*/
public function get_starting_sales () {
global $wpdb ;
return $wpdb -> get_col ( $wpdb -> prepare ( "
SELECT postmeta . post_id FROM { $wpdb -> postmeta } as postmeta
LEFT JOIN { $wpdb -> postmeta } as postmeta_2 ON postmeta . post_id = postmeta_2 . post_id
LEFT JOIN { $wpdb -> postmeta } as postmeta_3 ON postmeta . post_id = postmeta_3 . post_id
WHERE postmeta . meta_key = '_sale_price_dates_from'
AND postmeta_2 . meta_key = '_price'
AND postmeta_3 . meta_key = '_sale_price'
AND postmeta . meta_value > 0
AND postmeta . meta_value < % s
AND postmeta_2 . meta_value != postmeta_3 . meta_value
2017-03-13 19:52:44 +00:00
" , current_time( 'timestamp', true ) ) );
2016-11-11 14:31:15 +00:00
}
/**
* Returns an array of IDs of products that have sales which are due to end .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ return array
*/
public function get_ending_sales () {
global $wpdb ;
return $wpdb -> get_col ( $wpdb -> prepare ( "
SELECT postmeta . post_id FROM { $wpdb -> postmeta } as postmeta
LEFT JOIN { $wpdb -> postmeta } as postmeta_2 ON postmeta . post_id = postmeta_2 . post_id
LEFT JOIN { $wpdb -> postmeta } as postmeta_3 ON postmeta . post_id = postmeta_3 . post_id
WHERE postmeta . meta_key = '_sale_price_dates_to'
AND postmeta_2 . meta_key = '_price'
AND postmeta_3 . meta_key = '_regular_price'
AND postmeta . meta_value > 0
AND postmeta . meta_value < % s
AND postmeta_2 . meta_value != postmeta_3 . meta_value
2017-03-13 19:52:44 +00:00
" , current_time( 'timestamp', true ) ) );
2016-11-11 14:31:15 +00:00
}
/**
* Find a matching ( enabled ) variation within a variable product .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ param WC_Product $product Variable product .
* @ param array $match_attributes Array of attributes we want to try to match .
* @ return int Matching variation ID or 0.
*/
public function find_matching_product_variation ( $product , $match_attributes = array () ) {
$query_args = array (
'post_parent' => $product -> get_id (),
'post_type' => 'product_variation' ,
'orderby' => 'menu_order' ,
'order' => 'ASC' ,
'fields' => 'ids' ,
'post_status' => 'publish' ,
'numberposts' => 1 ,
'meta_query' => array (),
);
// Allow large queries in case user has many variations or attributes.
$GLOBALS [ 'wpdb' ] -> query ( 'SET SESSION SQL_BIG_SELECTS=1' );
foreach ( $product -> get_attributes () as $attribute ) {
if ( ! $attribute -> get_variation () ) {
continue ;
}
$attribute_field_name = 'attribute_' . sanitize_title ( $attribute -> get_name () );
if ( ! isset ( $match_attributes [ $attribute_field_name ] ) ) {
return 0 ;
}
2017-03-06 14:53:32 +00:00
// Note not wc_clean here to prevent removal of entities.
$value = $match_attributes [ $attribute_field_name ];
2016-11-11 14:31:15 +00:00
$query_args [ 'meta_query' ][] = array (
'relation' => 'OR' ,
array (
'key' => $attribute_field_name ,
'value' => array ( '' , $value ),
'compare' => 'IN' ,
),
array (
'key' => $attribute_field_name ,
'compare' => 'NOT EXISTS' ,
)
);
}
$variations = get_posts ( $query_args );
if ( $variations && ! is_wp_error ( $variations ) ) {
return current ( $variations );
} elseif ( version_compare ( get_post_meta ( $product -> get_id (), '_product_version' , true ), '2.4.0' , '<' ) ) {
/**
* Pre 2.4 handling where 'slugs' were saved instead of the full text attribute .
* Fallback is here because there are cases where data will be 'synced' but the product version will remain the same .
*/
return ( array_map ( 'sanitize_title' , $match_attributes ) === $match_attributes ) ? 0 : $this -> find_matching_product_variation ( $product , array_map ( 'sanitize_title' , $match_attributes ) );
}
return 0 ;
}
2016-12-08 14:28:51 +00:00
/**
* Make sure all variations have a sort order set so they can be reordered correctly .
*
* @ param int $parent_id
*/
public function sort_all_product_variations ( $parent_id ) {
global $wpdb ;
$ids = $wpdb -> get_col ( $wpdb -> prepare ( " SELECT ID FROM { $wpdb -> posts } WHERE post_type='product_variation' AND post_parent=%d AND post_status='publish' ORDER BY menu_order ASC, ID ASC " , $parent_id ) );
$index = 0 ;
foreach ( $ids as $id ) {
$wpdb -> update ( $wpdb -> posts , array ( 'menu_order' => ( $index ++ ) ), array ( 'ID' => absint ( $id ) ) );
}
}
2016-11-11 14:31:15 +00:00
/**
* Return a list of related products ( using data like categories and IDs ) .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-11 14:31:15 +00:00
* @ param array $cats_array List of categories IDs .
* @ param array $tags_array List of tags IDs .
* @ param array $exclude_ids Excluded IDs .
* @ param int $limit Limit of results .
* @ param int $product_id
* @ return array
*/
public function get_related_products ( $cats_array , $tags_array , $exclude_ids , $limit , $product_id ) {
global $wpdb ;
return $wpdb -> get_col ( implode ( ' ' , apply_filters ( 'woocommerce_product_related_posts_query' , $this -> get_related_products_query ( $cats_array , $tags_array , $exclude_ids , $limit + 10 ), $product_id ) ) );
}
/**
* Builds the related posts query .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-05-15 11:50:52 +00:00
*
2016-11-11 14:31:15 +00:00
* @ param array $cats_array List of categories IDs .
* @ param array $tags_array List of tags IDs .
* @ param array $exclude_ids Excluded IDs .
* @ param int $limit Limit of results .
2017-05-15 11:50:52 +00:00
*
* @ return array
2016-11-11 14:31:15 +00:00
*/
2016-11-16 12:17:00 +00:00
public function get_related_products_query ( $cats_array , $tags_array , $exclude_ids , $limit ) {
2016-11-11 14:31:15 +00:00
global $wpdb ;
2017-05-08 13:15:16 +00:00
$include_term_ids = array_merge ( $cats_array , $tags_array );
$exclude_term_ids = array ();
2016-12-08 17:01:39 +00:00
$product_visibility_term_ids = wc_get_product_visibility_term_ids ();
if ( $product_visibility_term_ids [ 'exclude-from-catalog' ] ) {
2017-05-08 13:15:16 +00:00
$exclude_term_ids [] = $product_visibility_term_ids [ 'exclude-from-catalog' ];
2016-12-08 10:56:45 +00:00
}
2016-12-08 17:01:39 +00:00
if ( 'yes' === get_option ( 'woocommerce_hide_out_of_stock_items' ) && $product_visibility_term_ids [ 'outofstock' ] ) {
2017-05-08 13:15:16 +00:00
$exclude_term_ids [] = $product_visibility_term_ids [ 'outofstock' ];
2016-11-11 14:31:15 +00:00
}
2017-05-08 13:15:16 +00:00
$query = array (
'fields' => "
SELECT DISTINCT ID FROM { $wpdb -> posts } p
" ,
'join' => '' ,
'where' => "
WHERE 1 = 1
AND p . post_status = 'publish'
AND p . post_type = 'product'
" ,
'limits' => "
LIMIT " . absint( $limit ) . "
" ,
);
2016-11-11 14:31:15 +00:00
2017-05-08 18:08:59 +00:00
if ( count ( $exclude_term_ids ) ) {
2017-05-08 13:15:16 +00:00
$query [ 'join' ] .= " LEFT JOIN ( SELECT object_id FROM { $wpdb -> term_relationships } WHERE term_taxonomy_id IN ( " . implode ( ',' , array_map ( 'absint' , $exclude_term_ids ) ) . " ) ) AS exclude_join ON exclude_join.object_id = p.ID " ;
$query [ 'where' ] .= " AND exclude_join.object_id IS NULL " ;
}
2016-11-11 14:31:15 +00:00
2017-05-08 18:08:59 +00:00
if ( count ( $include_term_ids ) ) {
2017-05-09 18:17:24 +00:00
$query [ 'join' ] .= " INNER JOIN ( SELECT object_id FROM { $wpdb -> term_relationships } INNER JOIN { $wpdb -> term_taxonomy } using( term_taxonomy_id ) WHERE term_id IN ( " . implode ( ',' , array_map ( 'absint' , $include_term_ids ) ) . " ) ) AS include_join ON include_join.object_id = p.ID " ;
2016-11-11 14:31:15 +00:00
}
2017-05-08 18:08:59 +00:00
if ( count ( $exclude_ids ) ) {
2017-05-08 13:15:16 +00:00
$query [ 'where' ] .= " AND p.ID NOT IN ( " . implode ( ',' , array_map ( 'absint' , $exclude_ids ) ) . " ) " ;
}
2016-11-11 14:31:15 +00:00
return $query ;
}
/**
* Update a product ' s stock amount directly .
*
* Uses queries rather than update_post_meta so we can do this in one query ( to avoid stock issues ) .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0 this supports set , increase and decrease .
2016-11-11 14:31:15 +00:00
* @ param int
* @ param int | null $stock_quantity
* @ param string $operation set , increase and decrease .
*/
2016-11-16 12:17:00 +00:00
public function update_product_stock ( $product_id_with_stock , $stock_quantity = null , $operation = 'set' ) {
2016-11-11 14:31:15 +00:00
global $wpdb ;
add_post_meta ( $product_id_with_stock , '_stock' , 0 , true );
// Update stock in DB directly
switch ( $operation ) {
case 'increase' :
$wpdb -> query ( $wpdb -> prepare ( " UPDATE { $wpdb -> postmeta } SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock' " , $stock_quantity , $product_id_with_stock ) );
break ;
case 'decrease' :
$wpdb -> query ( $wpdb -> prepare ( " UPDATE { $wpdb -> postmeta } SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock' " , $stock_quantity , $product_id_with_stock ) );
break ;
default :
$wpdb -> query ( $wpdb -> prepare ( " UPDATE { $wpdb -> postmeta } SET meta_value = %f WHERE post_id = %d AND meta_key='_stock' " , $stock_quantity , $product_id_with_stock ) );
break ;
}
wp_cache_delete ( $product_id_with_stock , 'post_meta' );
}
2017-02-10 23:41:53 +00:00
/**
* Update a product ' s sale count directly .
*
* Uses queries rather than update_post_meta so we can do this in one query for performance .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0 this supports set , increase and decrease .
2017-02-10 23:41:53 +00:00
* @ param int
* @ param int | null $quantity
* @ param string $operation set , increase and decrease .
*/
public function update_product_sales ( $product_id , $quantity = null , $operation = 'set' ) {
global $wpdb ;
add_post_meta ( $product_id , 'total_sales' , 0 , true );
// Update stock in DB directly
switch ( $operation ) {
case 'increase' :
$wpdb -> query ( $wpdb -> prepare ( " UPDATE { $wpdb -> postmeta } SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='total_sales' " , $quantity , $product_id ) );
break ;
case 'decrease' :
$wpdb -> query ( $wpdb -> prepare ( " UPDATE { $wpdb -> postmeta } SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='total_sales' " , $quantity , $product_id ) );
break ;
default :
$wpdb -> query ( $wpdb -> prepare ( " UPDATE { $wpdb -> postmeta } SET meta_value = %f WHERE post_id = %d AND meta_key='total_sales' " , $quantity , $product_id ) );
break ;
}
wp_cache_delete ( $product_id , 'post_meta' );
}
2016-11-16 12:17:00 +00:00
/**
* Update a products average rating meta .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-16 12:17:00 +00:00
* @ param WC_Product $product
*/
public function update_average_rating ( $product ) {
update_post_meta ( $product -> get_id (), '_wc_average_rating' , $product -> get_average_rating ( 'edit' ) );
}
/**
* Update a products review count meta .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-16 12:17:00 +00:00
* @ param WC_Product $product
*/
public function update_review_count ( $product ) {
update_post_meta ( $product -> get_id (), '_wc_review_count' , $product -> get_review_count ( 'edit' ) );
}
/**
* Update a products rating counts .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-16 12:17:00 +00:00
* @ param WC_Product $product
*/
public function update_rating_counts ( $product ) {
update_post_meta ( $product -> get_id (), '_wc_rating_count' , $product -> get_rating_counts ( 'edit' ) );
}
2016-11-16 20:48:42 +00:00
/**
* Get shipping class ID by slug .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-16 20:48:42 +00:00
* @ param $slug string
* @ return int | false
*/
public function get_shipping_class_id_by_slug ( $slug ) {
$shipping_class_term = get_term_by ( 'slug' , $slug , 'product_shipping_class' );
if ( $shipping_class_term ) {
return $shipping_class_term -> term_id ;
} else {
return false ;
}
}
2016-11-21 12:25:00 +00:00
/**
* Returns an array of products .
*
* @ param array $args @ see wc_get_products
2017-05-15 11:50:52 +00:00
*
* @ return array | object
2016-11-21 12:25:00 +00:00
*/
public function get_products ( $args = array () ) {
/**
* Generate WP_Query args .
*/
$wp_query_args = array (
'post_status' => $args [ 'status' ],
'posts_per_page' => $args [ 'limit' ],
'meta_query' => array (),
'orderby' => $args [ 'orderby' ],
'order' => $args [ 'order' ],
'tax_query' => array (),
);
2017-05-10 21:16:53 +00:00
if ( 'variation' === $args [ 'type' ] ) {
$wp_query_args [ 'post_type' ] = 'product_variation' ;
} elseif ( is_array ( $args [ 'type' ] ) && in_array ( 'variation' , $args [ 'type' ] ) ) {
$wp_query_args [ 'post_type' ] = array ( 'product_variation' , 'product' );
$wp_query_args [ 'tax_query' ][] = array (
'relation' => 'OR' ,
array (
'taxonomy' => 'product_type' ,
'field' => 'slug' ,
'terms' => $args [ 'type' ],
),
array (
'taxonomy' => 'product_type' ,
'field' => 'id' ,
'operator' => 'NOT EXISTS' ,
),
);
} else {
$wp_query_args [ 'post_type' ] = 'product' ;
2016-11-21 12:25:00 +00:00
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_type' ,
'field' => 'slug' ,
'terms' => $args [ 'type' ],
);
}
2017-05-10 21:16:53 +00:00
// Do not load unnecessary post data if the user only wants IDs.
if ( 'ids' === $args [ 'return' ] ) {
$wp_query_args [ 'fields' ] = 'ids' ;
}
2016-11-21 12:25:00 +00:00
if ( ! empty ( $args [ 'sku' ] ) ) {
$wp_query_args [ 'meta_query' ][] = array (
'key' => '_sku' ,
'value' => $args [ 'sku' ],
'compare' => 'LIKE' ,
);
}
if ( ! empty ( $args [ 'category' ] ) ) {
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_cat' ,
'field' => 'slug' ,
'terms' => $args [ 'category' ],
);
}
if ( ! empty ( $args [ 'tag' ] ) ) {
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_tag' ,
'field' => 'slug' ,
'terms' => $args [ 'tag' ],
);
}
if ( ! empty ( $args [ 'shipping_class' ] ) ) {
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_shipping_class' ,
'field' => 'slug' ,
'terms' => $args [ 'shipping_class' ],
);
}
if ( ! is_null ( $args [ 'parent' ] ) ) {
$wp_query_args [ 'post_parent' ] = absint ( $args [ 'parent' ] );
}
if ( ! is_null ( $args [ 'offset' ] ) ) {
$wp_query_args [ 'offset' ] = absint ( $args [ 'offset' ] );
} else {
$wp_query_args [ 'paged' ] = absint ( $args [ 'page' ] );
}
2017-01-09 12:10:47 +00:00
if ( ! empty ( $args [ 'include' ] ) ) {
$wp_query_args [ 'post__in' ] = array_map ( 'absint' , $args [ 'include' ] );
}
2016-11-21 12:25:00 +00:00
if ( ! empty ( $args [ 'exclude' ] ) ) {
$wp_query_args [ 'post__not_in' ] = array_map ( 'absint' , $args [ 'exclude' ] );
}
if ( ! $args [ 'paginate' ] ) {
$wp_query_args [ 'no_found_rows' ] = true ;
}
// Get results.
$products = new WP_Query ( $wp_query_args );
if ( 'objects' === $args [ 'return' ] ) {
2017-04-25 13:03:30 +00:00
$return = array_filter ( array_map ( 'wc_get_product' , $products -> posts ) );
2016-11-21 12:25:00 +00:00
} else {
$return = $products -> posts ;
}
if ( $args [ 'paginate' ] ) {
return ( object ) array (
'products' => $return ,
'total' => $products -> found_posts ,
'max_num_pages' => $products -> max_num_pages ,
);
} else {
return $return ;
}
}
2016-11-24 11:50:34 +00:00
/**
* Search product data for a term and return ids .
*
* @ param string $term
* @ param string $type of product
* @ param bool $include_variations in search or not
* @ return array of ids
*/
public function search_products ( $term , $type = '' , $include_variations = false ) {
global $wpdb ;
$search_fields = array_map ( 'wc_clean' , apply_filters ( 'woocommerce_product_search_fields' , array (
'_sku' ,
) ) );
2017-04-03 11:04:59 +00:00
$like_term = '%' . $wpdb -> esc_like ( $term ) . '%' ;
$post_types = $include_variations ? array ( 'product' , 'product_variation' ) : array ( 'product' );
$post_statuses = current_user_can ( 'edit_private_products' ) ? array ( 'private' , 'publish' ) : array ( 'publish' );
$type_join = '' ;
$type_where = '' ;
2016-11-24 11:50:34 +00:00
if ( $type ) {
if ( in_array ( $type , array ( 'virtual' , 'downloadable' ) ) ) {
$type_join = " LEFT JOIN { $wpdb -> postmeta } postmeta_type ON posts.ID = postmeta_type.post_id " ;
$type_where = " AND ( postmeta_type.meta_key = '_ { $type } ' AND postmeta_type.meta_value = 'yes' ) " ;
}
}
$product_ids = $wpdb -> get_col (
$wpdb -> prepare ( "
SELECT DISTINCT posts . ID FROM { $wpdb -> posts } posts
LEFT JOIN { $wpdb -> postmeta } postmeta ON posts . ID = postmeta . post_id
$type_join
WHERE (
posts . post_title LIKE % s
OR posts . post_content LIKE % s
OR (
postmeta . meta_key = '_sku' AND postmeta . meta_value LIKE % s
)
)
AND posts . post_type IN ( '" . implode( "' , '", $post_types ) . "' )
2017-04-03 11:04:59 +00:00
AND posts . post_status IN ( '" . implode( "' , '", $post_statuses ) . "' )
2016-11-24 11:50:34 +00:00
$type_where
ORDER BY posts . post_parent ASC , posts . post_title ASC
" ,
$like_term ,
$like_term ,
$like_term
)
);
if ( is_numeric ( $term ) ) {
2016-12-28 14:41:21 +00:00
$post_id = absint ( $term );
$post_type = get_post_type ( $post_id );
2017-01-03 11:24:36 +00:00
if ( 'product_variation' === $post_type && $include_variations ) {
$product_ids [] = $post_id ;
} elseif ( 'product' === $post_type ) {
$product_ids [] = $post_id ;
2016-12-28 14:41:21 +00:00
}
2017-01-03 11:24:36 +00:00
$product_ids [] = wp_get_post_parent_id ( $post_id );
2016-11-24 11:50:34 +00:00
}
return wp_parse_id_list ( $product_ids );
}
2017-02-08 19:18:39 +00:00
/**
* Get the product type based on product ID .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-02-08 19:18:39 +00:00
* @ param int $product_id
* @ return bool | string
*/
public function get_product_type ( $product_id ) {
$post_type = get_post_type ( $product_id );
if ( 'product_variation' === $post_type ) {
return 'variation' ;
} elseif ( 'product' === $post_type ) {
$terms = get_the_terms ( $product_id , 'product_type' );
return ! empty ( $terms ) ? sanitize_title ( current ( $terms ) -> name ) : 'simple' ;
} else {
return false ;
}
}
2016-11-11 14:31:15 +00:00
}