2016-11-11 14:31:15 +00:00
< ? php
2018-01-03 17:57:33 +00:00
/**
* WC_Product_Data_Store_CPT class file .
*
* @ package WooCommerce / Classes
*/
2016-11-11 14:31:15 +00:00
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
*/
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' ,
2017-07-25 17:01:24 +00:00
'_download_limit' ,
'_download_expiry' ,
2016-11-21 23:48:49 +00:00
'_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 .
2018-01-03 17:57:33 +00:00
*
* @ var bool
2016-11-11 14:31:15 +00:00
*/
protected $extra_data_saved = false ;
2017-02-14 16:14:37 +00:00
/**
* Stores updated props .
2018-01-03 16:44:37 +00:00
*
2017-02-14 16:14:37 +00:00
* @ 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
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
2016-11-11 14:31:15 +00:00
*/
public function create ( & $product ) {
2017-08-22 10:43:48 +00:00
if ( ! $product -> get_date_created ( 'edit' ) ) {
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
2018-01-03 16:44:37 +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 (),
'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 () ),
'post_name' => $product -> get_slug ( 'edit' ),
)
), true
);
2016-11-11 14:31:15 +00:00
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 .
2018-01-03 16:44:37 +00:00
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
* @ throws Exception If invalid product .
2016-11-11 14:31:15 +00:00
*/
public function read ( & $product ) {
$product -> set_defaults ();
2018-01-03 17:57:33 +00:00
$post_object = get_post ( $product -> get_id () );
2016-11-11 14:31:15 +00:00
2018-01-03 17:57:33 +00:00
if ( ! $product -> get_id () || ! $post_object || 'product' !== $post_object -> post_type ) {
2016-11-11 14:31:15 +00:00
throw new Exception ( __ ( 'Invalid product.' , 'woocommerce' ) );
}
2018-01-03 16:44:37 +00:00
$product -> set_props (
array (
'name' => $post_object -> post_title ,
'slug' => $post_object -> post_name ,
'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 ,
'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 ,
)
);
2016-11-11 14:31:15 +00:00
$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
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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.
2018-04-03 11:51:58 +00:00
} else { // Only update post modified time to record this save event.
$GLOBALS [ 'wpdb' ] -> update (
$GLOBALS [ 'wpdb' ] -> posts ,
array (
'post_modified' => current_time ( 'mysql' ),
'post_modified_gmt' => current_time ( 'mysql' , 1 ),
),
array (
'ID' => $product -> get_id (),
)
);
clean_post_cache ( $product -> get_id () );
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 .
2018-01-03 16:44:37 +00:00
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
2018-01-03 16:44:37 +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' ;
2018-01-03 16:44:37 +00:00
$args = wp_parse_args (
$args , array (
'force_delete' => false ,
)
);
2016-11-16 12:17:00 +00:00
2017-05-30 13:44:28 +00:00
if ( ! $id ) {
return ;
}
2016-11-16 12:17:00 +00:00
if ( $args [ 'force_delete' ] ) {
2018-03-29 12:52:19 +00:00
do_action ( 'woocommerce_before_delete_' . $post_type , $id );
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 .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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 ) {
2018-01-03 17:57:33 +00:00
$id = $product -> get_id ();
$review_count = get_post_meta ( $id , '_wc_review_count' , true );
$rating_counts = get_post_meta ( $id , '_wc_rating_count' , true );
$average_rating = get_post_meta ( $id , '_wc_average_rating' , true );
2016-11-11 15:31:00 +00:00
2018-01-03 17:57:33 +00:00
if ( '' === $review_count ) {
2016-11-16 12:17:00 +00:00
WC_Comments :: get_review_count_for_product ( $product );
} else {
$product -> set_review_count ( $review_count );
2016-11-11 15:31:00 +00:00
}
2018-01-03 17:57:33 +00:00
if ( '' === $rating_counts ) {
2016-11-16 12:17:00 +00:00
WC_Comments :: get_rating_counts_for_product ( $product );
} else {
$product -> set_rating_counts ( $rating_counts );
2016-11-11 15:31:00 +00:00
}
2018-01-03 17:57:33 +00:00
if ( '' === $average_rating ) {
2016-11-16 12:17:00 +00:00
WC_Comments :: get_average_rating_for_product ( $product );
} else {
$product -> set_average_rating ( $average_rating );
2016-11-11 15:31:00 +00:00
}
2018-01-03 16:44:37 +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-09-25 13:19:20 +00:00
// Handle sale dates on the fly in case of missed cron schedule.
2017-09-26 11:53:38 +00:00
if ( $product -> is_type ( 'simple' ) && $product -> is_on_sale ( 'edit' ) && $product -> get_sale_price ( 'edit' ) !== $product -> get_price ( 'edit' ) ) {
2017-09-25 13:19:20 +00:00
update_post_meta ( $product -> get_id (), '_price' , $product -> get_sale_price ( 'edit' ) );
$product -> set_price ( $product -> get_sale_price ( 'edit' ) );
}
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 .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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' .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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 ();
2018-01-03 17:57:33 +00:00
$featured = in_array ( 'featured' , $term_names , true );
$exclude_search = in_array ( 'exclude-from-search' , $term_names , true );
$exclude_catalog = in_array ( 'exclude-from-catalog' , $term_names , true );
2016-12-08 10:56:45 +00:00
if ( $exclude_search && $exclude_catalog ) {
$catalog_visibility = 'hidden' ;
} elseif ( $exclude_search ) {
$catalog_visibility = 'catalog' ;
} elseif ( $exclude_catalog ) {
$catalog_visibility = 'search' ;
} else {
$catalog_visibility = 'visible' ;
}
2018-01-03 16:44:37 +00:00
$product -> set_props (
array (
'featured' => $featured ,
'catalog_visibility' => $catalog_visibility ,
)
);
2016-12-08 10:56:45 +00:00
}
2016-11-11 14:31:15 +00:00
/**
* Read attributes from post meta .
*
2017-11-18 14:53:18 +00:00
* @ param WC_Product $product Product object .
2016-11-11 14:31:15 +00:00
*/
protected function read_attributes ( & $product ) {
2017-11-18 14:53:18 +00:00
$meta_attributes = get_post_meta ( $product -> get_id (), '_product_attributes' , true );
2016-11-11 14:31:15 +00:00
2017-11-18 14:53:18 +00:00
if ( ! empty ( $meta_attributes ) && is_array ( $meta_attributes ) ) {
2016-11-11 14:31:15 +00:00
$attributes = array ();
2017-11-18 14:53:18 +00:00
foreach ( $meta_attributes as $meta_attribute_key => $meta_attribute_value ) {
2018-01-03 16:44:37 +00:00
$meta_value = array_merge (
array (
'name' => '' ,
'value' => '' ,
'position' => 0 ,
'is_visible' => 0 ,
'is_variation' => 0 ,
'is_taxonomy' => 0 ,
), ( array ) $meta_attribute_value
);
2017-04-07 09:59:59 +00:00
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 {
2017-11-18 14:53:18 +00:00
$id = 0 ;
2016-11-11 14:31:15 +00:00
$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 .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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 ;
}
2018-01-03 16:44:37 +00:00
$download = new WC_Product_Download ();
2016-11-11 15:31:00 +00:00
$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 .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
* @ param bool $force 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' );
2018-04-10 13:43:38 +00:00
$value = is_string ( $value ) ? wp_slash ( $value ) : $value ;
2016-11-11 14:31:15 +00:00
switch ( $prop ) {
2018-01-03 16:44:37 +00:00
case 'virtual' :
case 'downloadable' :
case 'manage_stock' :
case 'sold_individually' :
2016-11-11 14:31:15 +00:00
$updated = update_post_meta ( $product -> get_id (), $meta_key , wc_bool_to_string ( $value ) );
break ;
2018-01-03 16:44:37 +00:00
case 'gallery_image_ids' :
2016-11-11 14:31:15 +00:00
$updated = update_post_meta ( $product -> get_id (), $meta_key , implode ( ',' , $value ) );
break ;
2018-01-03 16:44:37 +00:00
case 'image_id' :
2016-11-11 14:31:15 +00:00
if ( ! empty ( $value ) ) {
set_post_thumbnail ( $product -> get_id (), $value );
} else {
delete_post_meta ( $product -> get_id (), '_thumbnail_id' );
}
$updated = true ;
break ;
2018-01-03 16:44:37 +00:00
case 'date_on_sale_from' :
case 'date_on_sale_to' :
2017-03-13 21:36:31 +00:00
$updated = update_post_meta ( $product -> get_id (), $meta_key , $value ? $value -> getTimestamp () : '' );
break ;
2018-01-03 16:44:37 +00:00
default :
2016-11-11 14:31:15 +00:00
$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 ) {
2018-04-10 13:43:38 +00:00
if ( ! array_key_exists ( '_' . $key , $props_to_update ) ) {
2017-02-11 15:26:13 +00:00
continue ;
}
$function = 'get_' . $key ;
if ( is_callable ( array ( $product , $function ) ) ) {
2018-04-10 13:43:38 +00:00
$value = $product -> { $function }( 'edit' );
$value = is_string ( $value ) ? wp_slash ( $value ) : $value ;
if ( update_post_meta ( $product -> get_id (), '_' . $key , $value ) ) {
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
*
2018-01-03 17:57:33 +00:00
* @ since 3.0 . 0
* @ param WC_Product $product Product Object .
2017-02-11 15:26:13 +00:00
*/
2017-02-14 16:26:40 +00:00
protected function handle_updated_props ( & $product ) {
2018-06-11 10:44:58 +00:00
$price_is_synced = $product -> is_type ( array ( 'variable' , 'grouped' ) );
if ( ! $price_is_synced ) {
if ( in_array ( 'regular_price' , $this -> updated_props , true ) || in_array ( 'sale_price' , $this -> updated_props , true ) ) {
if ( $product -> get_sale_price ( 'edit' ) >= $product -> get_regular_price ( 'edit' ) ) {
update_post_meta ( $product -> get_id (), '_sale_price' , '' );
$product -> set_sale_price ( '' );
}
2017-08-22 10:43:48 +00:00
}
2018-06-11 10:44:58 +00:00
if ( in_array ( 'date_on_sale_from' , $this -> updated_props , true ) || in_array ( 'date_on_sale_to' , $this -> updated_props , true ) || in_array ( 'regular_price' , $this -> updated_props , true ) || in_array ( 'sale_price' , $this -> updated_props , true ) || in_array ( 'product_type' , $this -> updated_props , true ) ) {
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' ) );
} else {
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-08-22 10:43:48 +00:00
if ( in_array ( 'stock_quantity' , $this -> updated_props , true ) ) {
2018-05-02 13:28:53 +00:00
if ( $product -> is_type ( 'variation' ) ) {
do_action ( 'woocommerce_variation_set_stock' , $product );
} else {
do_action ( 'woocommerce_product_set_stock' , $product );
}
2016-11-11 14:31:15 +00:00
}
2017-08-22 10:43:48 +00:00
if ( in_array ( 'stock_status' , $this -> updated_props , true ) ) {
2018-05-02 13:28:53 +00:00
if ( $product -> is_type ( 'variation' ) ) {
do_action ( 'woocommerce_variation_set_stock_status' , $product -> get_id (), $product -> get_stock_status (), $product );
} else {
do_action ( '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 .
*
2017-11-23 14:26:43 +00:00
* @ param WC_Product $product Product object .
* @ param bool $force 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-11-23 14:26:43 +00:00
$categories = $product -> get_category_ids ( 'edit' );
2017-11-23 14:41:17 +00:00
if ( empty ( $categories ) && get_option ( 'default_product_cat' , 0 ) ) {
$categories = array ( get_option ( 'default_product_cat' , 0 ) );
2017-11-23 14:26:43 +00:00
}
wp_set_post_terms ( $product -> get_id (), $categories , 'product_cat' , false );
2017-02-11 15:26:13 +00:00
}
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
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
2018-01-03 16:44:37 +00:00
* @ 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 () ) {
2018-01-03 16:44:37 +00:00
case 'hidden' :
2017-02-11 15:35:20 +00:00
$terms [] = 'exclude-from-search' ;
$terms [] = 'exclude-from-catalog' ;
break ;
2018-01-03 16:44:37 +00:00
case 'catalog' :
2017-02-11 15:35:20 +00:00
$terms [] = 'exclude-from-search' ;
break ;
2018-01-03 16:44:37 +00:00
case 'search' :
2017-02-11 15:35:20 +00:00
$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 .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
* @ param bool $force 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
2018-03-13 22:37:09 +00:00
delete_transient ( 'wc_layered_nav_counts_' . $attribute_key );
2018-03-15 11:21:27 +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
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
* @ param bool $force 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 ) .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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 ) {
2017-08-30 15:23:57 +00:00
$this -> updated_props [] = 'product_type' ;
2017-01-11 11:48:31 +00:00
do_action ( 'woocommerce_product_type_changed' , $product , $old_type , $new_type );
}
2016-11-11 14:31:15 +00:00
}
/**
* Clear any caches .
*
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
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 () );
2017-10-25 12:27:18 +00:00
WC_Cache_Helper :: incr_cache_prefix ( 'product_' . $product -> get_id () );
2016-11-11 14:31:15 +00:00
}
/*
|--------------------------------------------------------------------------
| 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
2018-02-15 16:35:29 +00:00
$decimals = absint ( wc_get_price_decimals () );
$exclude_term_ids = array ();
$outofstock_join = '' ;
$outofstock_where = '' ;
$product_visibility_term_ids = wc_get_product_visibility_term_ids ();
2017-02-08 17:04:13 +00:00
2018-02-15 16:35:29 +00:00
if ( 'yes' === get_option ( 'woocommerce_hide_out_of_stock_items' ) && $product_visibility_term_ids [ 'outofstock' ] ) {
$exclude_term_ids [] = $product_visibility_term_ids [ 'outofstock' ];
}
if ( count ( $exclude_term_ids ) ) {
$outofstock_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 = id' ;
$outofstock_where = ' AND exclude_join.object_id IS NULL' ;
}
2018-01-03 16:44:37 +00:00
return $wpdb -> get_results (
2018-03-08 15:01:48 +00:00
// phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
2018-01-03 17:57:33 +00:00
$wpdb -> prepare (
" 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
2018-02-15 16:35:29 +00:00
$outofstock_join
2018-01-03 17:57:33 +00:00
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 ) != ''
AND CAST ( meta . meta_value AS DECIMAL ( 10 , % d ) ) = CAST ( meta2 . meta_value AS DECIMAL ( 10 , % d ) )
2018-02-15 16:35:29 +00:00
$outofstock_where
2018-01-03 17:57:33 +00:00
GROUP BY post . ID " ,
$decimals ,
$decimals
)
2018-03-08 15:01:48 +00:00
// phpcs:enable
2018-01-03 16:44:37 +00:00
);
2016-11-11 14:31:15 +00:00
}
/**
* 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 ();
2018-01-03 16:44:37 +00:00
return get_posts (
array (
'post_type' => array ( 'product' , 'product_variation' ),
'posts_per_page' => - 1 ,
'post_status' => 'publish' ,
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_tax_query
2018-01-03 16:44:37 +00:00
'tax_query' => array (
'relation' => 'AND' ,
array (
'taxonomy' => 'product_visibility' ,
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'featured' ] ),
),
array (
'taxonomy' => 'product_visibility' ,
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'exclude-from-catalog' ] ),
'operator' => 'NOT IN' ,
),
2016-11-11 14:31:15 +00:00
),
2018-01-03 16:44:37 +00:00
'fields' => 'id=>parent' ,
)
);
2016-11-11 14:31:15 +00:00
}
/**
* Check if product sku is found for any other product IDs .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-01-03 17:57:33 +00:00
* @ param int $product_id Product ID .
* @ param string $sku Will be slashed to work around https :// core . trac . wordpress . org / ticket / 27421.
2016-11-11 14:31:15 +00:00
* @ return bool
*/
public function is_existing_sku ( $product_id , $sku ) {
global $wpdb ;
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
2018-01-03 16:44:37 +00:00
return $wpdb -> get_var (
$wpdb -> prepare (
2018-01-03 17:57:33 +00:00
" 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' )
AND $wpdb -> posts . post_status != 'trash'
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
2018-01-03 16:44:37 +00:00
)
);
2016-11-11 14:31:15 +00:00
}
/**
* Return product ID based on SKU .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-08-22 18:04:21 +00:00
* @ param string $sku Product SKU .
2016-11-11 14:31:15 +00:00
* @ return int
*/
public function get_product_id_by_sku ( $sku ) {
global $wpdb ;
2017-08-22 18:04:21 +00:00
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
2018-01-03 16:44:37 +00:00
$id = $wpdb -> get_var (
$wpdb -> prepare (
2018-01-03 17:57:33 +00:00
" 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' )
AND posts . post_status != 'trash'
AND postmeta . meta_key = '_sku'
AND postmeta . meta_value = % s
LIMIT 1 " ,
$sku
2018-01-03 16:44:37 +00:00
)
);
2017-08-22 18:04:21 +00:00
return ( int ) apply_filters ( 'woocommerce_get_product_id_by_sku' , $id , $sku );
2016-11-11 14:31:15 +00:00
}
/**
* 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 ;
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
2018-01-03 16:44:37 +00:00
return $wpdb -> get_col (
$wpdb -> prepare (
2018-01-03 17:57:33 +00:00
" 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 " ,
current_time ( 'timestamp' , true )
2018-01-03 16:44:37 +00:00
)
);
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 ;
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
2018-01-03 16:44:37 +00:00
return $wpdb -> get_col (
$wpdb -> prepare (
2018-01-03 17:57:33 +00:00
" 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 " ,
current_time ( 'timestamp' , true )
2018-01-03 16:44:37 +00:00
)
);
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 .
2018-01-03 16:44:37 +00:00
* @ param array $match_attributes Array of attributes we want to try to match .
2016-11-11 14:31:15 +00:00
* @ 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 ,
2018-01-03 17:57:33 +00:00
'meta_query' => array (), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query
2016-11-11 14:31:15 +00:00
);
// 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' ,
2018-01-03 16:44:37 +00:00
),
2016-11-11 14:31:15 +00:00
);
}
$variations = get_posts ( $query_args );
if ( $variations && ! is_wp_error ( $variations ) ) {
return current ( $variations );
2018-01-03 16:44:37 +00:00
} elseif ( version_compare ( get_post_meta ( $product -> get_id (), '_product_version' , true ), '2.4.0' , '<' ) ) {
2016-11-11 14:31:15 +00:00
/**
* 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 .
*
2018-01-03 17:57:33 +00:00
* @ param int $parent_id Product ID .
2016-12-08 14:28:51 +00:00
*/
public function sort_all_product_variations ( $parent_id ) {
global $wpdb ;
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$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
)
);
2017-06-05 20:42:58 +00:00
$index = 1 ;
2016-12-08 14:28:51 +00:00
foreach ( $ids as $id ) {
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
2017-06-05 20:42:58 +00:00
$wpdb -> update ( $wpdb -> posts , array ( 'menu_order' => ( $index ++ ) ), array ( 'ID' => absint ( $id ) ) );
2016-12-08 14:28:51 +00:00
}
}
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 .
2018-01-03 17:57:33 +00:00
* @ param int $product_id Product ID .
2016-11-11 14:31:15 +00:00
* @ return array
*/
public function get_related_products ( $cats_array , $tags_array , $exclude_ids , $limit , $product_id ) {
global $wpdb ;
2017-12-19 02:18:33 +00:00
$args = array (
'categories' => $cats_array ,
'tags' => $tags_array ,
'exclude_ids' => $exclude_ids ,
'limit' => $limit + 10 ,
);
2017-12-19 09:19:10 +00:00
$related_product_query = ( array ) apply_filters ( 'woocommerce_product_related_posts_query' , $this -> get_related_products_query ( $cats_array , $tags_array , $exclude_ids , $limit + 10 ), $product_id , $args );
2017-12-19 02:18:33 +00:00
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery, WordPress.WP.PreparedSQL.NotPrepared
2017-12-19 09:19:10 +00:00
return $wpdb -> get_col ( implode ( ' ' , $related_product_query ) );
2016-11-11 14:31:15 +00:00
}
/**
* 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'
" ,
2018-01-03 16:44:37 +00:00
'limits' => '
LIMIT ' . absint( $limit ) . '
' ,
2017-05-08 13:15:16 +00:00
);
2016-11-11 14:31:15 +00:00
2017-05-08 18:08:59 +00:00
if ( count ( $exclude_term_ids ) ) {
2018-01-03 16:44:37 +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' ;
2017-05-08 13:15:16 +00:00
}
2016-11-11 14:31:15 +00:00
2017-05-08 18:08:59 +00:00
if ( count ( $include_term_ids ) ) {
2018-01-03 16:44:37 +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 ) ) {
2018-01-03 16:44:37 +00:00
$query [ 'where' ] .= ' AND p.ID NOT IN ( ' . implode ( ',' , array_map ( 'absint' , $exclude_ids ) ) . ' )' ;
2017-05-08 13:15:16 +00:00
}
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 .
2018-01-03 17:57:33 +00:00
* @ param int $product_id_with_stock Product ID .
* @ param int | null $stock_quantity Stock quantity .
* @ param string $operation Set , increase and decrease .
2016-11-11 14:31:15 +00:00
*/
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 );
2018-01-03 17:57:33 +00:00
// Update stock in DB directly.
2016-11-11 14:31:15 +00:00
switch ( $operation ) {
2018-01-03 16:44:37 +00:00
case 'increase' :
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$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
)
);
2016-11-11 14:31:15 +00:00
break ;
2018-01-03 16:44:37 +00:00
case 'decrease' :
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$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
)
);
2016-11-11 14:31:15 +00:00
break ;
2018-01-03 16:44:37 +00:00
default :
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$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
)
);
2016-11-11 14:31:15 +00:00
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 .
2018-01-03 17:57:33 +00:00
* @ param int $product_id Product ID .
* @ param int | null $quantity Quantity .
2018-01-03 16:44:37 +00:00
* @ param string $operation set , increase and decrease .
2017-02-10 23:41:53 +00:00
*/
public function update_product_sales ( $product_id , $quantity = null , $operation = 'set' ) {
global $wpdb ;
add_post_meta ( $product_id , 'total_sales' , 0 , true );
2018-01-03 17:57:33 +00:00
// Update stock in DB directly.
2017-02-10 23:41:53 +00:00
switch ( $operation ) {
2018-01-03 16:44:37 +00:00
case 'increase' :
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$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
)
);
2017-02-10 23:41:53 +00:00
break ;
2018-01-03 16:44:37 +00:00
case 'decrease' :
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$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
)
);
2017-02-10 23:41:53 +00:00
break ;
2018-01-03 16:44:37 +00:00
default :
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
$wpdb -> query (
$wpdb -> prepare (
" UPDATE { $wpdb -> postmeta } SET meta_value = %f WHERE post_id = %d AND meta_key='total_sales' " , $quantity , $product_id
)
);
2017-02-10 23:41:53 +00:00
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
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
2016-11-16 12:17:00 +00:00
*/
public function update_average_rating ( $product ) {
update_post_meta ( $product -> get_id (), '_wc_average_rating' , $product -> get_average_rating ( 'edit' ) );
2017-06-15 10:29:18 +00:00
self :: update_visibility ( $product , true );
2016-11-16 12:17:00 +00:00
}
/**
* Update a products review count meta .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
2016-11-16 12:17:00 +00:00
*/
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
2018-01-03 17:57:33 +00:00
* @ param WC_Product $product Product object .
2016-11-16 12:17:00 +00:00
*/
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
2018-01-03 17:57:33 +00:00
* @ param string $slug Product shipping class slug .
2016-11-16 20:48:42 +00:00
* @ 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 .
*
2018-01-03 17:57:33 +00:00
* @ param array $args Args to pass to WC_Product_Query () .
2017-05-15 11:50:52 +00:00
* @ return array | object
2018-01-03 17:57:33 +00:00
* @ see wc_get_products
2016-11-21 12:25:00 +00:00
*/
public function get_products ( $args = array () ) {
2017-07-14 17:01:25 +00:00
$query = new WC_Product_Query ( $args );
return $query -> get_products ();
2016-11-21 12:25:00 +00:00
}
2016-11-24 11:50:34 +00:00
/**
* Search product data for a term and return ids .
*
2018-06-09 16:23:07 +00:00
* @ param string $term Search term .
* @ param string $type Type of product .
* @ param bool $include_variations Include variations in search or not .
* @ param bool $all_statuses Should we search all statuses or limit to published .
* @ param null | int $limit Limit returned results . @ since 3.5 . 0.
2016-11-24 11:50:34 +00:00
* @ return array of ids
*/
2018-06-09 16:23:07 +00:00
public function search_products ( $term , $type = '' , $include_variations = false , $all_statuses = false , $limit = null ) {
2016-11-24 11:50:34 +00:00
global $wpdb ;
2017-04-03 11:04:59 +00:00
$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 = '' ;
2018-02-22 18:46:34 +00:00
$status_where = '' ;
2018-06-15 14:28:29 +00:00
$limit_query = '' ;
2018-03-12 19:18:13 +00:00
$term = wc_strtolower ( $term );
2018-02-22 18:22:55 +00:00
2018-02-27 19:45:45 +00:00
// See if search term contains OR keywords.
2018-03-12 19:18:13 +00:00
if ( strstr ( $term , ' or ' ) ) {
$term_groups = explode ( ' or ' , $term );
2018-02-22 18:22:55 +00:00
} else {
2018-02-27 19:45:45 +00:00
$term_groups = array ( $term );
2018-02-22 18:22:55 +00:00
}
2018-02-27 19:45:45 +00:00
$search_where = '' ;
$search_queries = array ();
foreach ( $term_groups as $term_group ) {
// Parse search terms.
if ( preg_match_all ( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/' , $term_group , $matches ) ) {
$search_terms = $this -> get_valid_search_terms ( $matches [ 0 ] );
$count = count ( $search_terms );
// if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence.
if ( 9 < $count || 0 === $count ) {
$search_terms = array ( $term_group );
}
} else {
$search_terms = array ( $term_group );
}
$term_group_query = '' ;
$searchand = '' ;
2018-02-22 18:22:55 +00:00
2018-02-27 19:45:45 +00:00
foreach ( $search_terms as $search_term ) {
$like = '%' . $wpdb -> esc_like ( $search_term ) . '%' ;
2018-03-19 17:36:36 +00:00
$term_group_query .= $wpdb -> prepare ( " { $searchand } ( ( posts.post_title LIKE %s) OR ( posts.post_excerpt LIKE %s) OR ( posts.post_content LIKE %s ) OR ( postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s ) ) " , $like , $like , $like , $like ); // @codingStandardsIgnoreLine.
2018-02-27 19:45:45 +00:00
$searchand = ' AND ' ;
}
if ( $term_group_query ) {
$search_queries [] = $term_group_query ;
}
2018-02-22 18:22:55 +00:00
}
2018-06-07 06:35:58 +00:00
if ( ! empty ( $search_queries ) ) {
2018-02-27 19:45:45 +00:00
$search_where = 'AND (' . implode ( ') OR (' , $search_queries ) . ')' ;
2018-02-22 18:22:55 +00:00
}
2016-11-24 11:50:34 +00:00
2018-03-19 17:36:36 +00:00
if ( $type && in_array ( $type , array ( 'virtual' , 'downloadable' ), true ) ) {
$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' ) " ;
2016-11-24 11:50:34 +00:00
}
2018-02-08 12:53:39 +00:00
if ( ! $all_statuses ) {
$status_where = " AND posts.post_status IN (' " . implode ( " ',' " , $post_statuses ) . " ') " ;
}
2018-06-09 16:23:07 +00:00
if ( $limit ) {
$limit_query = $wpdb -> prepare ( ' LIMIT %d ' , $limit );
}
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
2018-02-08 16:36:24 +00:00
$search_results = $wpdb -> get_results (
2018-01-03 17:57:33 +00:00
// phpcs:disable
2018-02-22 18:22:55 +00:00
" SELECT DISTINCT posts.ID as product_id, posts.post_parent as parent_id FROM { $wpdb -> posts } posts
LEFT JOIN { $wpdb -> postmeta } postmeta ON posts . ID = postmeta . post_id
$type_join
WHERE posts . post_type IN ( '" . implode( "' , '", $post_types ) . "' )
$search_where
$status_where
$type_where
2018-06-09 16:23:07 +00:00
ORDER BY posts . post_parent ASC , posts . post_title ASC
$limit_query
"
2018-01-03 17:57:33 +00:00
// phpcs:enable
2016-11-24 11:50:34 +00:00
);
2018-02-08 16:36:24 +00:00
$product_ids = wp_parse_id_list ( array_merge ( wp_list_pluck ( $search_results , 'product_id' ), wp_list_pluck ( $search_results , 'parent_id' ) ) );
2016-11-24 11:50:34 +00:00
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
2018-01-03 17:57:33 +00:00
* @ param int $product_id Product ID .
2017-02-08 19:18:39 +00:00
* @ 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 ;
}
}
2017-07-12 20:58:39 +00:00
2017-07-28 20:27:19 +00:00
/**
* Add ability to get products by 'reviews_allowed' in WC_Product_Query .
*
* @ since 3.2 . 0
2018-01-03 17:57:33 +00:00
* @ param string $where Where clause .
* @ param WP_Query $wp_query WP_Query instance .
* @ return string
2017-07-28 20:27:19 +00:00
*/
2017-07-25 17:01:24 +00:00
public function reviews_allowed_query_where ( $where , $wp_query ) {
global $wpdb ;
if ( isset ( $wp_query -> query_vars [ 'reviews_allowed' ] ) && is_bool ( $wp_query -> query_vars [ 'reviews_allowed' ] ) ) {
if ( $wp_query -> query_vars [ 'reviews_allowed' ] ) {
$where .= " AND $wpdb->posts .comment_status = 'open' " ;
} else {
$where .= " AND $wpdb->posts .comment_status = 'closed' " ;
}
}
return $where ;
}
2017-07-12 20:58:39 +00:00
/**
* Get valid WP_Query args from a WC_Product_Query ' s query variables .
*
* @ since 3.2 . 0
2018-01-03 17:57:33 +00:00
* @ param array $query_vars Query vars from a WC_Product_Query .
2017-07-12 20:58:39 +00:00
* @ return array
*/
protected function get_wp_query_args ( $query_vars ) {
// Map query vars to ones that get_wp_query_args or WP_Query recognize.
$key_mapping = array (
2017-07-25 17:01:24 +00:00
'status' => 'post_status' ,
'page' => 'paged' ,
'include' => 'post__in' ,
'stock_quantity' => 'stock' ,
'average_rating' => 'wc_average_rating' ,
'review_count' => 'wc_review_count' ,
2017-07-12 20:58:39 +00:00
);
foreach ( $key_mapping as $query_key => $db_key ) {
if ( isset ( $query_vars [ $query_key ] ) ) {
$query_vars [ $db_key ] = $query_vars [ $query_key ];
unset ( $query_vars [ $query_key ] );
}
}
2017-07-28 20:27:19 +00:00
// Map boolean queries that are stored as 'yes'/'no' in the DB to 'yes' or 'no'.
2017-07-24 22:05:23 +00:00
$boolean_queries = array (
'virtual' ,
'downloadable' ,
'sold_individually' ,
'manage_stock' ,
);
foreach ( $boolean_queries as $boolean_query ) {
2017-08-01 20:21:23 +00:00
if ( isset ( $query_vars [ $boolean_query ] ) && '' !== $query_vars [ $boolean_query ] ) {
2017-07-24 22:05:23 +00:00
$query_vars [ $boolean_query ] = $query_vars [ $boolean_query ] ? 'yes' : 'no' ;
}
}
2017-07-28 18:59:44 +00:00
// These queries cannot be auto-generated so we have to remove them and build them manually.
$manual_queries = array (
2017-08-01 19:52:12 +00:00
'sku' => '' ,
'featured' => '' ,
2017-07-28 18:59:44 +00:00
'visibility' => '' ,
);
foreach ( $manual_queries as $key => $manual_query ) {
if ( isset ( $query_vars [ $key ] ) ) {
$manual_queries [ $key ] = $query_vars [ $key ];
unset ( $query_vars [ $key ] );
}
2017-07-25 17:01:24 +00:00
}
2017-07-12 20:58:39 +00:00
$wp_query_args = parent :: get_wp_query_args ( $query_vars );
2017-07-14 17:10:37 +00:00
if ( ! isset ( $wp_query_args [ 'date_query' ] ) ) {
$wp_query_args [ 'date_query' ] = array ();
}
if ( ! isset ( $wp_query_args [ 'meta_query' ] ) ) {
2018-01-03 17:57:33 +00:00
// phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query
2017-07-14 17:10:37 +00:00
$wp_query_args [ 'meta_query' ] = array ();
}
2017-07-14 16:50:20 +00:00
// Handle product types.
if ( 'variation' === $query_vars [ 'type' ] ) {
$wp_query_args [ 'post_type' ] = 'product_variation' ;
2018-01-03 17:57:33 +00:00
} elseif ( is_array ( $query_vars [ 'type' ] ) && in_array ( 'variation' , $query_vars [ 'type' ], true ) ) {
2017-07-14 16:50:20 +00:00
$wp_query_args [ 'post_type' ] = array ( 'product_variation' , 'product' );
$wp_query_args [ 'tax_query' ][] = array (
'relation' => 'OR' ,
array (
'taxonomy' => 'product_type' ,
'field' => 'slug' ,
'terms' => $query_vars [ 'type' ],
),
array (
'taxonomy' => 'product_type' ,
'field' => 'id' ,
'operator' => 'NOT EXISTS' ,
),
);
} else {
$wp_query_args [ 'post_type' ] = 'product' ;
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_type' ,
'field' => 'slug' ,
'terms' => $query_vars [ 'type' ],
);
}
2017-07-28 20:27:19 +00:00
// Handle product categories.
2017-07-14 16:50:20 +00:00
if ( ! empty ( $query_vars [ 'category' ] ) ) {
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_cat' ,
'field' => 'slug' ,
2017-08-01 19:52:12 +00:00
'terms' => $query_vars [ 'category' ],
2017-07-14 16:50:20 +00:00
);
}
2017-07-28 20:27:19 +00:00
// Handle product tags.
2017-07-14 16:50:20 +00:00
if ( ! empty ( $query_vars [ 'tag' ] ) ) {
unset ( $wp_query_args [ 'tag' ] );
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_tag' ,
'field' => 'slug' ,
2017-08-01 19:52:12 +00:00
'terms' => $query_vars [ 'tag' ],
2017-07-14 16:50:20 +00:00
);
}
2017-07-28 20:27:19 +00:00
// Handle shipping classes.
2017-07-28 18:59:44 +00:00
if ( ! empty ( $query_vars [ 'shipping_class' ] ) ) {
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_shipping_class' ,
'field' => 'slug' ,
2017-08-01 19:52:12 +00:00
'terms' => $query_vars [ 'shipping_class' ],
2017-07-25 17:01:24 +00:00
);
}
2017-07-28 20:27:19 +00:00
// Handle total_sales.
2017-07-25 17:01:24 +00:00
// This query doesn't get auto-generated since the meta key doesn't have the underscore prefix.
if ( isset ( $query_vars [ 'total_sales' ] ) && '' !== $query_vars [ 'total_sales' ] ) {
$wp_query_args [ 'meta_query' ][] = array (
'key' => 'total_sales' ,
'value' => absint ( $query_vars [ 'total_sales' ] ),
'compare' => '=' ,
);
}
2017-07-28 20:27:19 +00:00
// Handle SKU.
2017-07-28 18:59:44 +00:00
if ( $manual_queries [ 'sku' ] ) {
$wp_query_args [ 'meta_query' ][] = array (
'key' => '_sku' ,
'value' => $manual_queries [ 'sku' ],
'compare' => 'LIKE' ,
2017-07-14 16:50:20 +00:00
);
}
2017-07-28 20:27:19 +00:00
// Handle featured.
2017-07-28 18:59:44 +00:00
if ( '' !== $manual_queries [ 'featured' ] ) {
2017-07-25 17:01:24 +00:00
$product_visibility_term_ids = wc_get_product_visibility_term_ids ();
2017-07-28 18:59:44 +00:00
if ( $manual_queries [ 'featured' ] ) {
2017-07-25 17:01:24 +00:00
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'featured' ] ),
);
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'exclude-from-catalog' ] ),
'operator' => 'NOT IN' ,
);
} else {
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
'field' => 'term_taxonomy_id' ,
'terms' => array ( $product_visibility_term_ids [ 'featured' ] ),
'operator' => 'NOT IN' ,
);
}
}
2017-07-28 20:27:19 +00:00
// Handle visibility.
2017-07-28 18:59:44 +00:00
if ( $manual_queries [ 'visibility' ] ) {
switch ( $manual_queries [ 'visibility' ] ) {
case 'search' :
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
2017-08-01 19:52:12 +00:00
'field' => 'slug' ,
'terms' => array ( 'exclude-from-search' ),
2017-07-28 18:59:44 +00:00
'operator' => 'NOT IN' ,
);
2018-01-03 16:44:37 +00:00
break ;
2017-07-28 18:59:44 +00:00
case 'catalog' :
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
2017-08-01 19:52:12 +00:00
'field' => 'slug' ,
'terms' => array ( 'exclude-from-catalog' ),
2017-07-28 18:59:44 +00:00
'operator' => 'NOT IN' ,
);
2018-01-03 16:44:37 +00:00
break ;
2017-07-28 18:59:44 +00:00
case 'visible' :
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
2017-08-01 19:52:12 +00:00
'field' => 'slug' ,
'terms' => array ( 'exclude-from-catalog' , 'exclude-from-search' ),
2017-07-28 18:59:44 +00:00
'operator' => 'NOT IN' ,
);
2018-01-03 16:44:37 +00:00
break ;
2017-07-28 18:59:44 +00:00
case 'hidden' :
$wp_query_args [ 'tax_query' ][] = array (
'taxonomy' => 'product_visibility' ,
2017-08-01 19:52:12 +00:00
'field' => 'slug' ,
'terms' => array ( 'exclude-from-catalog' , 'exclude-from-search' ),
2017-07-28 18:59:44 +00:00
'operator' => 'AND' ,
);
2018-01-03 16:44:37 +00:00
break ;
2017-07-28 18:59:44 +00:00
}
}
2017-07-28 20:27:19 +00:00
// Handle date queries.
2017-07-12 20:58:39 +00:00
$date_queries = array (
'date_created' => 'post_date' ,
'date_modified' => 'post_modified' ,
'date_on_sale_from' => '_sale_price_dates_from' ,
'date_on_sale_to' => '_sale_price_dates_to' ,
);
foreach ( $date_queries as $query_var_key => $db_key ) {
if ( isset ( $query_vars [ $query_var_key ] ) && '' !== $query_vars [ $query_var_key ] ) {
// Remove any existing meta queries for the same keys to prevent conflicts.
$existing_queries = wp_list_pluck ( $wp_query_args [ 'meta_query' ], 'key' , true );
foreach ( $existing_queries as $query_index => $query_contents ) {
unset ( $wp_query_args [ 'meta_query' ][ $query_index ] );
}
$wp_query_args = $this -> parse_date_for_wp_query ( $query_vars [ $query_var_key ], $db_key , $wp_query_args );
}
}
2017-07-28 20:27:19 +00:00
// Handle paginate.
2017-07-12 20:58:39 +00:00
if ( ! isset ( $query_vars [ 'paginate' ] ) || ! $query_vars [ 'paginate' ] ) {
$wp_query_args [ 'no_found_rows' ] = true ;
}
2017-07-28 20:27:19 +00:00
// Handle reviews_allowed.
2017-07-25 17:01:24 +00:00
if ( isset ( $query_vars [ 'reviews_allowed' ] ) && is_bool ( $query_vars [ 'reviews_allowed' ] ) ) {
add_filter ( 'posts_where' , array ( $this , 'reviews_allowed_query_where' ), 10 , 2 );
}
2017-07-12 20:58:39 +00:00
return apply_filters ( 'woocommerce_product_data_store_cpt_get_products_query' , $wp_query_args , $query_vars , $this );
}
/**
* Query for Products matching specific criteria .
*
* @ since 3.2 . 0
*
2018-01-03 17:57:33 +00:00
* @ param array $query_vars Query vars from a WC_Product_Query .
2017-07-12 20:58:39 +00:00
*
* @ return array | object
*/
public function query ( $query_vars ) {
$args = $this -> get_wp_query_args ( $query_vars );
if ( ! empty ( $args [ 'errors' ] ) ) {
$query = ( object ) array (
'posts' => array (),
'found_posts' => 0 ,
'max_num_pages' => 0 ,
);
} else {
$query = new WP_Query ( $args );
}
2017-07-14 17:01:25 +00:00
if ( isset ( $query_vars [ 'return' ] ) && 'objects' === $query_vars [ 'return' ] && ! empty ( $query -> posts ) ) {
// Prime caches before grabbing objects.
2017-08-29 10:19:00 +00:00
update_post_caches ( $query -> posts , array ( 'product' , 'product_variation' ) );
2017-07-14 17:01:25 +00:00
}
2017-07-12 20:58:39 +00:00
$products = ( isset ( $query_vars [ 'return' ] ) && 'ids' === $query_vars [ 'return' ] ) ? $query -> posts : array_filter ( array_map ( 'wc_get_product' , $query -> posts ) );
if ( isset ( $query_vars [ 'paginate' ] ) && $query_vars [ 'paginate' ] ) {
return ( object ) array (
'products' => $products ,
'total' => $query -> found_posts ,
'max_num_pages' => $query -> max_num_pages ,
);
}
return $products ;
}
2016-11-11 14:31:15 +00:00
}