Ability to make layered nav an "OR" query so you can expand your search rather than filter it. Closes #47.

This commit is contained in:
Mike Jolley 2011-11-15 15:15:12 +00:00
parent 80cfc09956
commit 9ee4460516
4 changed files with 115 additions and 39 deletions

View File

@ -9,11 +9,11 @@
*/
class woocommerce_query {
var $unfiltered_product_ids = array(); // Unfilted product ids (before layered nav etc)
var $filtered_product_ids = array(); // Filted product ids (after layered nav)
var $post__in = array(); // Product id's that match the layered nav + price filter
var $unfiltered_product_ids = array(); // Unfiltered product ids (before layered nav etc)
var $filtered_product_ids = array(); // Filtered product ids (after layered nav)
var $post__in = array(); // Product id's that match the layered nav + price filter
var $meta_query = ''; // The meta query for the page
var $layered_nav_post__in = array(); // posts matching layered nav only
var $layered_nav_post__in = array(); // posts matching layered nav only
var $layered_nav_product_ids = array(); // Stores posts matching layered nav, so price filter can find max price in view
/** constructor */

View File

@ -96,6 +96,7 @@ Yes you can! Join in on our GitHub repository :) https://github.com/woothemes/wo
* Ability to register from my account page
* Option to show size/weight on attributes tab
* Added logger class for debugging
* Ability to make layered nav an "OR" query so you can expand your search rather than filter it
= 1.2.1 - 10/11/2011 =
* Reworked downloadable and virtual products - now variations can be downloadable/virtual too making it more flexible

View File

