1 <?php
  2 /**
  3  * Layered Navigation Widget
  4  *
  5  * @author      WooThemes
  6  * @category    Widgets
  7  * @package     WooCommerce/Widgets
  8  * @version     2.1.0
  9  * @extends     WC_Widget
 10  */
 11 
 12 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 13 
 14 class WC_Widget_Layered_Nav extends WC_Widget {
 15 
 16     /**
 17      * Constructor
 18      */
 19     public function __construct() {
 20         $this->widget_cssclass    = 'woocommerce widget_layered_nav';
 21         $this->widget_description = __( 'Shows a custom attribute in a widget which lets you narrow down the list of products when viewing product categories.', 'woocommerce' );
 22         $this->widget_id          = 'woocommerce_layered_nav';
 23         $this->widget_name        = __( 'WooCommerce Layered Nav', 'woocommerce' );
 24 
 25         parent::__construct();
 26     }
 27 
 28     /**
 29      * update function.
 30      *
 31      * @see WP_Widget->update
 32      * @access public
 33      * @param array $new_instance
 34      * @param array $old_instance
 35      * @return array
 36      */
 37     public function update( $new_instance, $old_instance ) {
 38         $this->init_settings();
 39         return parent::update( $new_instance, $old_instance );
 40     }
 41 
 42     /**
 43      * form function.
 44      *
 45      * @see WP_Widget->form
 46      * @access public
 47      * @param array $instance
 48      * @return void
 49      */
 50     public function form( $instance ) {
 51         $this->init_settings();
 52         parent::form( $instance );
 53     }
 54 
 55     /**
 56      * Init settings after post types are registered
 57      */
 58     public function init_settings() {
 59         $attribute_array = array();
 60         $attribute_taxonomies = wc_get_attribute_taxonomies();
 61             if ( $attribute_taxonomies )
 62                 foreach ( $attribute_taxonomies as $tax )
 63                     if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) )
 64                         $attribute_array[ $tax->attribute_name ] = $tax->attribute_name;
 65 
 66         $this->settings           = array(
 67             'title'  => array(
 68                 'type'  => 'text',
 69                 'std'   => __( 'Filter by', 'woocommerce' ),
 70                 'label' => __( 'Title', 'woocommerce' )
 71             ),
 72             'attribute' => array(
 73                 'type'  => 'select',
 74                 'std'   => '',
 75                 'label' => __( 'Attribute', 'woocommerce' ),
 76                 'options' => $attribute_array
 77             ),
 78             'display_type' => array(
 79                 'type'  => 'select',
 80                 'std'   => 'list',
 81                 'label' => __( 'Display type', 'woocommerce' ),
 82                 'options' => array(
 83                     'list'      => __( 'List', 'woocommerce' ),
 84                     'dropdown'  => __( 'Dropdown', 'woocommerce' )
 85                 )
 86             ),
 87             'query_type' => array(
 88                 'type'  => 'select',
 89                 'std'   => 'and',
 90                 'label' => __( 'Query type', 'woocommerce' ),
 91                 'options' => array(
 92                     'and' => __( 'AND', 'woocommerce' ),
 93                     'or'  => __( 'OR', 'woocommerce' )
 94                 )
 95             ),
 96         );
 97     }
 98 
 99     /**
100      * widget function.
101      *
102      * @see WP_Widget
103      * @access public
104      * @param array $args
105      * @param array $instance
106      * @return void
107      */
108     public function widget( $args, $instance ) {
109         global $_chosen_attributes;
110 
111         extract( $args );
112 
113         if ( ! is_post_type_archive( 'product' ) && ! is_tax( get_object_taxonomies( 'product' ) ) )
114             return;
115 
116         $current_term   = is_tax() ? get_queried_object()->term_id : '';
117         $current_tax    = is_tax() ? get_queried_object()->taxonomy : '';
118         $title          = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
119         $taxonomy       = isset( $instance['attribute'] ) ? wc_attribute_taxonomy_name($instance['attribute']) : '';
120         $query_type     = isset( $instance['query_type'] ) ? $instance['query_type'] : 'and';
121         $display_type   = isset( $instance['display_type'] ) ? $instance['display_type'] : 'list';
122 
123         if ( ! taxonomy_exists( $taxonomy ) )
124             return;
125 
126         $get_terms_args = array( 'hide_empty' => '1' );
127 
128         $orderby = wc_attribute_orderby( $taxonomy );
129 
130         switch ( $orderby ) {
131             case 'name' :
132                 $get_terms_args['orderby']    = 'name';
133                 $get_terms_args['menu_order'] = false;
134             break;
135             case 'id' :
136                 $get_terms_args['orderby']    = 'id';
137                 $get_terms_args['order']      = 'ASC';
138                 $get_terms_args['menu_order'] = false;
139             break;
140             case 'menu_order' :
141                 $get_terms_args['menu_order'] = 'ASC';
142             break;
143         }
144 
145         $terms = get_terms( $taxonomy, $get_terms_args );
146 
147         if ( count( $terms ) > 0 ) {
148 
149             ob_start();
150 
151             $found = false;
152 
153             echo $before_widget . $before_title . $title . $after_title;
154 
155             // Force found when option is selected - do not force found on taxonomy attributes
156             if ( ! is_tax() && is_array( $_chosen_attributes ) && array_key_exists( $taxonomy, $_chosen_attributes ) )
157                 $found = true;
158 
159             if ( $display_type == 'dropdown' ) {
160 
161                 // skip when viewing the taxonomy
162                 if ( $current_tax && $taxonomy == $current_tax ) {
163 
164                     $found = false;
165 
166                 } else {
167 
168                     $taxonomy_filter = str_replace( 'pa_', '', $taxonomy );
169 
170                     $found = false;
171 
172                     echo '<select id="dropdown_layered_nav_' . $taxonomy_filter . '">';
173 
174                     echo '<option value="">' . sprintf( __( 'Any %s', 'woocommerce' ), wc_attribute_label( $taxonomy ) ) .'</option>';
175 
176                     foreach ( $terms as $term ) {
177 
178                         // If on a term page, skip that term in widget list
179                         if ( $term->term_id == $current_term )
180                             continue;
181 
182                         // Get count based on current view - uses transients
183                         $transient_name = 'wc_ln_count_' . md5( sanitize_key( $taxonomy ) . sanitize_key( $term->term_id ) );
184 
185                         if ( false === ( $_products_in_term = get_transient( $transient_name ) ) ) {
186 
187                             $_products_in_term = get_objects_in_term( $term->term_id, $taxonomy );
188 
189                             set_transient( $transient_name, $_products_in_term, YEAR_IN_SECONDS );
190                         }
191 
192                         $option_is_set = ( isset( $_chosen_attributes[ $taxonomy ] ) && in_array( $term->term_id, $_chosen_attributes[ $taxonomy ]['terms'] ) );
193 
194                         // If this is an AND query, only show options with count > 0
195                         if ( $query_type == 'and' ) {
196 
197                             $count = sizeof( array_intersect( $_products_in_term, WC()->query->filtered_product_ids ) );
198 
199                             if ( $count > 0 )
200                                 $found = true;
201 
202                             if ( $count == 0 && ! $option_is_set )
203                                 continue;
204 
205                         // If this is an OR query, show all options so search can be expanded
206                         } else {
207 
208                             $count = sizeof( array_intersect( $_products_in_term, WC()->query->unfiltered_product_ids ) );
209 
210                             if ( $count > 0 )
211                                 $found = true;
212 
213                         }
214 
215                         echo '<option value="' . esc_attr( $term->term_id ) . '" '.selected( isset( $_GET[ 'filter_' . $taxonomy_filter ] ) ? $_GET[ 'filter_' .$taxonomy_filter ] : '' , $term->term_id, false ) . '>' . $term->name . '</option>';
216                     }
217 
218                     echo '</select>';
219 
220                     wc_enqueue_js("
221 
222                         jQuery('#dropdown_layered_nav_$taxonomy_filter').change(function(){
223 
224                             location.href = '" . esc_url_raw( preg_replace( '%\/page/[0-9]+%', '', add_query_arg('filtering', '1', remove_query_arg( array( 'page', 'filter_' . $taxonomy_filter ) ) ) ) ) . "&filter_$taxonomy_filter=' + jQuery('#dropdown_layered_nav_$taxonomy_filter').val();
225 
226                         });
227 
228                     ");
229 
230                 }
231 
232             } else {
233 
234                 // List display
235                 echo "<ul>";
236 
237                 foreach ( $terms as $term ) {
238 
239                     // Get count based on current view - uses transients
240                     $transient_name = 'wc_ln_count_' . md5( sanitize_key( $taxonomy ) . sanitize_key( $term->term_id ) );
241 
242                     if ( false === ( $_products_in_term = get_transient( $transient_name ) ) ) {
243 
244                         $_products_in_term = get_objects_in_term( $term->term_id, $taxonomy );
245 
246                         set_transient( $transient_name, $_products_in_term );
247                     }
248 
249                     $option_is_set = ( isset( $_chosen_attributes[ $taxonomy ] ) && in_array( $term->term_id, $_chosen_attributes[ $taxonomy ]['terms'] ) );
250 
251                     // skip the term for the current archive
252                     if ( $current_term == $term->term_id )
253                         continue;
254 
255                     // If this is an AND query, only show options with count > 0
256                     if ( $query_type == 'and' ) {
257 
258                         $count = sizeof( array_intersect( $_products_in_term, WC()->query->filtered_product_ids ) );
259 
260                         if ( $count > 0 && $current_term !== $term->term_id )
261                             $found = true;
262 
263                         if ( $count == 0 && ! $option_is_set )
264                             continue;
265 
266                     // If this is an OR query, show all options so search can be expanded
267                     } else {
268 
269                             $count = sizeof( array_intersect( $_products_in_term, WC()->query->unfiltered_product_ids ) );
270 
271                             if ( $count > 0 )
272                                 $found = true;
273 
274                     }
275 
276                     $arg = 'filter_' . sanitize_title( $instance['attribute'] );
277 
278                     $current_filter = ( isset( $_GET[ $arg ] ) ) ? explode( ',', $_GET[ $arg ] ) : array();
279 
280                     if ( ! is_array( $current_filter ) )
281                         $current_filter = array();
282 
283                     $current_filter = array_map( 'esc_attr', $current_filter );
284 
285                     if ( ! in_array( $term->term_id, $current_filter ) )
286                         $current_filter[] = $term->term_id;
287 
288                     // Base Link decided by current page
289                     if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
290                         $link = home_url();
291                     } elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id('shop') ) ) {
292                         $link = get_post_type_archive_link( 'product' );
293                     } else {
294                         $link = get_term_link( get_query_var('term'), get_query_var('taxonomy') );
295                     }
296 
297                     // All current filters
298                     if ( $_chosen_attributes ) {
299                         foreach ( $_chosen_attributes as $name => $data ) {
300                             if ( $name !== $taxonomy ) {
301 
302                                 // Exclude query arg for current term archive term
303                                 while ( in_array( $current_term, $data['terms'] ) ) {
304                                     $key = array_search( $current_term, $data );
305                                     unset( $data['terms'][$key] );
306                                 }
307 
308                                 // Remove pa_ and sanitize
309                                 $filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
310 
311                                 if ( ! empty( $data['terms'] ) )
312                                     $link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
313 
314                                 if ( $data['query_type'] == 'or' )
315                                     $link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
316                             }
317                         }
318                     }
319 
320                     // Min/Max
321                     if ( isset( $_GET['min_price'] ) )
322                         $link = add_query_arg( 'min_price', $_GET['min_price'], $link );
323 
324                     if ( isset( $_GET['max_price'] ) )
325                         $link = add_query_arg( 'max_price', $_GET['max_price'], $link );
326 
327                     // Orderby
328                     if ( isset( $_GET['orderby'] ) )
329                         $link = add_query_arg( 'orderby', $_GET['orderby'], $link );
330 
331                     // Current Filter = this widget
332                     if ( isset( $_chosen_attributes[ $taxonomy ] ) && is_array( $_chosen_attributes[ $taxonomy ]['terms'] ) && in_array( $term->term_id, $_chosen_attributes[ $taxonomy ]['terms'] ) ) {
333 
334                         $class = 'class="chosen"';
335 
336                         // Remove this term is $current_filter has more than 1 term filtered
337                         if ( sizeof( $current_filter ) > 1 ) {
338                             $current_filter_without_this = array_diff( $current_filter, array( $term->term_id ) );
339                             $link = add_query_arg( $arg, implode( ',', $current_filter_without_this ), $link );
340                         }
341 
342                     } else {
343 
344                         $class = '';
345                         $link = add_query_arg( $arg, implode( ',', $current_filter ), $link );
346 
347                     }
348 
349                     // Search Arg
350                     if ( get_search_query() )
351                         $link = add_query_arg( 's', get_search_query(), $link );
352 
353                     // Post Type Arg
354                     if ( isset( $_GET['post_type'] ) )
355                         $link = add_query_arg( 'post_type', $_GET['post_type'], $link );
356 
357                     // Query type Arg
358                     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'] ) ) )
359                         $link = add_query_arg( 'query_type_' . sanitize_title( $instance['attribute'] ), 'or', $link );
360 
361                     echo '<li ' . $class . '>';
362 
363                     echo ( $count > 0 || $option_is_set ) ? '<a href="' . esc_url( apply_filters( 'woocommerce_layered_nav_link', $link ) ) . '">' : '<span>';
364 
365                     echo $term->name;
366 
367                     echo ( $count > 0 || $option_is_set ) ? '</a>' : '</span>';
368 
369                     echo ' <small class="count">' . $count . '</small></li>';
370 
371                 }
372 
373                 echo "</ul>";
374 
375             } // End display type conditional
376 
377             echo $after_widget;
378 
379             if ( ! $found )
380                 ob_end_clean();
381             else
382                 echo ob_get_clean();
383         }
384     }
385 }
386 
387 register_widget( 'WC_Widget_Layered_Nav' );
388 
WooCommerce API documentation generated by ApiGen 2.8.0