Allow select visibility in product shortcodes

Ref #15979
This commit is contained in:
Claudio Sanches 2017-08-29 18:29:29 -03:00
parent 19050bfd0c
commit 6dc58b4f6a
3 changed files with 294 additions and 81 deletions

View File

@ -261,8 +261,6 @@ class WC_Shortcodes {
$type = 'best_selling_products';
} elseif ( isset( $atts['top_rated'] ) && wc_string_to_bool( $atts['top_rated'] ) ) {
$type = 'top_rated_products';
} elseif ( isset( $atts['featured'] ) && wc_string_to_bool( $atts['featured'] ) ) {
$type = 'featured_products';
}
$shortcode = new WC_Shortcode_Products( $atts, $type );
@ -456,6 +454,8 @@ class WC_Shortcodes {
'cat_operator' => 'IN',
), (array) $atts );
$atts['visibility'] = 'featured';
$shortcode = new WC_Shortcode_Products( $atts, 'featured_products' );
return $shortcode->get_content();

View File

@ -41,6 +41,14 @@ class WC_Shortcode_Products {
*/
protected $query_args = array();
/**
* Set custom visibility.
*
* @since 3.2.0
* @var bool
*/
protected $custom_visibility = false;
/**
* Initialize shortcode.
*
@ -105,18 +113,19 @@ class WC_Shortcode_Products {
$attributes = $this->parse_legacy_attributes( $attributes );
return shortcode_atts( array(
'limit' => '-1', // Results limit.
'columns' => '4', // Number of columns.
'orderby' => 'title', // menu_order, title, date, rand, price, popularity, rating, or id.
'order' => 'ASC', // ASC or DESC.
'ids' => '', // Comma separated IDs.
'skus' => '', // Comma separated SKUs.
'category' => '', // Comma separated category slugs.
'cat_operator' => 'IN', // Operator to compare categories. Possible values are 'IN', 'NOT IN', 'AND'.
'attribute' => '', // Single attribute slug.
'terms' => '', // Comma separated term slugs.
'terms_operator' => 'IN', // Operator to compare terms. Possible values are 'IN', 'NOT IN', 'AND'.
'class' => '', // HTML class.
'limit' => '-1', // Results limit.
'columns' => '4', // Number of columns.
'orderby' => 'title', // menu_order, title, date, rand, price, popularity, rating, or id.
'order' => 'ASC', // ASC or DESC.
'ids' => '', // Comma separated IDs.
'skus' => '', // Comma separated SKUs.
'category' => '', // Comma separated category slugs.
'cat_operator' => 'IN', // Operator to compare categories. Possible values are 'IN', 'NOT IN', 'AND'.
'attribute' => '', // Single attribute slug.
'terms' => '', // Comma separated term slugs.
'terms_operator' => 'IN', // Operator to compare terms. Possible values are 'IN', 'NOT IN', 'AND'.
'visibility' => 'visible', // Possible values are 'visible', 'catalog', 'search', 'hidden'.
'class' => '', // HTML class.
), $attributes, $this->type );
}
@ -163,9 +172,12 @@ class WC_Shortcode_Products {
// @codingStandardsIgnoreStart
$query_args['posts_per_page'] = (int) $this->attributes['limit'];
$query_args['meta_query'] = WC()->query->get_meta_query();
$query_args['tax_query'] = WC()->query->get_tax_query();
$query_args['tax_query'] = array();
// @codingStandardsIgnoreEnd
// Visibility.
$this->set_visibility_query_args( $query_args );
// SKUs.
$this->set_skus_query_args( $query_args );
@ -228,7 +240,7 @@ class WC_Shortcode_Products {
* @param array $query_args Query args.
*/
protected function set_attributes_query_args( &$query_args ) {
if ( ! empty( $this->attributes['attribute'] ) || ! empty( $this->attributes['filter'] ) ) {
if ( ! empty( $this->attributes['attribute'] ) || ! empty( $this->attributes['terms'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => strstr( $this->attributes['attribute'], 'pa_' ) ? sanitize_title( $this->attributes['attribute'] ) : 'pa_' . sanitize_title( $this->attributes['attribute'] ),
'terms' => array_map( 'sanitize_title', explode( ',', $this->attributes['terms'] ) ),
@ -290,18 +302,87 @@ class WC_Shortcode_Products {
}
/**
* Set featured products query args.
* Set visibility query args.
*
* @since 3.2.0
* @param array $query_args Query args.
*/
protected function set_featured_products_query_args( &$query_args ) {
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => 'featured',
'field' => 'name',
'operator' => 'IN',
);
protected function set_visibility_query_args( &$query_args ) {
switch ( $this->attributes['visibility'] ) {
case 'hidden' :
$this->custom_visibility = true;
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => array( 'exclude-from-catalog', 'exclude-from-search' ),
'field' => 'name',
'operator' => 'AND',
'include_children' => false,
);
break;
case 'catalog' :
$this->custom_visibility = true;
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-search',
'field' => 'name',
'operator' => 'IN',
'include_children' => false,
);
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-catalog',
'field' => 'name',
'operator' => 'NOT IN',
'include_children' => false,
);
break;
case 'search' :
$this->custom_visibility = true;
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-catalog',
'field' => 'name',
'operator' => 'IN',
'include_children' => false,
);
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-search',
'field' => 'name',
'operator' => 'NOT IN',
'include_children' => false,
);
break;
case 'featured' :
$query_args['tax_query'] = array_merge( $query_args['tax_query'], WC()->query->get_tax_query() );
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'terms' => 'featured',
'field' => 'name',
'operator' => 'IN',
'include_children' => false,
);
break;
default :
$query_args['tax_query'] = array_merge( $query_args['tax_query'], WC()->query->get_tax_query() );
break;
}
}
/**
* Set product as visible when quering for hidden products.
*
* @since 3.2.0
* @param bool $visibility Product visibility.
* @return bool
*/
public function set_product_as_visible( $visibility ) {
if ( $this->custom_visibility ) {
return true;
}
return $visibility;
}
/**
@ -323,6 +404,36 @@ class WC_Shortcode_Products {
return $classes;
}
/**
* Get products.
*
* @since 3.2.0
* @return array
*/
protected function get_products() {
$transient_name = 'wc_loop' . substr( md5( wp_json_encode( $this->query_args ) . $this->type ), 28 ) . WC_Cache_Helper::get_transient_version( 'product_query' );
$products = get_transient( $transient_name );
if ( false === $products || ! is_a( $products, 'WP_Query' ) ) {
if ( 'top_rated_products' === $this->type ) {
add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
$products = new WP_Query( $this->query_args );
remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
} else {
$products = new WP_Query( $this->query_args );
}
// Remove ordering query arguments.
if ( ! empty( $this->attributes['category'] ) ) {
WC()->query->remove_ordering_args();
}
set_transient( $transient_name, $products, DAY_IN_SECONDS * 30 );
}
return $products;
}
/**
* Loop over found products.
*
@ -336,25 +447,7 @@ class WC_Shortcode_Products {
$classes = $this->get_wrapper_classes( $columns );
$woocommerce_loop['columns'] = $columns;
$woocommerce_loop['name'] = $this->type;
$transient_name = 'wc_loop' . substr( md5( wp_json_encode( $this->query_args ) . $this->type ), 28 ) . WC_Cache_Helper::get_transient_version( 'product_query' );
$products = get_transient( $transient_name );
if ( false === $products || ! is_a( $products, 'WP_Query' ) ) {
if ( 'top_rated_products' === $this->type ) {
add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
$products = new WP_Query( $this->query_args );
remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
} else {
$products = new WP_Query( $this->query_args );
}
set_transient( $transient_name, $products, DAY_IN_SECONDS * 30 );
}
// Remove ordering query arguments.
if ( ! empty( $this->attributes['category'] ) ) {
WC()->query->remove_ordering_args();
}
$products = $this->get_products();
ob_start();
@ -368,7 +461,15 @@ class WC_Shortcode_Products {
while ( $products->have_posts() ) {
$products->the_post();
// Set custom product visibility when quering hidden products.
add_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) );
// Render product template.
wc_get_template_part( 'content', 'product' );
// Restore product visibility.
remove_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) );
}
woocommerce_product_loop_end();

View File

@ -24,6 +24,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'attribute' => '',
'terms' => '',
'terms_operator' => 'IN',
'visibility' => 'visible',
'class' => '',
);
$this->assertEquals( $expected, $shortcode->get_attributes() );
@ -44,6 +45,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'attribute' => '',
'terms' => '',
'terms_operator' => 'IN',
'visibility' => 'visible',
'class' => '',
);
$this->assertEquals( $expected2, $shortcode2->get_attributes() );
@ -89,11 +91,11 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
);
$this->assertEquals( $expected2, $shortcode2->get_query_args() );
$shortcode2 = new WC_Shortcode_Products( array(
$shortcode3 = new WC_Shortcode_Products( array(
'ids' => '1,2,3',
'skus' => 'foo,bar',
) );
$expected2 = array(
$expected3 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -105,16 +107,16 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'tax_query' => $tax_query,
'post__in' => array( '1', '2', '3' ),
);
$expected2['meta_query'][] = array(
$expected3['meta_query'][] = array(
'key' => '_sku',
'value' => array( 'foo', 'bar' ),
'compare' => 'IN',
);
$this->assertEquals( $expected2, $shortcode2->get_query_args() );
$this->assertEquals( $expected3, $shortcode3->get_query_args() );
// product_category shortcode.
$shortcode3 = new WC_Shortcode_Products( array(
$shortcode4 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'menu_order title',
@ -122,7 +124,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'category' => 'clothing',
'operator' => 'IN',
), 'product_category' );
$expected3 = array(
$expected4 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -134,16 +136,17 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'tax_query' => $tax_query,
'meta_key' => '',
);
$expected3['tax_query'][] = array(
$expected4['tax_query'][] = array(
'taxonomy' => 'product_cat',
'terms' => array( 'clothing' ),
'field' => 'slug',
'operator' => 'IN',
);
$this->assertEquals( $expected3, $shortcode3->get_query_args() );
$this->assertEquals( $expected4, $shortcode4->get_query_args() );
// recent_products shortcode.
$shortcode4 = new WC_Shortcode_Products( array(
$shortcode5 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'date',
@ -151,7 +154,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'category' => '',
'operator' => 'IN',
), 'recent_products' );
$expected4 = array(
$expected5 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -163,14 +166,14 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'tax_query' => $tax_query,
);
$this->assertEquals( $expected4, $shortcode4->get_query_args() );
$this->assertEquals( $expected5, $shortcode5->get_query_args() );
// product shortcode.
$shortcode5 = new WC_Shortcode_Products( array(
$shortcode6 = new WC_Shortcode_Products( array(
'ids' => '1',
'per_page' => '1',
), 'product' );
$expected5 = array(
$expected6 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -183,10 +186,10 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'p' => '1',
);
$this->assertEquals( $expected5, $shortcode5->get_query_args() );
$this->assertEquals( $expected6, $shortcode6->get_query_args() );
// sale_products shortcode.
$shortcode6 = new WC_Shortcode_Products( array(
$shortcode7 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'title',
@ -194,7 +197,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'category' => '',
'operator' => 'IN',
), 'sale_products' );
$expected6 = array(
$expected7 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -207,16 +210,16 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'post__in' => array_merge( array( 0 ), wc_get_product_ids_on_sale() ),
);
$this->assertEquals( $expected6, $shortcode6->get_query_args() );
$this->assertEquals( $expected7, $shortcode7->get_query_args() );
// best_selling_products shortcode.
$shortcode7 = new WC_Shortcode_Products( array(
$shortcode8 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'category' => '',
'operator' => 'IN',
), 'best_selling_products' );
$expected7 = array(
$expected8 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -229,10 +232,10 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'meta_key' => 'total_sales',
);
$this->assertEquals( $expected7, $shortcode7->get_query_args() );
$this->assertEquals( $expected8, $shortcode8->get_query_args() );
// top_rated_products shortcode.
$shortcode8 = new WC_Shortcode_Products( array(
$shortcode9 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'title',
@ -240,7 +243,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'category' => '',
'operator' => 'IN',
), 'top_rated_products' );
$expected8 = array(
$expected9 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -252,16 +255,19 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'tax_query' => $tax_query,
);
$this->assertEquals( $expected9, $shortcode9->get_query_args() );
// featured_products shortcode.
$shortcode9 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'date',
'order' => 'DESC',
'category' => '',
'operator' => 'IN',
), 'featured_products' );
$expected9 = array(
$shortcode10 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'date',
'order' => 'DESC',
'category' => '',
'operator' => 'IN',
'visibility' => 'featured',
) );
$expected10 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -272,16 +278,19 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'meta_query' => $meta_query,
'tax_query' => array_merge( $tax_query, array(
array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
'operator' => 'IN',
'taxonomy' => 'product_visibility',
'terms' => 'featured',
'field' => 'name',
'operator' => 'IN',
'include_children' => false,
),
) ),
);
$this->assertEquals( $expected10, $shortcode10->get_query_args() );
// product_attribute shortcode.
$shortcode10 = new WC_Shortcode_Products( array(
$shortcode11 = new WC_Shortcode_Products( array(
'per_page' => '12',
'columns' => '4',
'orderby' => 'title',
@ -289,7 +298,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'attribute' => 'color',
'filter' => 'black',
), 'product_attribute' );
$expected10 = array(
$expected11 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
@ -308,7 +317,97 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
) ),
);
$this->assertEquals( $expected10, $shortcode10->get_query_args() );
$this->assertEquals( $expected11, $shortcode11->get_query_args() );
// Check for visibility shortcode.
$shortcode12 = new WC_Shortcode_Products( array(
'visibility' => 'hidden',
) );
$expected12 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'orderby' => 'title',
'order' => 'ASC',
'posts_per_page' => -1,
'meta_query' => $meta_query,
'tax_query' => array(
array(
'taxonomy' => 'product_visibility',
'terms' => array( 'exclude-from-catalog', 'exclude-from-search' ),
'field' => 'name',
'operator' => 'AND',
'include_children' => false,
),
),
);
$this->assertEquals( $expected12, $shortcode12->get_query_args() );
$shortcode13 = new WC_Shortcode_Products( array(
'visibility' => 'catalog',
) );
$expected13 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'orderby' => 'title',
'order' => 'ASC',
'posts_per_page' => -1,
'meta_query' => $meta_query,
'tax_query' => array(
array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-search',
'field' => 'name',
'operator' => 'IN',
'include_children' => false,
),
array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-catalog',
'field' => 'name',
'operator' => 'NOT IN',
'include_children' => false,
),
),
);
$this->assertEquals( $expected13, $shortcode13->get_query_args() );
$shortcode14 = new WC_Shortcode_Products( array(
'visibility' => 'search',
) );
$expected14 = array(
'post_type' => 'product',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'orderby' => 'title',
'order' => 'ASC',
'posts_per_page' => -1,
'meta_query' => $meta_query,
'tax_query' => array(
array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-catalog',
'field' => 'name',
'operator' => 'IN',
'include_children' => false,
),
array(
'taxonomy' => 'product_visibility',
'terms' => 'exclude-from-search',
'field' => 'name',
'operator' => 'NOT IN',
'include_children' => false,
),
),
);
$this->assertEquals( $expected14, $shortcode14->get_query_args() );
}
/**
@ -330,6 +429,19 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
$this->assertTrue( ! empty( $result ) );
}
/**
* Test: WC_Shortcode_Products::set_product_as_visible.
*/
public function test_set_product_as_visible() {
$shortcode = new WC_Shortcode_Products();
$this->assertFalse( $shortcode->set_product_as_visible( false ) );
$shortcode2 = new WC_Shortcode_Products( array(
'visibility' => 'hidden',
) );
$this->assertTrue( $shortcode2->set_product_as_visible( false ) );
}
/**
* Test: WC_Shortcode_Products::order_by_rating_post_clauses.
*/