@ -25,8 +25,16 @@ function woocommerce_layered_nav_init() {
$attribute = strtolower(sanitize_title($tax->attribute_name));
$taxonomy = $woocommerce->attribute_taxonomy_name($attribute);
$name = 'filter_' . $attribute;
$query_type_name = 'query_type_' . $attribute;
if (isset($_GET[$name]) && taxonomy_exists($taxonomy)) $_chosen_attributes[$taxonomy] = explode(',', $_GET[$name] );
if (isset($_GET[$name]) && taxonomy_exists($taxonomy)) :
$_chosen_attributes[$taxonomy]['terms'] = explode(',', $_GET[$name] );
if (isset($_GET[$query_type_name]) && $_GET[$query_type_name]=='or') :
$_chosen_attributes[$taxonomy]['query_type'] = 'or';
else :
$_chosen_attributes[$taxonomy]['query_type'] = 'and';
endif;
endif;
endforeach;
endif;
@ -45,23 +53,50 @@ function woocommerce_layered_nav_query( $filtered_posts ) {
if (sizeof($_chosen_attributes)>0) :
$matched_products = array();
$filtered = false;
$filtered_attribute = false;
foreach ($_chosen_attributes as $attribute => $values) :
if (sizeof($values)>0) :
foreach ($values as $value) :
foreach ($_chosen_attributes as $attribute => $data) :
$matched_products_from_attribute = array();
$filtered = false;
if (sizeof($data['terms'])>0) :
foreach ($data['terms'] as $value) :
$posts = get_objects_in_term( $value, $attribute );
if (!is_wp_error($posts) && (sizeof($matched_products)>0 || $filtered)) :
$matched_products = array_intersect($posts, $matched_products);
elseif (!is_wp_error($posts)) :
$matched_products = $posts;
// AND or OR
if ($data['query_type']=='or') :
if (!is_wp_error($posts) && (sizeof($matched_products_from_attribute)>0 || $filtered)) :
$matched_products_from_attribute = array_merge($posts, $matched_products_from_attribute);
elseif (!is_wp_error($posts)) :
$matched_products_from_attribute = $posts;
endif;
else :
if (!is_wp_error($posts) && (sizeof($matched_products_from_attribute)>0 || $filtered)) :
$matched_products_from_attribute = array_intersect($posts, $matched_products_from_attribute);
elseif (!is_wp_error($posts)) :
$matched_products_from_attribute = $posts;
endif;
endif;
$filtered = true;
endforeach;
endif;
if (sizeof($matched_products)>0 || $filtered_attribute) :
$matched_products = array_intersect($matched_products_from_attribute, $matched_products);
else :
$matched_products = $matched_products_from_attribute;
endif;
$filtered_attribute = true;
endforeach;
if ($filtered) :
@ -119,18 +154,18 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
global $_chosen_attributes, $woocommerce, $wp_query;
$title = $instance['title'];
$taxonomy = $woocommerce->attribute_taxonomy_name($instance['attribute']);
$title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
$taxonomy = $woocommerce->attribute_taxonomy_name($instance['attribute']);
$query_type = (isset($instance['query_type'])) ? $instance['query_type'] : 'and';
if (!taxonomy_exists($taxonomy)) return;
$title = apply_filters('widget_title', $title, $instance, $this->id_base);
$args = array(
'hide_empty' => '1'
);
$terms = get_terms( $taxonomy, $args );
$count = count($terms);
if($count > 0){
$found = false;
@ -155,13 +190,25 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
set_transient( $transient_name, $_products_in_term );
}
$count = sizeof(array_intersect($_products_in_term, $woocommerce->query->filtered_product_ids));
$option_is_set = (isset($_chosen_attributes[$taxonomy]) && in_array($term->term_id, $_chosen_attributes[$taxonomy]['terms']));
// If this is an AND query, only show options with count > 0
if ($query_type=='and') {
$count = sizeof(array_intersect($_products_in_term, $woocommerce->query->filtered_product_ids));
if ($count>0) $found = true;
if ($count>0) $found = true;
$option_is_set = (isset($_chosen_attributes[$taxonomy]) && in_array($term->term_id, $_chosen_attributes[$taxonomy]));
if ($count==0 && !$option_is_set) continue;
if ($count==0 && !$option_is_set) continue;
// If this is an OR query, show all options so search can be expanded
} else {
$count = sizeof(array_intersect($_products_in_term, $woocommerce->query->unfiltered_product_ids));
if ($count>0) $found = true;
}
$class = '';
@ -183,9 +230,10 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
endif;
// All current filters
if ($_chosen_attributes) foreach ($_chosen_attributes as $name => $value) :
if ($_chosen_attributes) foreach ($_chosen_attributes as $name => $data) :
if ($name!==$taxonomy) :
$link = add_query_arg( strtolower(sanitize_title(str_replace('pa_', 'filter_', $name))), implode(',', $value), $link );
$link = add_query_arg( strtolower(sanitize_title(str_replace('pa_', 'filter_', $name))), implode(',', $data['terms']), $link );
if ($data['query_type']=='or') $link = add_query_arg( strtolower(sanitize_title(str_replace('pa_', 'query_type_', $name))), 'or', $link );
endif;
endforeach;
@ -198,7 +246,7 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
endif;
// Current Filter = this widget
if (isset( $_chosen_attributes[$taxonomy] ) && is_array($_chosen_attributes[$taxonomy]) && in_array($term->term_id, $_chosen_attributes[$taxonomy])) :
if (isset( $_chosen_attributes[$taxonomy] ) && is_array($_chosen_attributes[$taxonomy]['terms']) && in_array($term->term_id, $_chosen_attributes[$taxonomy]['terms'])) :
$class = 'class="chosen"';
// Remove this term is $current_filter has more than 1 term filtered
@ -206,6 +254,7 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
$current_filter_without_this = array_diff($current_filter, array($term->term_id));
$link = add_query_arg( $arg, implode(',', $current_filter_without_this), $link );
endif;
else :
$link = add_query_arg( $arg, implode(',', $current_filter), $link );
endif;
@ -220,6 +269,11 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
$link = add_query_arg( 'post_type', $_GET['post_type'], $link );
endif;
// Query type Arg
if ($query_type=='or' && !( sizeof($current_filter) == 1 && isset( $_chosen_attributes[$taxonomy]['terms'] ) && is_array($_chosen_attributes[$taxonomy]['terms']) && in_array($term->term_id, $_chosen_attributes[$taxonomy]['terms']) )) :
$link = add_query_arg( 'query_type_'.strtolower(sanitize_title($instance['attribute'])), 'or', $link );
endif;
echo '<li '.$class.'>';
if ($count>0 || $option_is_set) echo '<a href="'.$link.'">'; else echo '<span>';
@ -253,12 +307,15 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
if (!isset($new_instance['title']) || empty($new_instance['title'])) $new_instance['title'] = $woocommerce->attribute_label($new_instance['attribute']);
$instance['title'] = strip_tags(stripslashes($new_instance['title']));
$instance['attribute'] = stripslashes($new_instance['attribute']);
$instance['query_type'] = stripslashes($new_instance['query_type']);
return $instance;
}
/** @see WP_Widget->form */
function form( $instance ) {
global $woocommerce;
if (!isset($instance['query_type'])) $instance['query_type'] = 'and';
?>
<p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:', 'woothemes') ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id('title') ); ?>" name="<?php echo esc_attr( $this->get_field_name('title') ); ?>" value="<?php if (isset ( $instance['title'])) {echo esc_attr( $instance['title'] );} ?>" /></p>
@ -281,7 +338,13 @@ class WooCommerce_Widget_Layered_Nav extends WP_Widget {
endforeach;
endif;
?>
</select>
</select></p>
<p><label for="<?php echo $this->get_field_id('query_type'); ?>"><?php _e('Query Type:', 'woothemes') ?></label>
<select id="<?php echo esc_attr( $this->get_field_id('query_type') ); ?>" name="<?php echo esc_attr( $this->get_field_name('query_type') ); ?>">
<option value="and" <?php selected($instance['query_type'], 'and'); ?>><?php _e('AND', 'woothemes'); ?></option>
<option value="or" <?php selected($instance['query_type'], 'or'); ?>><?php _e('OR', 'woothemes'); ?></option>
</select></p>
<?php
}
} // class WooCommerce_Widget_Layered_Nav

View File

@ -105,10 +105,10 @@ class WooCommerce_Widget_Price_Filter extends WP_Widget {
function widget( $args, $instance ) {
extract($args);
if (!is_tax( 'product_cat' ) && !is_post_type_archive('product') && !is_tax( 'product_tag' )) return;
global $_chosen_attributes, $wpdb, $woocommerce, $wp_query;
if (!is_tax( 'product_cat' ) && !is_post_type_archive('product') && !is_tax( 'product_tag' )) return;
$title = $instance['title'];
$title = apply_filters('widget_title', $title, $instance, $this->id_base);
@ -120,24 +120,36 @@ class WooCommerce_Widget_Price_Filter extends WP_Widget {
if (get_search_query()) $fields = '<input type="hidden" name="s" value="'.get_search_query().'" />';
if (isset($_GET['post_type'])) $fields .= '<input type="hidden" name="post_type" value="'.esc_attr( $_GET['post_type'] ).'" />';
if ($_chosen_attributes) foreach ($_chosen_attributes as $attribute => $value) :
if ($_chosen_attributes) foreach ($_chosen_attributes as $attribute => $data) :
$fields .= '<input type="hidden" name="'.esc_attr( str_replace('pa_', 'filter_', $attribute) ).'" value="'.esc_attr( implode(',', $value) ).'" />';
$fields .= '<input type="hidden" name="'.esc_attr( str_replace('pa_', 'filter_', $attribute) ).'" value="'.esc_attr( implode(',', $data['terms']) ).'" />';
if ($data['query_type']=='or') $fields .= '<input type="hidden" name="'.esc_attr( str_replace('pa_', 'query_type_', $attribute) ).'" value="or" />';
endforeach;
$min = 0;
if (sizeof($woocommerce->query->layered_nav_product_ids)==0) :
$max = ceil($wpdb->get_var("SELECT max(meta_value + 0)
FROM $wpdb->posts
LEFT JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id
WHERE meta_key = 'price' AND (
$wpdb->posts.ID IN (".implode(',', $woocommerce->query->layered_nav_product_ids).")
OR (
$wpdb->posts.post_parent IN (".implode(',', $woocommerce->query->layered_nav_product_ids).")
AND $wpdb->posts.post_parent != 0
)
)"));
$max = ceil($wpdb->get_var("SELECT max(meta_value + 0)
FROM $wpdb->posts
LEFT JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id
WHERE meta_key = 'price'"));
else :
$max = ceil($wpdb->get_var("SELECT max(meta_value + 0)
FROM $wpdb->posts
LEFT JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id
WHERE meta_key = 'price' AND (
$wpdb->posts.ID IN (".implode(',', $woocommerce->query->layered_nav_product_ids).")
OR (
$wpdb->posts.post_parent IN (".implode(',', $woocommerce->query->layered_nav_product_ids).")
AND $wpdb->posts.post_parent != 0
)
)"));
endif;
echo '<form method="get" action="">
<div class="price_slider_wrapper">