diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php
index 00894d3bc9e..05e2c7ba8f9 100644
--- a/includes/wc-template-functions.php
+++ b/includes/wc-template-functions.php
@@ -148,54 +148,67 @@ function wc_setup_product_data( $post ) {
}
add_action( 'the_post', 'wc_setup_product_data' );
-if ( ! function_exists( 'woocommerce_reset_loop' ) ) {
-
- /**
- * Reset the loop's index and columns when we're done outputting a product loop.
- */
- function woocommerce_reset_loop() {
- $GLOBALS['woocommerce_loop'] = array(
- 'loop' => '',
- 'columns' => '',
- 'name' => '',
- );
+/**
+ * Sets up the woocommerce_loop global from the passed args or from the main query.
+ *
+ * @since 3.3.0
+ * @param array $args Args to pass into the global.
+ */
+function wc_setup_loop( $args = array() ) {
+ if ( isset( $GLOBALS['woocommerce_loop'] ) ) {
+ return; // If the loop has already been setup, bail.
}
+
+ $GLOBALS['woocommerce_loop'] = wp_parse_args( $args, array(
+ 'loop' => 0,
+ 'columns' => wc_get_default_products_per_row(),
+ 'name' => '',
+ 'is_shortcode' => false,
+ 'is_paginated' => true,
+ 'is_search' => $GLOBALS['wp_query']->is_search(),
+ 'is_filtered' => is_filtered(),
+ 'total' => $GLOBALS['wp_query']->found_posts,
+ 'total_pages' => $GLOBALS['wp_query']->max_num_pages,
+ 'per_page' => $GLOBALS['wp_query']->get( 'posts_per_page' ),
+ 'current_page' => max( 1, $GLOBALS['wp_query']->get( 'paged', 1 ) ),
+ ) );
}
-add_filter( 'loop_end', 'woocommerce_reset_loop' );
+add_action( 'woocommerce_before_shop_loop', 'wc_setup_loop' );
/**
- * Products RSS Feed.
+ * Resets the woocommerce_loop global.
*
- * @deprecated 2.6
- * @access public
+ * @since 3.3.0
*/
-function wc_products_rss_feed() {
- // Product RSS.
- if ( is_post_type_archive( 'product' ) || is_singular( 'product' ) ) {
+function wc_reset_loop() {
+ unset( $GLOBALS['woocommerce_loop'] );
+}
+add_action( 'woocommerce_after_shop_loop', 'woocommerce_reset_loop', 999 );
- $feed = get_post_type_archive_feed_link( 'product' );
+/**
+ * Gets a property from the woocommerce_loop global.
+ *
+ * @since 3.3.0
+ * @param string $prop Prop to get.
+ * @param string $default Default if the prop does not exist.
+ * @return mixed
+ */
+function wc_get_loop_prop( $prop, $default = '' ) {
+ return isset( $GLOBALS['woocommerce_loop'], $GLOBALS['woocommerce_loop'][ $prop ] ) ? $GLOBALS['woocommerce_loop'][ $prop ] : $default;
+}
- echo '';
-
- } elseif ( is_tax( 'product_cat' ) ) {
-
- $term = get_term_by( 'slug', esc_attr( get_query_var( 'product_cat' ) ), 'product_cat' );
-
- if ( $term ) {
- $feed = add_query_arg( 'product_cat', $term->slug, get_post_type_archive_feed_link( 'product' ) );
- /* translators: %s: category name */
- echo '';
- }
- } elseif ( is_tax( 'product_tag' ) ) {
-
- $term = get_term_by( 'slug', esc_attr( get_query_var( 'product_tag' ) ), 'product_tag' );
-
- if ( $term ) {
- $feed = add_query_arg( 'product_tag', $term->slug, get_post_type_archive_feed_link( 'product' ) );
- /* translators: %s: tag name */
- echo '';
- }
+/**
+ * Sets a property in the woocommerce_loop global.
+ *
+ * @since 3.3.0
+ * @param string $prop Prop to set.
+ * @param string $value Value to set.
+ */
+function wc_set_loop_prop( $prop, $value = '' ) {
+ if ( ! isset( $GLOBALS['woocommerce_loop'] ) ) {
+ wc_setup_loop();
}
+ $GLOBALS['woocommerce_loop'][ $prop ] = $value;
}
/**
@@ -330,20 +343,21 @@ function wc_get_default_product_rows_per_page() {
}
/**
- * Get classname for loops based on $woocommerce_loop global.
+ * Get classname for woocommerce loops.
*
* @since 2.6.0
* @return string
*/
function wc_get_loop_class() {
- global $woocommerce_loop;
+ $loop_index = wc_get_loop_prop( 'loop', 0 );
+ $columns = wc_get_loop_prop( 'columns', wc_get_default_products_per_row() );
- $woocommerce_loop['loop'] = ! empty( $woocommerce_loop['loop'] ) ? $woocommerce_loop['loop'] + 1 : 1;
- $woocommerce_loop['columns'] = ! empty( $woocommerce_loop['columns'] ) ? $woocommerce_loop['columns'] : wc_get_default_products_per_row();
+ $loop_index ++;
+ wc_set_loop_prop( 'loop', $loop_index );
- if ( 0 === ( $woocommerce_loop['loop'] - 1 ) % $woocommerce_loop['columns'] || 1 === $woocommerce_loop['columns'] ) {
+ if ( 0 === ( $loop_index - 1 ) % $columns || 1 === $columns ) {
return 'first';
- } elseif ( 0 === $woocommerce_loop['loop'] % $woocommerce_loop['columns'] ) {
+ } elseif ( 0 === $loop_index % $columns ) {
return 'last';
} else {
return '';
@@ -513,12 +527,10 @@ if ( ! function_exists( 'woocommerce_content' ) ) {
-
-
-
-
-
-
+
+
+
+
@@ -642,14 +654,16 @@ if ( ! function_exists( 'woocommerce_product_loop_start' ) ) {
function woocommerce_product_loop_start( $echo = true ) {
ob_start();
- $GLOBALS['woocommerce_loop']['loop'] = 0;
+ wc_set_loop_prop( 'loop', 0 );
wc_get_template( 'loop/loop-start.php' );
+ $loop_start = apply_filters( 'woocommerce_product_loop_start', ob_get_clean() );
+
if ( $echo ) {
- echo ob_get_clean(); // WPCS: XSS ok.
+ echo $loop_start; // WPCS: XSS ok.
} else {
- return ob_get_clean();
+ return $loop_start;
}
}
}
@@ -667,10 +681,12 @@ if ( ! function_exists( 'woocommerce_product_loop_end' ) ) {
wc_get_template( 'loop/loop-end.php' );
+ $loop_end = apply_filters( 'woocommerce_product_loop_end', ob_get_clean() );
+
if ( $echo ) {
- echo ob_get_clean(); // WPCS: XSS ok.
+ echo $loop_end; // WPCS: XSS ok.
} else {
- return ob_get_clean();
+ return $loop_end;
}
}
}
@@ -868,29 +884,17 @@ if ( ! function_exists( 'woocommerce_result_count' ) ) {
/**
* Output the result count text (Showing x - x of x results).
- *
- * @param array $args Pass an associative array of parameters. Uses this if passed, otherwise uses global $wp_query.
*/
- function woocommerce_result_count( $args = array() ) {
- if ( ! woocommerce_products_will_display() ) {
- return;
- }
- if ( empty( $args ) ) {
- $query = $GLOBALS['wp_query'];
- $args = array(
- 'total' => $query->found_posts,
- 'per_page' => $query->get( 'posts_per_page' ),
- 'current' => max( 1, $query->get( 'paged', 1 ) ),
+ function woocommerce_result_count() {
+ if ( wc_get_loop_prop( 'is_paginated' ) && woocommerce_products_will_display() ) {
+ $args = array(
+ 'total' => wc_get_loop_prop( 'total' ),
+ 'per_page' => wc_get_loop_prop( 'per_page' ),
+ 'current' => wc_get_loop_prop( 'current_page' ),
);
+
+ wc_get_template( 'loop/result-count.php', $args );
}
-
- $args = wp_parse_args( $args, array(
- 'total' => 0,
- 'per_page' => 0,
- 'current' => 0,
- ) );
-
- wc_get_template( 'loop/result-count.php', $args );
}
}
@@ -903,7 +907,6 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
if ( ! woocommerce_products_will_display() ) {
return;
}
- $query = $GLOBALS['wp_query'];
$orderby = isset( $_GET['orderby'] ) ? wc_clean( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
$show_default_orderby = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
$catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array(
@@ -915,7 +918,7 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
'price-desc' => __( 'Sort by price: high to low', 'woocommerce' ),
) );
- if ( ! is_shortcode_loop() && $GLOBALS['wp_query']->is_search() ) {
+ if ( wc_get_loop_prop( 'is_search' ) ) {
$catalog_orderby_options = array_merge( array( 'relevance' => __( 'Relevance', 'woocommerce' ) ), $catalog_orderby_options );
unset( $catalog_orderby_options['menu_order'] );
if ( 'menu_order' === $orderby ) {
@@ -942,27 +945,24 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
if ( ! function_exists( 'woocommerce_pagination' ) ) {
/**
- * Output the pagination.
- *
- * @param array $args Pass an associative array of parameters. Uses this if passed, otherwise uses global $wp_query.
- */
- function woocommerce_pagination( $args = array() ) {
- if ( empty( $args ) ) {
- $query = $GLOBALS['wp_query'];
- $args = array(
- 'total' => $query->max_num_pages,
- 'current' => max( 1, $query->get( 'paged', 1 ) ),
+ * Output the pagination. */
+ function woocommerce_pagination() {
+ if ( wc_get_loop_prop( 'is_paginated' ) && woocommerce_products_will_display() ) {
+ $args = array(
+ 'total' => wc_get_loop_prop( 'total_pages' ),
+ 'current' => wc_get_loop_prop( 'current_page' ),
);
+
+ if ( wc_get_loop_prop( 'is_shortcode' ) ) {
+ $args['base'] = esc_url_raw( add_query_arg( 'product-page', '%#%', false ) );
+ $args['format'] = '?product-page = %#%';
+ } else {
+ $args['base'] = esc_url_raw( str_replace( 999999999, '%#%', remove_query_arg( 'add-to-cart', get_pagenum_link( 999999999, false ) ) ) );
+ $args['format'] = '';
+ }
+
+ wc_get_template( 'loop/pagination.php', $args );
}
-
- $args = wp_parse_args( $args, array(
- 'total' => 1,
- 'current' => 1,
- 'base' => esc_url_raw( str_replace( 999999999, '%#%', remove_query_arg( 'add-to-cart', get_pagenum_link( 999999999, false ) ) ) ),
- 'format' => '',
- ) );
-
- wc_get_template( 'loop/pagination.php', $args );
}
}
@@ -1211,18 +1211,6 @@ if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) {
wc_get_template( 'single-product/tabs/additional-information.php' );
}
}
-if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
-
- /**
- * Output the reviews tab content.
- *
- * @deprecated 2.4.0 Unused.
- */
- function woocommerce_product_reviews_tab() {
- wc_deprecated_function( 'woocommerce_product_reviews_tab', '2.4' );
- }
-}
-
if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) {
/**
@@ -1397,7 +1385,7 @@ if ( ! function_exists( 'woocommerce_related_products' ) ) {
* @param array $args Provided arguments.
*/
function woocommerce_related_products( $args = array() ) {
- global $product, $woocommerce_loop;
+ global $product;
if ( ! $product ) {
return;
@@ -1419,8 +1407,8 @@ if ( ! function_exists( 'woocommerce_related_products' ) ) {
$args['related_products'] = wc_products_array_orderby( $args['related_products'], $args['orderby'], $args['order'] );
// Set global loop values.
- $woocommerce_loop['name'] = 'related';
- $woocommerce_loop['columns'] = apply_filters( 'woocommerce_related_products_columns', $args['columns'] );
+ wc_set_loop_prop( 'name', 'related' );
+ wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_related_products_columns', $args['columns'] ) );
wc_get_template( 'single-product/related.php', $args );
}
@@ -1437,7 +1425,7 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
* @param string $order Sort direction.
*/
function woocommerce_upsell_display( $limit = '-1', $columns = 4, $orderby = 'rand', $order = 'desc' ) {
- global $product, $woocommerce_loop;
+ global $product;
if ( ! $product ) {
return;
@@ -1449,8 +1437,9 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
'orderby' => $orderby,
'columns' => $columns,
) );
- $woocommerce_loop['name'] = 'up-sells';
- $woocommerce_loop['columns'] = apply_filters( 'woocommerce_upsells_columns', isset( $args['columns'] ) ? $args['columns'] : $columns );
+ wc_set_loop_prop( 'name', 'up-sells' );
+ wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_upsells_columns', isset( $args['columns'] ) ? $args['columns'] : $columns ) );
+
$orderby = apply_filters( 'woocommerce_upsells_orderby', isset( $args['orderby'] ) ? $args['orderby'] : $orderby );
$limit = apply_filters( 'woocommerce_upsells_total', isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : $limit );
@@ -1505,15 +1494,13 @@ if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) {
* @param string $order (default: 'desc').
*/
function woocommerce_cross_sell_display( $limit = 2, $columns = 2, $orderby = 'rand', $order = 'desc' ) {
- global $woocommerce_loop;
-
if ( is_checkout() ) {
return;
}
// Get visible cross sells then sort them at random.
$cross_sells = array_filter( array_map( 'wc_get_product', WC()->cart->get_cross_sells() ), 'wc_products_array_filter_visible' );
- $woocommerce_loop['name'] = 'cross-sells';
- $woocommerce_loop['columns'] = apply_filters( 'woocommerce_cross_sells_columns', $columns );
+ wc_set_loop_prop( 'name', 'cross-sells' );
+ wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_cross_sells_columns', $columns ) );
// Handle orderby and limit results.
$orderby = apply_filters( 'woocommerce_cross_sells_orderby', $orderby );
@@ -1710,12 +1697,29 @@ if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
* @return bool
*/
function woocommerce_products_will_display() {
- if ( is_search() || is_filtered() || is_paged() || is_shortcode_loop() ) {
- return true;
+ return 0 < wc_get_loop_prop( 'total', 0 );
+ }
+}
+
+if ( ! function_exists( 'woocommerce_maybe_show_product_subcategories' ) ) {
+
+ /**
+ * Maybe display categories before, or instead of, a product loop.
+ *
+ * @since 3.3.0
+ * @param string $loop_html HTML.
+ * @return string
+ */
+ function woocommerce_maybe_show_product_subcategories( $loop_html ) {
+ // Don't show when filtering, searching or when on page > 1.
+ if ( 1 < wc_get_loop_prop( 'current_page' ) || wc_get_loop_prop( 'is_search' ) || wc_get_loop_prop( 'is_filtered' ) ) {
+ return $loop_html;
}
+ $parent_id = 0;
$display_type = '';
+ // Check categories are enabled and see what level to query.
if ( is_shop() ) {
$display_type = get_option( 'woocommerce_shop_page_display', '' );
} elseif ( is_product_category() ) {
@@ -1724,17 +1728,20 @@ if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
$display_type = '' === $display_type ? get_option( 'woocommerce_category_archive_display', '' ) : $display_type;
}
- if ( 'subcategories' === $display_type ) {
- if ( is_product_category() ) {
- $term = get_queried_object();
- if ( $term && ! count( get_term_children( $term->term_id, $term->taxonomy ) ) ) {
- return true;
- }
+ // If displaying categories, append to the loop.
+ if ( '' !== $display_type ) {
+ ob_start();
+ woocommerce_product_subcategories( array(
+ 'parent_id' => $parent_id,
+ ) );
+ $loop_html .= ob_get_clean();
+
+ if ( 'subcategories' === $display_type ) {
+ wc_set_loop_prop( 'total', 0 );
}
- return false;
}
- return true;
+ return $loop_html;
}
}
@@ -1751,33 +1758,11 @@ if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
'before' => '',
'after' => '',
'parent_id' => 0,
- 'display_type' => '',
) );
- // Don't show when filtering, searching or when on page > 1.
- if ( is_search() || is_filtered() || is_paged() ) {
- return;
- }
-
- $parent_id = $args['parent_id'];
- $display_type = $args['display_type'];
-
- // Check categories are enabled and see what level to query.
- if ( is_shop() ) {
- $display_type = get_option( 'woocommerce_shop_page_display', '' );
- } elseif ( is_product_category() ) {
- $parent_id = get_queried_object_id();
- $display_type = get_woocommerce_term_meta( $parent_id, 'display_type', true );
- $display_type = '' === $display_type ? get_option( 'woocommerce_category_archive_display', '' ) : $display_type;
- }
-
- if ( '' === $display_type ) {
- return false;
- }
-
// NOTE: using child_of instead of parent - this is not ideal but due to a WP bug ( https://core.trac.wordpress.org/ticket/15626 ) pad_counts won't work.
$product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array(
- 'parent' => $parent_id,
+ 'parent' => $args['parent_id'],
'menu_order' => 'ASC',
'hide_empty' => 0,
'hierarchical' => 1,
@@ -1789,29 +1774,21 @@ if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
$product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' );
}
- if ( $product_categories ) {
- echo wp_kses_post( $args['before'] );
-
- foreach ( $product_categories as $category ) {
- wc_get_template( 'content-product_cat.php', array(
- 'category' => $category,
- ) );
- }
-
- echo wp_kses_post( $args['after'] );
-
- // If we are hiding products disable the loop and pagination.
- if ( 'subcategories' === $display_type ) {
- global $wp_query;
-
- $wp_query->post_count = 0;
- $wp_query->max_num_pages = 0;
- }
-
- return true;
+ if ( ! $product_categories ) {
+ return false;
}
- return false;
+ echo wp_kses_post( $args['before'] );
+
+ foreach ( $product_categories as $category ) {
+ wc_get_template( 'content-product_cat.php', array(
+ 'category' => $category,
+ ) );
+ }
+
+ echo wp_kses_post( $args['after'] );
+
+ return true;
}
}
@@ -2704,3 +2681,35 @@ add_action( 'wp_head', 'wc_page_noindex' );
function wc_get_theme_slug_for_templates() {
return apply_filters( 'woocommerce_theme_slug_for_templates', get_option( 'template' ) );
}
+
+/**
+ * Products RSS Feed.
+ *
+ * @deprecated 2.6
+ */
+function wc_products_rss_feed() {
+ wc_deprecated_function( 'wc_products_rss_feed', '2.6' );
+}
+
+if ( ! function_exists( 'woocommerce_reset_loop' ) ) {
+
+ /**
+ * Reset the loop's index and columns when we're done outputting a product loop.
+ *
+ * @deprecated 3.3
+ */
+ function woocommerce_reset_loop() {
+ wc_reset_loop();
+ }
+}
+
+if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
+ /**
+ * Output the reviews tab content.
+ *
+ * @deprecated 2.4.0 Unused.
+ */
+ function woocommerce_product_reviews_tab() {
+ wc_deprecated_function( 'woocommerce_product_reviews_tab', '2.4' );
+ }
+}