2012-05-26 16:25:07 +00:00
< ? php
2017-11-07 08:55:27 +00:00
/**
* Layered nav widget
*
2020-09-01 14:47:41 +00:00
* @ package WooCommerce\Widgets
2018-03-09 20:28:08 +00:00
* @ version 2.6 . 0
2017-11-07 08:55:27 +00:00
*/
2014-11-19 17:12:56 +00:00
2018-03-09 20:10:03 +00:00
defined ( 'ABSPATH' ) || exit ;
2014-11-19 17:12:56 +00:00
2012-05-26 16:25:07 +00:00
/**
2018-03-09 20:10:03 +00:00
* Widget layered nav class .
2012-05-26 16:25:07 +00:00
*/
2013-05-24 15:51:58 +00:00
class WC_Widget_Layered_Nav extends WC_Widget {
2012-08-14 17:37:50 +00:00
/**
2015-11-03 13:31:20 +00:00
* Constructor .
2012-08-14 17:37:50 +00:00
*/
2013-05-24 15:51:58 +00:00
public function __construct () {
2017-07-10 13:36:47 +00:00
$this -> widget_cssclass = 'woocommerce widget_layered_nav woocommerce-widget-layered-nav' ;
2017-08-25 11:07:17 +00:00
$this -> widget_description = __ ( 'Display a list of attributes to filter products in your store.' , 'woocommerce' );
2013-05-24 15:51:58 +00:00
$this -> widget_id = 'woocommerce_layered_nav' ;
2017-08-25 11:07:17 +00:00
$this -> widget_name = __ ( 'Filter Products by Attribute' , 'woocommerce' );
2013-06-20 11:05:07 +00:00
parent :: __construct ();
}
/**
2016-01-06 18:58:38 +00:00
* Updates a particular instance of a widget .
2013-06-20 11:05:07 +00:00
*
* @ see WP_Widget -> update
2014-11-15 01:12:59 +00:00
*
2017-11-07 08:55:27 +00:00
* @ param array $new_instance New Instance .
* @ param array $old_instance Old Instance .
2014-11-15 01:12:59 +00:00
*
2013-06-20 11:05:07 +00:00
* @ return array
*/
public function update ( $new_instance , $old_instance ) {
$this -> init_settings ();
return parent :: update ( $new_instance , $old_instance );
}
/**
2016-01-06 18:58:38 +00:00
* Outputs the settings update form .
2013-06-20 11:05:07 +00:00
*
* @ see WP_Widget -> form
2014-11-15 01:12:59 +00:00
*
2017-11-07 08:55:27 +00:00
* @ param array $instance Instance .
2013-06-20 11:05:07 +00:00
*/
public function form ( $instance ) {
$this -> init_settings ();
2013-11-28 13:12:08 +00:00
parent :: form ( $instance );
2013-06-20 11:05:07 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Init settings after post types are registered .
2013-06-20 11:05:07 +00:00
*/
public function init_settings () {
2014-11-15 01:12:59 +00:00
$attribute_array = array ();
2019-01-04 16:11:36 +00:00
$std_attribute = '' ;
2013-09-12 13:41:02 +00:00
$attribute_taxonomies = wc_get_attribute_taxonomies ();
2013-05-24 15:51:58 +00:00
2016-06-06 16:24:31 +00:00
if ( ! empty ( $attribute_taxonomies ) ) {
2014-11-15 01:12:59 +00:00
foreach ( $attribute_taxonomies as $tax ) {
if ( taxonomy_exists ( wc_attribute_taxonomy_name ( $tax -> attribute_name ) ) ) {
$attribute_array [ $tax -> attribute_name ] = $tax -> attribute_name ;
}
}
2019-01-04 16:11:36 +00:00
$std_attribute = current ( $attribute_array );
2014-11-15 01:12:59 +00:00
}
$this -> settings = array (
2018-03-09 20:10:03 +00:00
'title' => array (
2013-05-24 15:51:58 +00:00
'type' => 'text' ,
'std' => __ ( 'Filter by' , 'woocommerce' ),
2016-08-27 01:46:45 +00:00
'label' => __ ( 'Title' , 'woocommerce' ),
2013-05-24 15:51:58 +00:00
),
2018-03-09 20:10:03 +00:00
'attribute' => array (
2014-11-15 01:12:59 +00:00
'type' => 'select' ,
2019-01-04 16:11:36 +00:00
'std' => $std_attribute ,
2014-11-15 01:12:59 +00:00
'label' => __ ( 'Attribute' , 'woocommerce' ),
2016-08-27 01:46:45 +00:00
'options' => $attribute_array ,
2013-05-24 15:51:58 +00:00
),
'display_type' => array (
2014-11-15 01:12:59 +00:00
'type' => 'select' ,
'std' => 'list' ,
'label' => __ ( 'Display type' , 'woocommerce' ),
2013-05-24 15:51:58 +00:00
'options' => array (
2014-11-15 01:12:59 +00:00
'list' => __ ( 'List' , 'woocommerce' ),
2016-08-27 01:46:45 +00:00
'dropdown' => __ ( 'Dropdown' , 'woocommerce' ),
),
2013-05-24 15:51:58 +00:00
),
2018-03-09 20:10:03 +00:00
'query_type' => array (
2014-11-15 01:12:59 +00:00
'type' => 'select' ,
'std' => 'and' ,
'label' => __ ( 'Query type' , 'woocommerce' ),
2013-05-24 15:51:58 +00:00
'options' => array (
'and' => __ ( 'AND' , 'woocommerce' ),
2016-08-27 01:46:45 +00:00
'or' => __ ( 'OR' , 'woocommerce' ),
),
2013-05-24 15:51:58 +00:00
),
);
2012-05-26 16:25:07 +00:00
}
2019-01-04 16:11:36 +00:00
/**
* Get this widgets taxonomy .
*
* @ param array $instance Array of instance options .
* @ return string
*/
protected function get_instance_taxonomy ( $instance ) {
if ( isset ( $instance [ 'attribute' ] ) ) {
return wc_attribute_taxonomy_name ( $instance [ 'attribute' ] );
}
$attribute_taxonomies = wc_get_attribute_taxonomies ();
if ( ! empty ( $attribute_taxonomies ) ) {
foreach ( $attribute_taxonomies as $tax ) {
if ( taxonomy_exists ( wc_attribute_taxonomy_name ( $tax -> attribute_name ) ) ) {
return wc_attribute_taxonomy_name ( $tax -> attribute_name );
}
}
}
return '' ;
}
/**
* Get this widgets query type .
*
* @ param array $instance Array of instance options .
* @ return string
*/
protected function get_instance_query_type ( $instance ) {
return isset ( $instance [ 'query_type' ] ) ? $instance [ 'query_type' ] : 'and' ;
}
/**
* Get this widgets display type .
*
* @ param array $instance Array of instance options .
* @ return string
*/
protected function get_instance_display_type ( $instance ) {
return isset ( $instance [ 'display_type' ] ) ? $instance [ 'display_type' ] : 'list' ;
}
2012-08-14 17:37:50 +00:00
/**
2016-01-06 18:58:38 +00:00
* Output widget .
2012-08-14 17:37:50 +00:00
*
* @ see WP_Widget
2014-11-15 01:12:59 +00:00
*
2017-11-07 08:55:27 +00:00
* @ param array $args Arguments .
* @ param array $instance Instance .
2012-08-14 17:37:50 +00:00
*/
2013-05-24 15:51:58 +00:00
public function widget ( $args , $instance ) {
2017-11-09 11:16:47 +00:00
if ( ! is_shop () && ! is_product_taxonomy () ) {
2012-08-28 15:18:16 +00:00
return ;
2014-11-15 01:12:59 +00:00
}
2012-08-14 17:37:50 +00:00
2016-02-10 10:19:32 +00:00
$_chosen_attributes = WC_Query :: get_layered_nav_chosen_attributes ();
2019-01-04 16:11:36 +00:00
$taxonomy = $this -> get_instance_taxonomy ( $instance );
$query_type = $this -> get_instance_query_type ( $instance );
$display_type = $this -> get_instance_display_type ( $instance );
2012-05-26 16:25:07 +00:00
2014-11-15 01:12:59 +00:00
if ( ! taxonomy_exists ( $taxonomy ) ) {
2012-08-28 15:18:16 +00:00
return ;
2014-11-15 01:12:59 +00:00
}
2012-08-14 17:37:50 +00:00
2019-01-25 19:55:25 +00:00
$terms = get_terms ( $taxonomy , array ( 'hide_empty' => '1' ) );
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
if ( 0 === count ( $terms ) ) {
2016-02-09 14:41:17 +00:00
return ;
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
ob_start ();
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
$this -> widget_start ( $args , $instance );
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
if ( 'dropdown' === $display_type ) {
2017-07-10 13:36:47 +00:00
wp_enqueue_script ( 'selectWoo' );
2017-02-24 08:18:38 +00:00
wp_enqueue_style ( 'select2' );
2016-02-09 14:41:17 +00:00
$found = $this -> layered_nav_dropdown ( $terms , $taxonomy , $query_type );
} else {
$found = $this -> layered_nav_list ( $terms , $taxonomy , $query_type );
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
$this -> widget_end ( $args );
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// Force found when option is selected - do not force found on taxonomy attributes.
2016-02-09 14:41:17 +00:00
if ( ! is_tax () && is_array ( $_chosen_attributes ) && array_key_exists ( $taxonomy , $_chosen_attributes ) ) {
$found = true ;
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
if ( ! $found ) {
ob_end_clean ();
} else {
2017-11-07 08:55:27 +00:00
echo ob_get_clean (); // @codingStandardsIgnoreLine
2016-02-09 14:41:17 +00:00
}
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
/**
* Return the currently viewed taxonomy name .
2017-11-07 08:55:27 +00:00
*
2016-02-09 14:41:17 +00:00
* @ return string
*/
protected function get_current_taxonomy () {
return is_tax () ? get_queried_object () -> taxonomy : '' ;
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
/**
* Return the currently viewed term ID .
2017-11-07 08:55:27 +00:00
*
2016-02-09 14:41:17 +00:00
* @ return int
*/
protected function get_current_term_id () {
return absint ( is_tax () ? get_queried_object () -> term_id : 0 );
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
/**
* Return the currently viewed term slug .
2017-11-07 08:55:27 +00:00
*
2016-02-09 14:41:17 +00:00
* @ return int
*/
protected function get_current_term_slug () {
return absint ( is_tax () ? get_queried_object () -> slug : 0 );
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
/**
* Show dropdown layered nav .
2017-11-07 08:55:27 +00:00
*
* @ param array $terms Terms .
* @ param string $taxonomy Taxonomy .
* @ param string $query_type Query Type .
2016-02-09 14:41:17 +00:00
* @ return bool Will nav display ?
*/
protected function layered_nav_dropdown ( $terms , $taxonomy , $query_type ) {
2017-07-10 13:36:47 +00:00
global $wp ;
2016-02-09 14:41:17 +00:00
$found = false ;
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
if ( $taxonomy !== $this -> get_current_taxonomy () ) {
2016-06-15 18:20:48 +00:00
$term_counts = $this -> get_filtered_term_product_counts ( wp_list_pluck ( $terms , 'term_id' ), $taxonomy , $query_type );
2017-07-11 10:31:02 +00:00
$_chosen_attributes = WC_Query :: get_layered_nav_chosen_attributes ();
2019-01-23 15:11:27 +00:00
$taxonomy_filter_name = wc_attribute_taxonomy_slug ( $taxonomy );
2016-10-05 18:37:53 +00:00
$taxonomy_label = wc_attribute_label ( $taxonomy );
2018-03-09 20:10:03 +00:00
/* translators: %s: taxonomy name */
$any_label = apply_filters ( 'woocommerce_layered_nav_any_label' , sprintf ( __ ( 'Any %s' , 'woocommerce' ), $taxonomy_label ), $taxonomy_label , $taxonomy );
$multiple = 'or' === $query_type ;
$current_values = isset ( $_chosen_attributes [ $taxonomy ][ 'terms' ] ) ? $_chosen_attributes [ $taxonomy ][ 'terms' ] : array ();
2012-08-14 17:37:50 +00:00
2017-07-10 13:36:47 +00:00
if ( '' === get_option ( 'permalink_structure' ) ) {
$form_action = remove_query_arg ( array ( 'page' , 'paged' ), add_query_arg ( $wp -> query_string , '' , home_url ( $wp -> request ) ) );
2017-02-24 08:18:38 +00:00
} else {
2017-07-10 13:36:47 +00:00
$form_action = preg_replace ( '%\/page/[0-9]+%' , '' , home_url ( trailingslashit ( $wp -> request ) ) );
2017-02-24 08:18:38 +00:00
}
2017-07-10 13:36:47 +00:00
echo '<form method="get" action="' . esc_url ( $form_action ) . '" class="woocommerce-widget-layered-nav-dropdown">' ;
echo '<select class="woocommerce-widget-layered-nav-dropdown dropdown_layered_nav_' . esc_attr ( $taxonomy_filter_name ) . '"' . ( $multiple ? 'multiple="multiple"' : '' ) . '>' ;
2016-10-05 17:45:09 +00:00
echo '<option value="">' . esc_html ( $any_label ) . '</option>' ;
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
foreach ( $terms as $term ) {
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// If on a term page, skip that term in widget list.
2016-02-09 14:41:17 +00:00
if ( $term -> term_id === $this -> get_current_term_id () ) {
continue ;
2012-05-26 16:25:07 +00:00
}
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// Get count based on current view.
2018-03-09 20:10:03 +00:00
$option_is_set = in_array ( $term -> slug , $current_values , true );
$count = isset ( $term_counts [ $term -> term_id ] ) ? $term_counts [ $term -> term_id ] : 0 ;
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// Only show options with count > 0.
2016-02-10 10:02:50 +00:00
if ( 0 < $count ) {
$found = true ;
2016-11-22 18:48:39 +00:00
} elseif ( 0 === $count && ! $option_is_set ) {
2016-02-10 10:02:50 +00:00
continue ;
2016-02-09 14:41:17 +00:00
}
2012-08-14 17:37:50 +00:00
2017-11-21 16:50:33 +00:00
echo '<option value="' . esc_attr ( urldecode ( $term -> slug ) ) . '" ' . selected ( $option_is_set , true , false ) . '>' . esc_html ( $term -> name ) . '</option>' ;
2016-02-09 14:41:17 +00:00
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
echo '</select>' ;
2012-08-14 17:37:50 +00:00
2017-07-10 13:36:47 +00:00
if ( $multiple ) {
2017-11-07 08:56:36 +00:00
echo '<button class="woocommerce-widget-layered-nav-dropdown__submit" type="submit" value="' . esc_attr__ ( 'Apply' , 'woocommerce' ) . '">' . esc_html__ ( 'Apply' , 'woocommerce' ) . '</button>' ;
2017-07-10 13:36:47 +00:00
}
if ( 'or' === $query_type ) {
echo '<input type="hidden" name="query_type_' . esc_attr ( $taxonomy_filter_name ) . '" value="or" />' ;
}
echo '<input type="hidden" name="filter_' . esc_attr ( $taxonomy_filter_name ) . '" value="' . esc_attr ( implode ( ',' , $current_values ) ) . '" />' ;
2017-11-07 08:55:27 +00:00
echo wc_query_string_form_fields ( null , array ( 'filter_' . $taxonomy_filter_name , 'query_type_' . $taxonomy_filter_name ), '' , true ); // @codingStandardsIgnoreLine
2017-07-10 13:36:47 +00:00
echo '</form>' ;
2018-03-09 20:10:03 +00:00
wc_enqueue_js (
"
2017-07-10 13:36:47 +00:00
// Update value on change.
2016-09-01 21:33:36 +00:00
jQuery ( '.dropdown_layered_nav_" . esc_js( $taxonomy_filter_name ) . "' ) . change ( function () {
2016-02-09 14:41:17 +00:00
var slug = jQuery ( this ) . val ();
2017-07-10 13:36:47 +00:00
jQuery ( ':input[name=\"filter_" . esc_js( $taxonomy_filter_name ) . "\"]' ) . val ( slug );
2017-07-11 20:21:42 +00:00
// Submit form on change if standard dropdown.
if ( ! jQuery ( this ) . attr ( 'multiple' ) ) {
jQuery ( this ) . closest ( 'form' ) . submit ();
}
2016-02-09 14:41:17 +00:00
});
2017-02-24 08:18:38 +00:00
// Use Select2 enhancement if possible
2017-07-10 13:36:47 +00:00
if ( jQuery () . selectWoo ) {
2017-02-24 08:18:38 +00:00
var wc_layered_nav_select = function () {
2017-07-10 13:36:47 +00:00
jQuery ( '.dropdown_layered_nav_" . esc_js( $taxonomy_filter_name ) . "' ) . selectWoo ( {
2019-02-20 12:00:47 +00:00
placeholder : decodeURIComponent ( '" . rawurlencode( (string) wp_specialchars_decode( $any_label ) ) . "' ),
2017-07-10 13:36:47 +00:00
minimumResultsForSearch : 5 ,
2017-10-09 10:21:21 +00:00
width : '100%' ,
2017-10-09 18:10:32 +00:00
allowClear : " . ( $multiple ? 'false' : 'true' ) . " ,
language : {
noResults : function () {
2017-10-09 18:37:09 +00:00
return '" . esc_js( _x( ' No matches found ', ' enhanced select ', ' woocommerce ' ) ) . "' ;
2017-10-09 18:10:32 +00:00
}
}
2017-07-10 13:36:47 +00:00
} );
2017-02-24 08:18:38 +00:00
};
wc_layered_nav_select ();
}
2018-03-09 20:10:03 +00:00
"
);
2016-02-09 14:41:17 +00:00
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
return $found ;
}
2012-08-14 17:37:50 +00:00
2016-02-09 21:14:55 +00:00
/**
2016-02-10 11:07:42 +00:00
* Count products within certain terms , taking the main WP query into consideration .
2016-11-17 22:33:24 +00:00
*
2017-06-16 12:35:31 +00:00
* This query allows counts to be generated based on the viewed products , not all products .
*
2017-11-07 08:55:27 +00:00
* @ param array $term_ids Term IDs .
* @ param string $taxonomy Taxonomy .
* @ param string $query_type Query Type .
2016-02-10 11:07:42 +00:00
* @ return array
2016-02-09 21:14:55 +00:00
*/
2016-02-10 11:07:42 +00:00
protected function get_filtered_term_product_counts ( $term_ids , $taxonomy , $query_type ) {
2016-02-10 10:02:50 +00:00
global $wpdb ;
2020-04-30 14:49:36 +00:00
$main_tax_query = $this -> get_main_tax_query ();
$meta_query = $this -> get_main_meta_query ();
2016-02-09 21:14:55 +00:00
2020-04-30 14:49:36 +00:00
$non_variable_tax_query_sql = array ( 'where' => '' );
2020-07-13 13:00:02 +00:00
$is_and_query = 'and' === $query_type ;
2020-04-30 14:49:36 +00:00
foreach ( $main_tax_query as $key => $query ) {
if ( is_array ( $query ) && $taxonomy === $query [ 'taxonomy' ] ) {
if ( $is_and_query ) {
$non_variable_tax_query_sql = $this -> convert_tax_query_to_sql ( array ( $query ) );
2016-02-09 21:14:55 +00:00
}
2020-04-30 14:49:36 +00:00
unset ( $main_tax_query [ $key ] );
2016-02-09 21:14:55 +00:00
}
}
2020-04-30 14:49:36 +00:00
$exclude_variable_products_tax_query_sql = $this -> get_extra_tax_query_sql ( 'product_type' , array ( 'variable' ), 'NOT IN' );
$meta_query_sql = ( new WP_Meta_Query ( $meta_query ) ) -> get_sql ( 'post' , $wpdb -> posts , 'ID' );
$main_tax_query_sql = $this -> convert_tax_query_to_sql ( $main_tax_query );
$term_ids_sql = '(' . implode ( ',' , array_map ( 'absint' , $term_ids ) ) . ')' ;
2016-06-16 11:36:18 +00:00
2020-08-27 08:59:15 +00:00
// Generate the first part of the query.
2020-08-31 10:22:33 +00:00
// This one will return non-variable products and variable products with concrete values for the attributes.
2016-06-16 11:36:18 +00:00
$query = array ();
2020-09-01 14:47:41 +00:00
$query [ 'select' ] = " SELECT IF( { $wpdb -> posts } .post_type='variable_product', { $wpdb -> posts } .post_parent, { $wpdb -> posts } .ID) AS product_id, terms.term_id AS term_count_id " ;
2016-06-16 11:36:18 +00:00
$query [ 'from' ] = " FROM { $wpdb -> posts } " ;
$query [ 'join' ] = "
2020-07-13 13:00:02 +00:00
INNER JOIN { $wpdb -> term_relationships } AS tr ON { $wpdb -> posts } . ID = tr . object_id
2016-06-15 18:20:48 +00:00
INNER JOIN { $wpdb -> term_taxonomy } AS term_taxonomy USING ( term_taxonomy_id )
INNER JOIN { $wpdb -> terms } AS terms USING ( term_id )
2020-04-30 14:49:36 +00:00
{ $main_tax_query_sql [ 'join' ]} { $meta_query_sql [ 'join' ]} " ; // Not an omission, really no more JOINs required.
2020-07-13 13:00:02 +00:00
$variable_where_part = "
OR ({ $wpdb -> posts } . post_type = 'product_variation'
2020-04-30 14:49:36 +00:00
AND NOT EXISTS (
SELECT ID FROM { $wpdb -> posts } AS parent
WHERE parent . ID = { $wpdb -> posts } . post_parent AND parent . post_status NOT IN ( 'publish' )
2020-07-13 13:00:02 +00:00
))
2020-04-30 14:49:36 +00:00
" ;
$search_sql = '' ;
$search = $this -> get_main_search_query_sql ();
if ( $search ) {
$search_sql = ' AND ' . $search ;
}
2016-07-19 13:09:56 +00:00
2018-03-09 20:10:03 +00:00
$query [ 'where' ] = "
2020-04-30 14:49:36 +00:00
WHERE
{ $wpdb -> posts } . post_status = 'publish'
{ $main_tax_query_sql [ 'where' ]} { $meta_query_sql [ 'where' ]}
AND (
(
{ $wpdb -> posts } . post_type = 'product'
{ $exclude_variable_products_tax_query_sql [ 'where' ]}
{ $non_variable_tax_query_sql [ 'where' ]}
)
2020-07-13 13:00:02 +00:00
{ $variable_where_part }
2020-04-30 14:49:36 +00:00
)
AND terms . term_id IN { $term_ids_sql }
{ $search_sql } " ;
$search = $this -> get_main_search_query_sql ();
2018-03-09 20:10:03 +00:00
if ( $search ) {
2016-07-19 13:09:56 +00:00
$query [ 'where' ] .= ' AND ' . $search ;
2016-07-14 01:44:00 +00:00
}
2016-07-19 13:09:56 +00:00
2020-08-31 10:22:33 +00:00
$query = apply_filters ( 'woocommerce_get_filtered_term_product_counts_query' , $query );
$main_query_sql = implode ( ' ' , $query );
2020-08-27 08:59:15 +00:00
// Generate the second part of the query.
2020-08-31 10:22:33 +00:00
// This one will return products having "Any..." as the value of the attribute.
2020-08-27 08:59:15 +00:00
$query_sql_for_attributes_with_any_value = "
2020-09-04 09:29:50 +00:00
SELECT { $wpdb -> posts } . ID AS product_id , { $wpdb -> term_relationships } . term_taxonomy_id as term_count_id FROM { $wpdb -> posts }
JOIN { $wpdb -> posts } variations ON variations . post_parent = { $wpdb -> posts } . ID
LEFT JOIN { $wpdb -> postmeta } ON variations . ID = { $wpdb -> postmeta } . post_id AND { $wpdb -> postmeta } . meta_key = 'attribute_$taxonomy'
JOIN { $wpdb -> term_relationships } ON { $wpdb -> term_relationships } . object_id = { $wpdb -> posts } . ID
2020-09-01 14:47:41 +00:00
WHERE ( { $wpdb -> postmeta } . meta_key IS NULL OR { $wpdb -> postmeta } . meta_value = '' )
2020-09-04 09:29:50 +00:00
AND { $wpdb -> posts } . post_type = 'product'
2020-08-27 08:59:15 +00:00
AND { $wpdb -> posts } . post_status = 'publish'
2020-09-04 09:29:50 +00:00
AND variations . post_status = 'publish'
AND variations . post_type = 'product_variation'
2020-08-27 08:59:15 +00:00
AND { $wpdb -> term_relationships } . term_taxonomy_id in $term_ids_sql
2020-08-31 10:22:33 +00:00
{ $main_tax_query_sql [ 'where' ]} " ;
2020-08-27 08:59:15 +00:00
2020-09-02 14:22:25 +00:00
// We have two queries - let's see if cached results of this query already exist.
$query_hash = md5 ( $main_query_sql . $query_sql_for_attributes_with_any_value );
2018-03-03 05:54:08 +00:00
// Maybe store a transient of the count values.
$cache = apply_filters ( 'woocommerce_layered_nav_count_maybe_cache' , true );
if ( true === $cache ) {
2018-09-20 15:21:39 +00:00
$cached_counts = ( array ) get_transient ( 'wc_layered_nav_counts_' . sanitize_title ( $taxonomy ) );
2018-03-06 15:32:12 +00:00
} else {
2018-03-13 22:34:47 +00:00
$cached_counts = array ();
}
2017-06-16 12:35:31 +00:00
if ( ! isset ( $cached_counts [ $query_hash ] ) ) {
2020-09-02 14:22:25 +00:00
$counts = $this -> get_term_product_counts_from_queries ( $main_query_sql , $query_sql_for_attributes_with_any_value );
2017-06-16 12:35:31 +00:00
$cached_counts [ $query_hash ] = $counts ;
2018-03-03 05:54:08 +00:00
if ( true === $cache ) {
2018-09-20 15:21:39 +00:00
set_transient ( 'wc_layered_nav_counts_' . sanitize_title ( $taxonomy ), $cached_counts , DAY_IN_SECONDS );
2018-03-03 05:54:08 +00:00
}
2017-06-16 12:35:31 +00:00
}
return array_map ( 'absint' , ( array ) $cached_counts [ $query_hash ] );
2016-02-09 21:14:55 +00:00
}
2020-09-02 14:22:25 +00:00
/**
* Get the count of terms for products , using a set of SQL queries that are return pairs of product id - term id .
*
* @ param string ... $queries SQL queries to use , each must return a " product_id " column and a " terms_count_id " column .
*
* @ return array An array where the keys are term ids , and the values are term counts .
*/
private function get_term_product_counts_from_queries ( ... $queries ) {
global $wpdb ;
$total_counts = null ;
foreach ( $queries as $query ) {
$query = "
SELECT COUNT ( DISTINCT ( product_id )) AS term_count , term_count_id FROM (
{ $query }
) AS x GROUP BY term_count_id " ;
$results = $wpdb -> get_results ( $query , ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$counts = array_map ( 'absint' , wp_list_pluck ( $results , 'term_count' , 'term_count_id' ) );
if ( is_null ( $total_counts ) ) {
$total_counts = $counts ;
} else {
foreach ( $counts as $term_id => $term_count ) {
if ( array_key_exists ( $term_id , $total_counts ) ) {
$total_counts [ $term_id ] += $term_count ;
} else {
$total_counts [ $term_id ] = $term_count ;
}
}
}
}
return $total_counts ;
}
2020-04-30 14:49:36 +00:00
/**
* Wrapper for WC_Query :: get_main_tax_query () to ease unit testing .
*
2020-07-09 14:47:14 +00:00
* @ since 4.4 . 0
2020-04-30 14:49:36 +00:00
* @ return array
*/
protected function get_main_tax_query () {
return WC_Query :: get_main_tax_query ();
}
/**
* Wrapper for WC_Query :: get_main_search_query_sql () to ease unit testing .
*
2020-07-09 14:47:14 +00:00
* @ since 4.4 . 0
2020-04-30 14:49:36 +00:00
* @ return string
*/
protected function get_main_search_query_sql () {
return WC_Query :: get_main_search_query_sql ();
}
/**
* Wrapper for WC_Query :: get_main_search_queryget_main_meta_query to ease unit testing .
*
2020-07-09 14:47:14 +00:00
* @ since 4.4 . 0
2020-04-30 14:49:36 +00:00
* @ return array
*/
protected function get_main_meta_query () {
return WC_Query :: get_main_meta_query ();
}
/**
* Get a tax query SQL for a given set of taxonomy , terms and operator .
* Uses an intermediate WP_Tax_Query object .
*
2020-07-09 14:47:14 +00:00
* @ since 4.4 . 0
2020-04-30 14:49:36 +00:00
* @ param string $taxonomy Taxonomy name .
* @ param array $terms Terms to include in the query .
* @ param string $operator Query operator , as supported by WP_Tax_Query ; e . g . " NOT IN " .
*
* @ return array
*/
private function get_extra_tax_query_sql ( $taxonomy , $terms , $operator ) {
$query = array (
array (
'taxonomy' => $taxonomy ,
'field' => 'slug' ,
'terms' => $terms ,
'operator' => $operator ,
'include_children' => false ,
),
);
return $this -> convert_tax_query_to_sql ( $query );
}
/**
* Convert a tax query array to SQL using an intermediate WP_Tax_Query object .
*
2020-07-09 14:47:14 +00:00
* @ since 4.4 . 0
2020-04-30 14:49:36 +00:00
* @ param array $query Query array in the same format accepted by WP_Tax_Query constructor .
*
* @ return array Query SQL as returned by WP_Tax_Query -> get_sql .
*/
private function convert_tax_query_to_sql ( $query ) {
global $wpdb ;
return ( new WP_Tax_Query ( $query ) ) -> get_sql ( $wpdb -> posts , 'ID' );
}
2016-02-09 14:41:17 +00:00
/**
* Show list based layered nav .
2016-11-17 22:33:24 +00:00
*
2017-11-07 08:55:27 +00:00
* @ param array $terms Terms .
* @ param string $taxonomy Taxonomy .
* @ param string $query_type Query Type .
2016-11-17 22:33:24 +00:00
* @ return bool Will nav display ?
2016-02-09 14:41:17 +00:00
*/
protected function layered_nav_list ( $terms , $taxonomy , $query_type ) {
2017-11-07 08:55:27 +00:00
// List display.
2017-07-10 13:36:47 +00:00
echo '<ul class="woocommerce-widget-layered-nav-list">' ;
2014-02-24 15:45:43 +00:00
2016-02-10 11:07:42 +00:00
$term_counts = $this -> get_filtered_term_product_counts ( wp_list_pluck ( $terms , 'term_id' ), $taxonomy , $query_type );
2016-02-10 10:19:32 +00:00
$_chosen_attributes = WC_Query :: get_layered_nav_chosen_attributes ();
$found = false ;
2019-01-30 16:29:01 +00:00
$base_link = $this -> get_current_page_url ();
2012-11-27 16:22:47 +00:00
2016-02-09 14:41:17 +00:00
foreach ( $terms as $term ) {
2016-11-17 22:33:24 +00:00
$current_values = isset ( $_chosen_attributes [ $taxonomy ][ 'terms' ] ) ? $_chosen_attributes [ $taxonomy ][ 'terms' ] : array ();
2018-03-09 20:10:03 +00:00
$option_is_set = in_array ( $term -> slug , $current_values , true );
2016-11-17 22:33:24 +00:00
$count = isset ( $term_counts [ $term -> term_id ] ) ? $term_counts [ $term -> term_id ] : 0 ;
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// Skip the term for the current archive.
2016-02-09 14:41:17 +00:00
if ( $this -> get_current_term_id () === $term -> term_id ) {
continue ;
}
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// Only show options with count > 0.
2016-02-10 10:02:50 +00:00
if ( 0 < $count ) {
$found = true ;
2016-11-17 22:33:24 +00:00
} elseif ( 0 === $count && ! $option_is_set ) {
2016-02-10 10:02:50 +00:00
continue ;
2016-02-09 14:41:17 +00:00
}
2012-08-14 17:37:50 +00:00
2020-04-21 13:52:40 +00:00
$filter_name = 'filter_' . wc_attribute_taxonomy_slug ( $taxonomy );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$current_filter = isset ( $_GET [ $filter_name ] ) ? explode ( ',' , wc_clean ( wp_unslash ( $_GET [ $filter_name ] ) ) ) : array ();
2016-02-09 14:41:17 +00:00
$current_filter = array_map ( 'sanitize_title' , $current_filter );
2012-08-14 17:37:50 +00:00
2018-03-09 20:10:03 +00:00
if ( ! in_array ( $term -> slug , $current_filter , true ) ) {
2016-02-09 14:41:17 +00:00
$current_filter [] = $term -> slug ;
}
2012-08-14 17:37:50 +00:00
2019-01-30 16:29:01 +00:00
$link = remove_query_arg ( $filter_name , $base_link );
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
// Add current filters to URL.
foreach ( $current_filter as $key => $value ) {
2017-11-07 08:55:27 +00:00
// Exclude query arg for current term archive term.
2016-02-09 14:41:17 +00:00
if ( $value === $this -> get_current_term_slug () ) {
unset ( $current_filter [ $key ] );
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
// Exclude self so filter can be unset on click.
if ( $option_is_set && $value === $term -> slug ) {
unset ( $current_filter [ $key ] );
}
}
2012-08-14 17:37:50 +00:00
2016-02-09 14:41:17 +00:00
if ( ! empty ( $current_filter ) ) {
2017-10-27 15:07:30 +00:00
asort ( $current_filter );
2016-02-09 14:41:17 +00:00
$link = add_query_arg ( $filter_name , implode ( ',' , $current_filter ), $link );
2012-08-14 17:37:50 +00:00
2017-11-07 08:55:27 +00:00
// Add Query type Arg to URL.
if ( 'or' === $query_type && ! ( 1 === count ( $current_filter ) && $option_is_set ) ) {
2019-01-23 15:11:27 +00:00
$link = add_query_arg ( 'query_type_' . wc_attribute_taxonomy_slug ( $taxonomy ), 'or' , $link );
2012-05-26 16:25:07 +00:00
}
2017-10-27 14:59:47 +00:00
$link = str_replace ( '%2C' , ',' , $link );
2016-02-09 14:41:17 +00:00
}
2012-08-14 17:37:50 +00:00
2016-10-11 17:23:25 +00:00
if ( $count > 0 || $option_is_set ) {
2019-01-30 13:21:11 +00:00
$link = apply_filters ( 'woocommerce_layered_nav_link' , $link , $term , $taxonomy );
$term_html = '<a rel="nofollow" href="' . esc_url ( $link ) . '">' . esc_html ( $term -> name ) . '</a>' ;
2016-10-11 17:23:25 +00:00
} else {
$link = false ;
$term_html = '<span>' . esc_html ( $term -> name ) . '</span>' ;
}
2016-02-09 14:41:17 +00:00
2016-10-11 17:23:25 +00:00
$term_html .= ' ' . apply_filters ( 'woocommerce_layered_nav_count' , '<span class="count">(' . absint ( $count ) . ')</span>' , $count , $term );
2016-06-08 15:29:27 +00:00
2017-07-10 13:36:47 +00:00
echo '<li class="woocommerce-widget-layered-nav-list__item wc-layered-nav-term ' . ( $option_is_set ? 'woocommerce-widget-layered-nav-list__item--chosen chosen' : '' ) . '">' ;
2020-04-21 13:52:40 +00:00
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.EscapeOutput.OutputNotEscaped
echo apply_filters ( 'woocommerce_layered_nav_term_html' , $term_html , $term , $link , $count );
2016-06-08 15:29:27 +00:00
echo '</li>' ;
2012-05-26 16:25:07 +00:00
}
2016-02-09 14:41:17 +00:00
echo '</ul>' ;
return $found ;
2012-05-26 16:25:07 +00:00
}
2013-05-24 15:51:58 +00:00
}