Product Visibility Taxonomies (#12527)

* Convert visibility and featured to taxonomy

* Comment

* Add missing tax_queries

* Only check SKU after read.

* Added visibility term for outofstock products to speed those queries up al

* wc_bool_to_string
This commit is contained in:
Mike Jolley 2016-12-08 10:56:45 +00:00 committed by GitHub
parent af3f735664
commit 47fbae4d26
23 changed files with 280 additions and 151 deletions

File diff suppressed because one or more lines are too long

View File

@ -2174,13 +2174,9 @@ table.wp-list-table {
}
span.wc-featured {
margin: 0;
cursor: pointer;
&::before {
content: '\f155';
}
&.not-featured {
&::before {
content: '\f154';
@ -2191,12 +2187,11 @@ table.wp-list-table {
td.column-featured {
span.wc-featured {
font-size: 1.6em;
cursor: pointer;
}
}
span.wc-type {
margin: 0;
&::before {
font-family: 'WooCommerce';
content: '\e006';
@ -2206,6 +2201,7 @@ table.wp-list-table {
span.product-type {
@include ir();
font-size: 1.6em;
margin: 0 auto;
&::before {
@include icon( '\e006' );

View File

@ -299,6 +299,7 @@ abstract class WC_Data {
/**
* Read Meta Data from the database. Ignore any internal properties.
* Uses it's own caches because get_metadata does not provide meta_ids.
*
* @since 2.6.0
* @param bool $force_read True to force a new DB read (and update cache).

View File

@ -807,7 +807,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
*/
public function set_sku( $sku ) {
$sku = (string) $sku;
if ( ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) {
if ( $this->get_object_read() && ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) {
$this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) );
}
$this->set_prop( 'sku', $sku );

View File

@ -736,7 +736,6 @@ class WC_Admin_Post_Types {
public function product_sortable_columns( $columns ) {
$custom = array(
'price' => 'price',
'featured' => array( 'featured', 1 ),
'sku' => 'sku',
'name' => 'title',
);
@ -1671,12 +1670,6 @@ class WC_Admin_Post_Types {
'orderby' => 'meta_value_num',
) );
}
if ( 'featured' == $vars['orderby'] ) {
$vars = array_merge( $vars, array(
'meta_key' => '_featured',
'orderby' => 'meta_value',
) );
}
if ( 'sku' == $vars['orderby'] ) {
$vars = array_merge( $vars, array(
'meta_key' => '_sku',
@ -1876,27 +1869,23 @@ class WC_Admin_Post_Types {
* Output product visibility options.
*/
public function product_data_visibility() {
global $post;
global $post, $thepostid, $product_object;
if ( 'product' != $post->post_type ) {
if ( 'product' !== $post->post_type ) {
return;
}
$current_visibility = ( $current_visibility = get_post_meta( $post->ID, '_visibility', true ) ) ? $current_visibility : apply_filters( 'woocommerce_product_visibility_default' , 'visible' );
$current_featured = ( $current_featured = get_post_meta( $post->ID, '_featured', true ) ) ? $current_featured : 'no';
$visibility_options = apply_filters( 'woocommerce_product_visibility_options', array(
'visible' => __( 'Catalog/search', 'woocommerce' ),
'catalog' => __( 'Catalog', 'woocommerce' ),
'search' => __( 'Search', 'woocommerce' ),
'hidden' => __( 'Hidden', 'woocommerce' ),
) );
$thepostid = $post->ID;
$product_object = $thepostid ? wc_get_product( $thepostid ) : new WC_Product;
$current_visibility = $product_object->get_catalog_visibility();
$current_featured = wc_bool_to_string( $product_object->get_featured() );
$visibility_options = wc_get_product_visibility_options();
?>
<div class="misc-pub-section" id="catalog-visibility">
<?php _e( 'Catalog visibility:', 'woocommerce' ); ?> <strong id="catalog-visibility-display"><?php
echo isset( $visibility_options[ $current_visibility ] ) ? esc_html( $visibility_options[ $current_visibility ] ) : esc_html( $current_visibility );
if ( 'yes' == $current_featured ) {
if ( 'yes' === $current_featured ) {
echo ', ' . __( 'Featured', 'woocommerce' );
}
?></strong>

View File

@ -12,15 +12,15 @@
</label>
<?php foreach ( self::get_product_type_options() as $key => $option ) :
if ( $thepostid ) {
$selected_value = is_callable( array( $product_object, "is_$key" ) ) ? $product_object->{"is_$key"}() : get_post_meta( $post->ID, '_' . $key, true );
if ( $product_object ) {
$selected_value = is_callable( array( $product_object, "is_$key" ) ) ? $product_object->{"is_$key"}() : 'yes' === get_post_meta( $post->ID, '_' . $key, true );
} else {
$selected_value = isset( $option['default'] ) ? $option['default'] : 'no';
$selected_value = 'yes' === ( isset( $option['default'] ) ? $option['default'] : 'no' );
}
?>
<label for="<?php echo esc_attr( $option['id'] ); ?>" class="<?php echo esc_attr( $option['wrapper_class'] ); ?> tips" data-tip="<?php echo esc_attr( $option['description'] ); ?>">
<?php echo esc_html( $option['label'] ); ?>:
<input type="checkbox" name="<?php echo esc_attr( $option['id'] ); ?>" id="<?php echo esc_attr( $option['id'] ); ?>" <?php echo checked( $selected_value, 'yes', false ); ?> />
<input type="checkbox" name="<?php echo esc_attr( $option['id'] ); ?>" id="<?php echo esc_attr( $option['id'] ); ?>" <?php echo checked( $selected_value, true, false ); ?> />
</label>
<?php endforeach; ?>
</span>

View File

@ -169,6 +169,15 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
$args['tax_query'] = $tax_query;
}
// Filter featured.
if ( is_bool( $request['featured'] ) ) {
$args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
);
}
// Filter by sku.
if ( ! empty( $request['sku'] ) ) {
$args['meta_query'] = $this->add_meta_query( $args, array(
@ -177,14 +186,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
) );
}
// Filter featured.
if ( is_bool( $request['featured'] ) ) {
$args['meta_query'] = $this->add_meta_query( $args, array(
'key' => '_featured',
'value' => true === $request['featured'] ? 'yes' : 'no',
) );
}
// Filter by tax class.
if ( ! empty( $request['tax_class'] ) ) {
$args['meta_query'] = $this->add_meta_query( $args, array(

View File

@ -75,6 +75,8 @@ class WC_Install {
'2.7.0' => array(
'wc_update_270_webhooks',
'wc_update_270_grouped_products',
'wc_update_270_settings',
'wc_update_270_product_visibility',
),
);
@ -366,6 +368,12 @@ class WC_Install {
'variable',
'external',
),
'product_visibility' => array(
'exclude-from-search',
'exclude-from-catalog',
'featured',
'outofstock',
),
);
foreach ( $taxonomies as $taxonomy => $terms ) {

View File

@ -61,6 +61,18 @@ class WC_Post_types {
) )
);
register_taxonomy( 'product_visibility',
apply_filters( 'woocommerce_taxonomy_objects_product_visibility', array( 'product', 'product_variation' ) ),
apply_filters( 'woocommerce_taxonomy_args_product_visibility', array(
'hierarchical' => false,
'show_ui' => false,
'show_in_nav_menus' => false,
'query_var' => is_admin(),
'rewrite' => false,
'public' => false,
) )
);
register_taxonomy( 'product_cat',
apply_filters( 'woocommerce_taxonomy_objects_product_cat', array( 'product' ) ),
apply_filters( 'woocommerce_taxonomy_args_product_cat', array(

View File

@ -390,8 +390,8 @@ class WC_Query {
}
// Query vars that affect posts shown
$q->set( 'meta_query', $this->get_meta_query( $q->get( 'meta_query' ) ) );
$q->set( 'tax_query', $this->get_tax_query( $q->get( 'tax_query' ) ) );
$q->set( 'meta_query', $this->get_meta_query( $q->get( 'meta_query' ), true ) );
$q->set( 'tax_query', $this->get_tax_query( $q->get( 'tax_query' ), true ) );
$q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_shop_per_page', get_option( 'posts_per_page' ) ) );
$q->set( 'wc_query', 'product_query' );
$q->set( 'post__in', array_unique( (array) apply_filters( 'loop_shop_post_in', array() ) ) );
@ -526,15 +526,14 @@ class WC_Query {
* Appends meta queries to an array.
*
* @param array $meta_query
* @param bool $main_query
* @return array
*/
public function get_meta_query( $meta_query = array() ) {
public function get_meta_query( $meta_query = array(), $main_query = false ) {
if ( ! is_array( $meta_query ) ) {
$meta_query = array();
}
$meta_query['visibility'] = $this->visibility_meta_query();
$meta_query['stock_status'] = $this->stock_status_meta_query();
$meta_query['price_filter'] = $this->price_filter_meta_query();
$meta_query['rating_filter'] = $this->rating_filter_meta_query();
@ -572,55 +571,64 @@ class WC_Query {
/**
* Returns a meta query to handle product visibility.
*
* @deprecated 2.7.0 Replaced with taxonomy.
* @param string $compare (default: 'IN')
* @return array
*/
public function visibility_meta_query( $compare = 'IN' ) {
return array(
'key' => '_visibility',
'value' => is_search() ? array( 'visible', 'search' ) : array( 'visible', 'catalog' ),
'compare' => $compare,
);
return array();
}
/**
* Returns a meta query to handle product stock status.
*
* @access public
* @deprecated 2.7.0 Replaced with taxonomy.
* @param string $status (default: 'instock')
* @return array
*/
public function stock_status_meta_query( $status = 'instock' ) {
return 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ? array(
'key' => '_stock_status',
'value' => $status,
'compare' => '=',
) : array();
return array();
}
/**
* Appends tax queries to an array.
* @param array $tax_query
* @param bool $main_query
* @return array
*/
public function get_tax_query( $tax_query = array() ) {
public function get_tax_query( $tax_query = array(), $main_query = false ) {
if ( ! is_array( $tax_query ) ) {
$tax_query = array();
$tax_query = array( 'relation' => 'AND' );
}
// Layered nav filters on terms
if ( $_chosen_attributes = $this->get_layered_nav_chosen_attributes() ) {
// Layered nav filters on terms.
if ( $main_query && ( $_chosen_attributes = $this->get_layered_nav_chosen_attributes() ) ) {
foreach ( $_chosen_attributes as $taxonomy => $data ) {
$tax_query[] = array(
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => $data['terms'],
'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN',
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => $data['terms'],
'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN',
'include_children' => false,
);
}
}
$product_visibility_not_in = array( is_search() && $main_query ? 'exclude-from-search' : 'exclude-from-catalog' );
// Hide out of stock products.
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$product_visibility_not_in[] = 'outofstock';
}
$tax_query[] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => $product_visibility_not_in,
'operator' => 'NOT IN',
);
return array_filter( apply_filters( 'woocommerce_product_query_tax_query', $tax_query, $this ) );
}

View File

@ -195,6 +195,7 @@ class WC_Shortcodes {
'order' => $ordering_args['order'],
'posts_per_page' => $atts['per_page'],
'meta_query' => $meta_query,
'tax_query' => WC()->query->get_tax_query(),
);
$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
@ -308,6 +309,7 @@ class WC_Shortcodes {
'orderby' => $atts['orderby'],
'order' => $atts['order'],
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
);
$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
@ -338,6 +340,7 @@ class WC_Shortcodes {
'order' => $atts['order'],
'posts_per_page' => -1,
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
);
if ( ! empty( $atts['skus'] ) ) {
@ -346,16 +349,10 @@ class WC_Shortcodes {
'value' => array_map( 'trim', explode( ',', $atts['skus'] ) ),
'compare' => 'IN',
);
// Ignore catalog visibility
$query_args['meta_query'] = array_merge( $query_args['meta_query'], WC()->query->stock_status_meta_query() );
}
if ( ! empty( $atts['ids'] ) ) {
$query_args['post__in'] = array_map( 'trim', explode( ',', $atts['ids'] ) );
// Ignore catalog visibility
$query_args['meta_query'] = array_merge( $query_args['meta_query'], WC()->query->stock_status_meta_query() );
}
return self::product_loop( $query_args, $atts, 'products' );
@ -380,6 +377,7 @@ class WC_Shortcodes {
'no_found_rows' => 1,
'post_status' => 'publish',
'meta_query' => $meta_query,
'tax_query' => WC()->query->get_tax_query(),
);
if ( isset( $atts['sku'] ) ) {
@ -537,6 +535,7 @@ class WC_Shortcodes {
'post_status' => 'publish',
'post_type' => 'product',
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
'post__in' => array_merge( array( 0 ), wc_get_product_ids_on_sale() ),
);
@ -567,6 +566,7 @@ class WC_Shortcodes {
'meta_key' => 'total_sales',
'orderby' => 'meta_value_num',
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
);
$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
@ -598,6 +598,7 @@ class WC_Shortcodes {
'order' => $atts['order'],
'posts_per_page' => $atts['per_page'],
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
);
$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
@ -627,10 +628,13 @@ class WC_Shortcodes {
'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'.
), $atts, 'featured_products' );
$meta_query = WC()->query->get_meta_query();
$meta_query[] = array(
'key' => '_featured',
'value' => 'yes',
$meta_query = WC()->query->get_meta_query();
$tax_query = WC()->query->get_tax_query();
$tax_query[] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
'operator' => 'IN',
);
$query_args = array(
@ -641,6 +645,7 @@ class WC_Shortcodes {
'orderby' => $atts['orderby'],
'order' => $atts['order'],
'meta_query' => $meta_query,
'tax_query' => $tax_query,
);
$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
@ -795,13 +800,13 @@ class WC_Shortcodes {
'orderby' => $atts['orderby'],
'order' => $atts['order'],
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => array(
array(
'taxonomy' => strstr( $atts['attribute'], 'pa_' ) ? sanitize_title( $atts['attribute'] ) : 'pa_' . sanitize_title( $atts['attribute'] ),
'terms' => array_map( 'sanitize_title', explode( ',', $atts['filter'] ) ),
'field' => 'slug',
),
),
'tax_query' => WC()->query->get_tax_query(),
);
$query_args['tax_query'][] = array(
'taxonomy' => strstr( $atts['attribute'], 'pa_' ) ? sanitize_title( $atts['attribute'] ) : 'pa_' . sanitize_title( $atts['attribute'] ),
'terms' => array_map( 'sanitize_title', explode( ',', $atts['filter'] ) ),
'field' => 'slug',
);
return self::product_loop( $query_args, $atts, 'product_attribute' );
@ -840,7 +845,10 @@ class WC_Shortcodes {
*/
private static function _maybe_add_category_args( $args, $category, $operator ) {
if ( ! empty( $category ) ) {
$args['tax_query'] = array(
if ( empty( $args['tax_query'] ) ) {
$args['tax_query'] = array();
}
$args['tax_query'][] = array(
array(
'taxonomy' => 'product_cat',
'terms' => array_map( 'sanitize_title', explode( ',', $category ) ),

View File

@ -132,6 +132,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$product->read_meta_data();
$this->read_attributes( $product );
$this->read_downloads( $product );
$this->read_visibility( $product );
$this->read_product_data( $product );
$product->set_object_read( true );
}
@ -155,6 +156,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$this->update_post_meta( $product );
$this->update_terms( $product );
$this->update_visibility( $product );
$this->update_attributes( $product );
$this->update_downloads( $product );
$product->save_meta_data();
@ -224,8 +226,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
$product->set_props( array(
'featured' => get_post_meta( $id, '_featured', true ),
'catalog_visibility' => get_post_meta( $id, '_visibility', true ),
'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 ),
@ -269,6 +269,36 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
}
/**
* Convert visibility terms to props.
* Catalog visibility valid values are 'visible', 'catalog', 'search', and 'hidden'.
*
* @param WC_Product
* @since 2.7.0
*/
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,
) );
}
/**
* Read attributes from post meta.
*
@ -334,7 +364,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$updated_props = array();
$changed_props = array_keys( $product->get_changes() );
$meta_key_to_props = array(
'_visibility' => 'catalog_visibility',
'_sku' => 'sku',
'_regular_price' => 'regular_price',
'_sale_price' => 'sale_price',
@ -359,7 +388,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'_product_image_gallery' => 'gallery_image_ids',
'_download_limit' => 'download_limit',
'_download_expiry' => 'download_expiry',
'_featured' => 'featured',
'_thumbnail_id' => 'image_id',
'_stock' => 'stock_quantity',
'_stock_status' => 'stock_status',
@ -377,7 +405,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
case 'virtual' :
case 'downloadable' :
case 'manage_stock' :
case 'featured' :
case 'sold_individually' :
$updated = update_post_meta( $product->get_id(), $meta_key, wc_bool_to_string( $value ) );
break;
@ -411,14 +438,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
}
if ( in_array( 'featured', $updated_props ) ) {
delete_transient( 'wc_featured_products' );
}
if ( in_array( 'catalog_visibility', $updated_props ) ) {
do_action( 'woocommerce_product_set_visibility', $product->get_id(), $product->get_catalog_visibility() );
}
if ( in_array( 'stock_quantity', $updated_props ) ) {
do_action( $product->is_type( 'variation' ) ? 'woocommerce_variation_set_stock' : 'woocommerce_product_set_stock' , $product );
}
@ -451,6 +470,42 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
wp_set_post_terms( $product->get_id(), array( $product->get_shipping_class_id( 'edit' ) ), 'product_shipping_class', false );
}
/**
* Update visibility terms based on props.
*
* @param WC_Product
* @since 2.7.0
*/
protected function update_visibility( &$product ) {
$terms = array();
if ( $product->get_featured() ) {
$terms[] = 'featured';
}
if ( 'outofstock' === $product->get_stock_status() ) {
$terms[] = 'outofstock';
}
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() );
}
}
/**
* Update attributes which are a mix of terms and meta data.
*
@ -604,15 +659,18 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'post_type' => array( 'product', 'product_variation' ),
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
'tax_query' => array(
'relation' => 'AND',
array(
'key' => '_visibility',
'value' => array( 'catalog', 'visible' ),
'compare' => 'IN',
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => array( 'featured' ),
),
array(
'key' => '_featured',
'value' => 'yes',
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => array( 'exclude-from-catalog' ),
'operator' => 'NOT IN',
),
),
'fields' => 'id=>parent',
@ -803,23 +861,20 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$limit = absint( $limit );
$query = array();
$query['fields'] = "SELECT DISTINCT ID FROM {$wpdb->posts} p";
$query['join'] = " INNER JOIN {$wpdb->postmeta} pm ON ( pm.post_id = p.ID AND pm.meta_key='_visibility' )";
$query['join'] .= " INNER JOIN {$wpdb->term_relationships} tr ON (p.ID = tr.object_id)";
$query['join'] = " INNER JOIN {$wpdb->term_relationships} tr ON (p.ID = tr.object_id)";
$query['join'] .= " INNER JOIN {$wpdb->term_taxonomy} tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)";
$query['join'] .= " INNER JOIN {$wpdb->terms} t ON (t.term_id = tt.term_id)";
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$query['join'] .= " INNER JOIN {$wpdb->postmeta} pm2 ON ( pm2.post_id = p.ID AND pm2.meta_key='_stock_status' )";
}
$query['where'] = ' WHERE 1=1';
$query['where'] .= " AND p.post_status = 'publish'";
$query['where'] .= " AND p.post_type = 'product'";
$query['where'] .= " AND p.ID NOT IN ( {$exclude_ids} )";
$query['where'] .= " AND pm.meta_value IN ( 'visible', 'catalog' )";
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$query['where'] .= " AND pm2.meta_value = 'instock'";
if ( $exclude_catalog_term = get_term_by( 'name', 'exclude-from-catalog', 'product_visibility' ) ) {
$query['where'] .= " AND t.term_id !=" . absint( $exclude_catalog_term->term_id );
}
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ( $outofstock_term = get_term_by( 'name', 'outofstock', 'product_visibility' ) ) ) {
$query['where'] .= " AND t.term_id !=" . absint( $outofstock_term->term_id );
}
if ( $cats_array || $tags_array ) {

View File

@ -55,10 +55,11 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
'numberposts' => -1,
);
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$visible_only_args['meta_query'][] = array(
'key' => '_stock_status',
'value' => 'instock',
'compare' => '=',
$visible_only_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'outofstock',
'operator' => 'NOT IN',
);
}
$children['all'] = get_posts( apply_filters( 'woocommerce_variable_children_args', $all_args, $product, false ) );

View File

@ -687,7 +687,7 @@ function wc_get_product_attachment_props( $attachment_id, $product = false ) {
*/
function wc_get_product_visibility_options() {
return apply_filters( 'woocommerce_product_visibility_options', array(
'visible' => __( 'Catalog/search', 'woocommerce' ),
'visible' => __( 'Visible', 'woocommerce' ),
'catalog' => __( 'Catalog', 'woocommerce' ),
'search' => __( 'Search', 'woocommerce' ),
'hidden' => __( 'Hidden', 'woocommerce' ),

View File

@ -550,31 +550,23 @@ function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_
_update_post_term_count( $terms, $taxonomy );
}
// Stock query
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$stock_join = "LEFT JOIN {$wpdb->postmeta} AS meta_stock ON posts.ID = meta_stock.post_id";
$stock_query = "
AND meta_stock.meta_key = '_stock_status'
AND meta_stock.meta_value = 'instock'
";
} else {
$stock_query = $stock_join = '';
}
// Main query
$count_query = "
SELECT COUNT( DISTINCT posts.ID ) FROM {$wpdb->posts} as posts
LEFT JOIN {$wpdb->postmeta} AS meta_visibility ON posts.ID = meta_visibility.post_id
LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
$stock_join
WHERE post_status = 'publish'
AND post_type = 'product'
AND meta_visibility.meta_key = '_visibility'
AND meta_visibility.meta_value IN ( 'visible', 'catalog' )
$stock_query
";
if ( $exclude_catalog_term = get_term_by( 'name', 'exclude-from-catalog', 'product_visibility' ) ) {
$count_query .= "AND term_id !=" . absint( $exclude_catalog_term->term_id );
}
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ( $outofstock_term = get_term_by( 'name', 'outofstock', 'product_visibility' ) ) ) {
$count_query .= "AND term_id !=" . absint( $outofstock_term->term_id );
}
// Pre-process term taxonomy ids
if ( ! $terms_are_term_taxonomy_ids ) {
// We passed in an array of TERMS in format id=>parent
@ -634,8 +626,9 @@ function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_
* @param int $product_id
*/
function wc_recount_after_stock_change( $product_id ) {
if ( get_option( 'woocommerce_hide_out_of_stock_items' ) != 'yes' )
if ( 'yes' !== get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
return;
}
$product_terms = get_the_terms( $product_id, 'product_cat' );

View File

@ -1027,3 +1027,31 @@ function wc_update_270_settings() {
update_option( 'woocommerce_shipping_tax_class', '' );
}
}
/**
* Convert meta values into term for product visibility.
*/
function wc_update_270_product_visibility() {
global $wpdb;
$featured_term = get_term_by( 'name', 'featured', 'product_visibility' );
$exclude_search_term = get_term_by( 'name', 'exclude-from-search', 'product_visibility' );
$exclude_catalog_term = get_term_by( 'name', 'exclude-from-catalog', 'product_visibility' );
$outofstock_term = get_term_by( 'name', 'out-of-stock', 'product_visibility' );
if ( $featured_term ) {
$wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_featured' AND meta_value = 'yes';", $featured_term->term_id ) );
}
if ( $exclude_search_term ) {
$wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_visibility' AND meta_value IN ('hidden', 'catalog');", $exclude_search_term->term_id ) );
}
if ( $exclude_catalog_term ) {
$wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_visibility' AND meta_value IN ('hidden', 'search');", $exclude_catalog_term->term_id ) );
}
if ( $outofstock_term ) {
$wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_stock_status' AND meta_value IN 'outofstock';", $outofstock_term->term_id ) );
}
}

View File

@ -345,7 +345,7 @@ class WC_Widget_Layered_Nav extends WC_Widget {
if ( 'or' === $query_type ) {
foreach ( $tax_query as $key => $query ) {
if ( $taxonomy === $query['taxonomy'] ) {
if ( is_array( $query ) && $taxonomy === $query['taxonomy'] ) {
unset( $tax_query[ $key ] );
}
}

View File

@ -101,10 +101,18 @@ class WC_Widget_Products extends WC_Widget {
'no_found_rows' => 1,
'order' => $order,
'meta_query' => array(),
'tax_query' => array(
'relation' => 'AND',
),
);
if ( empty( $instance['show_hidden'] ) ) {
$query_args['meta_query'][] = WC()->query->visibility_meta_query();
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => is_search() ? 'exclude-from-search' : 'exclude-from-catalog',
'operator' => 'NOT IN',
);
$query_args['post_parent'] = 0;
}
@ -117,14 +125,23 @@ class WC_Widget_Products extends WC_Widget {
);
}
$query_args['meta_query'][] = WC()->query->stock_status_meta_query();
$query_args['meta_query'] = array_filter( $query_args['meta_query'] );
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$query_args['tax_query'] = array(
array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'outofstock',
'operator' => 'NOT IN',
),
);
}
switch ( $show ) {
case 'featured' :
$query_args['meta_query'][] = array(
'key' => '_featured',
'value' => 'yes',
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
);
break;
case 'onsale' :

View File

@ -63,11 +63,25 @@ class WC_Widget_Recently_Viewed extends WC_Widget {
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : $this->settings['number']['std'];
$query_args = array( 'posts_per_page' => $number, 'no_found_rows' => 1, 'post_status' => 'publish', 'post_type' => 'product', 'post__in' => $viewed_products, 'orderby' => 'post__in' );
$query_args = array(
'posts_per_page' => $number,
'no_found_rows' => 1,
'post_status' => 'publish',
'post_type' => 'product',
'post__in' => $viewed_products,
'orderby' => 'post__in',
);
$query_args['meta_query'] = array();
$query_args['meta_query'][] = WC()->query->stock_status_meta_query();
$query_args['meta_query'] = array_filter( $query_args['meta_query'] );
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$query_args['tax_query'] = array(
array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'outofstock',
'operator' => 'NOT IN',
),
);
}
$r = new WP_Query( $query_args );

View File

@ -70,10 +70,10 @@ class WC_Widget_Top_Rated_Products extends WC_Widget {
'meta_key' => '_wc_average_rating',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
);
$query_args['meta_query'] = WC()->query->get_meta_query();
$r = new WP_Query( $query_args );
if ( $r->have_posts() ) {

View File

@ -188,6 +188,8 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woocommerce/wo
* Added tool to clear orphaned variations in system status.
* Remove checkbox options in system status tools and replace with constants.
* Added security section in system status report.
* Performance - Converted _featured and _visibility meta data to terms for faster catalog queries. Upgrade routine handles migration. Developers may need to update queries to reflect this change.
* Performance - Added visibility term for outofstock products to speed those queries up also.
* Performance - Introduced a new CRUD (create, read, update, delete) system for Products, Orders, Customers and Shipping Zones.
* Performance - Optimised variable product sync. Upper/lower price meta is no longer stored, just the main prices, if a child has weight, and if a child has dimensions.
* Performance - Removed WP_Query from up-sells.php and related.php and replaced with PHP foreach loop (since we already have the product IDs).

View File

@ -39,7 +39,6 @@ class WC_Helper_Product {
update_post_meta( $product, '_tax_status', 'taxable' );
update_post_meta( $product, '_downloadable', 'no' );
update_post_meta( $product, '_virtual', 'no' );
update_post_meta( $product, '_visibility', 'visible' );
update_post_meta( $product, '_stock_status', 'instock' );
wp_set_object_terms( $product, 'simple', 'product_type' );
@ -93,7 +92,6 @@ class WC_Helper_Product {
update_post_meta( $product, '_tax_status', 'taxable' );
update_post_meta( $product, '_downloadable', 'no' );
update_post_meta( $product, '_virtual', 'no' );
update_post_meta( $product, '_visibility', 'visible' );
update_post_meta( $product, '_stock_status', 'instock' );
wp_set_object_terms( $product, 'grouped', 'product_type' );
@ -136,7 +134,6 @@ class WC_Helper_Product {
update_post_meta( $product_id, '_tax_status', 'taxable' );
update_post_meta( $product_id, '_downloadable', 'no' );
update_post_meta( $product_id, '_virtual', 'no' );
update_post_meta( $product_id, '_visibility', 'visible' );
update_post_meta( $product_id, '_stock_status', 'instock' );
// Attributes

View File

@ -157,7 +157,6 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case {
update_post_meta( $product->get_id(), '_regular_price', wc_format_decimal( 10 ) );
update_post_meta( $product->get_id(), '_price', wc_format_decimal( 5 ) );
update_post_meta( $product->get_id(), '_sale_price', wc_format_decimal( 5 ) );
update_post_meta( $product->get_id(), '_featured', 'yes' );
wc_get_product_ids_on_sale(); // Creates the transient for on sale products
wc_get_featured_product_ids(); // Creates the transient for featured products
@ -205,8 +204,8 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case {
// Create product
$product = WC_Helper_Product::create_simple_product();
update_post_meta( $product->get_id(), '_featured', 'yes' );
$product->set_featured( true );
$product->save();
$this->assertEquals( array( $product->get_id() ), wc_get_featured_product_ids() );