add-to-cart
11 months ago
additional-information
3 years ago
advanced-search
11 months ago
archive-description
3 years ago
archive-products
11 months ago
archive-result-count
2 years ago
archive-title
11 months ago
archive-view-mode
2 years ago
breadcrumbs
4 years ago
call-for-price
11 months ago
cart-table
11 months ago
cart-totals
2 years ago
checkout-coupon-form
3 years ago
checkout-form-additional
3 years ago
checkout-form-billing
2 years ago
checkout-form-login
3 years ago
checkout-form-shipping
2 years ago
checkout-payment
3 years ago
checkout-review-order
2 years ago
checkout-shipping-methods
2 years ago
cross-sells
11 months ago
deal-products
2 years ago
empty-cart-message
2 years ago
filter-orderby
2 years ago
filter-products-per-page
3 years ago
filterable-product-list
11 months ago
init
11 months ago
notice
2 years ago
product-categories
3 years ago
product-category-lists
11 months ago
product-description
2 years ago
product-excerpt
3 years ago
product-image
11 months ago
product-list
2 years ago
product-meta
2 years ago
product-price
3 years ago
product-rating
3 years ago
product-review
2 years ago
product-share
2 years ago
product-sku
3 years ago
product-stock
11 months ago
product-tabs
2 years ago
product-tags
3 years ago
product-title
2 years ago
qr-code
11 months ago
recently-viewed-products
11 months ago
related
11 months ago
return-to-shop
2 years ago
up-sells
11 months ago
view-single-product
3 years ago
lazy-cache.php
3 years ago
manifest.php
11 months ago
prod-short-code.php
2 years ago
products.php
3 years ago
shop.php
3 years ago
widget-helper.php
2 years ago
shop.php
320 lines
| 1 | <?php |
| 2 | |
| 3 | namespace ShopEngine\Widgets; |
| 4 | |
| 5 | defined('ABSPATH') || exit; |
| 6 | |
| 7 | class Shop extends \WC_Shortcode_Products { |
| 8 | |
| 9 | private static $_instance = null; |
| 10 | private $settings = array(); |
| 11 | private $is_added_product_filter = false; |
| 12 | |
| 13 | |
| 14 | public function __construct($settings = array(), $type = 'products') { |
| 15 | |
| 16 | $this->settings = $settings; |
| 17 | $this->type = $type; |
| 18 | |
| 19 | $this->attributes = $this->parse_attributes([ |
| 20 | 'columns' => $settings['columns'], |
| 21 | 'rows' => $settings['rows'], |
| 22 | 'paginate' => $settings['paginate'], |
| 23 | 'class' => $settings['class'], |
| 24 | 'cache' => false, |
| 25 | ]); |
| 26 | |
| 27 | $this->query_args = $this->parse_query_args(); |
| 28 | } |
| 29 | |
| 30 | |
| 31 | protected function get_query_results() { |
| 32 | |
| 33 | $results = parent::get_query_results(); |
| 34 | // Start edit. |
| 35 | if($this->is_added_product_filter) { |
| 36 | remove_action('pre_get_posts', [wc()->query, 'product_query']); |
| 37 | } |
| 38 | |
| 39 | // End edit. |
| 40 | |
| 41 | return $results; |
| 42 | } |
| 43 | |
| 44 | |
| 45 | protected function parse_query_args() { |
| 46 | $settings = $this->settings; |
| 47 | $query_args = [ |
| 48 | 'post_type' => 'product', |
| 49 | 'post_status' => 'publish', |
| 50 | 'ignore_sticky_posts' => true, |
| 51 | 'no_found_rows' => false === wc_string_to_bool($this->attributes['paginate']), |
| 52 | ]; |
| 53 | |
| 54 | if('preview' == $this->settings['query_post_type']) { |
| 55 | $query_args = $GLOBALS['wp_query']->query_vars; |
| 56 | |
| 57 | // Fix for parent::get_transient_name. |
| 58 | $query_args['p'] = 0; |
| 59 | $query_args['post_type'] = 'product'; |
| 60 | $query_args['post_status'] = 'publish'; |
| 61 | $query_args['ignore_sticky_posts'] = true; |
| 62 | $query_args['no_found_rows'] = false === wc_string_to_bool($this->attributes['paginate']); |
| 63 | $query_args['orderby'] = ''; |
| 64 | $query_args['order'] = ''; |
| 65 | |
| 66 | // Allow queries to run e.g. layered nav. |
| 67 | add_action('pre_get_posts', array(wc()->query, 'product_query')); |
| 68 | |
| 69 | $this->is_added_product_filter = true; |
| 70 | |
| 71 | } elseif('current_query' == $this->settings['query_post_type']) { |
| 72 | if(!is_page(wc_get_page_id('shop'))) { |
| 73 | $query_args = $GLOBALS['wp_query']->query_vars; |
| 74 | } |
| 75 | |
| 76 | // Fix for parent::get_transient_name. |
| 77 | if(!isset($query_args['orderby'])) { |
| 78 | $query_args['orderby'] = ''; |
| 79 | $query_args['order'] = ''; |
| 80 | } |
| 81 | |
| 82 | add_action('pre_get_posts', [wc()->query, 'product_query']); |
| 83 | $this->is_added_product_filter = true; |
| 84 | |
| 85 | } else { |
| 86 | $query_args = [ |
| 87 | 'post_type' => 'product', |
| 88 | 'post_status' => 'publish', |
| 89 | 'ignore_sticky_posts' => true, |
| 90 | 'no_found_rows' => false === wc_string_to_bool($this->attributes['paginate']), |
| 91 | 'orderby' => $settings['orderby'], |
| 92 | 'order' => strtoupper($settings['order']), |
| 93 | ]; |
| 94 | |
| 95 | $query_args['meta_query'] = WC()->query->get_meta_query(); |
| 96 | $query_args['tax_query'] = []; |
| 97 | // @codingStandardsIgnoreEnd |
| 98 | |
| 99 | // Visibility. |
| 100 | $this->set_visibility_query_args($query_args); |
| 101 | |
| 102 | // SKUs. |
| 103 | $this->set_featured_query_args($query_args); |
| 104 | |
| 105 | // IDs. |
| 106 | $this->set_ids_query_args($query_args); |
| 107 | |
| 108 | // Set specific types query args. |
| 109 | if(method_exists($this, "set_{$this->type}_query_args")) { |
| 110 | $this->{"set_{$this->type}_query_args"}($query_args); |
| 111 | } |
| 112 | |
| 113 | // Categories. |
| 114 | $this->set_categories_query_args($query_args); |
| 115 | |
| 116 | // Tags. |
| 117 | $this->set_tags_query_args($query_args); |
| 118 | |
| 119 | $query_args = apply_filters('woocommerce_shortcode_products_query', $query_args, $this->attributes, $this->type); |
| 120 | |
| 121 | if('yes' === $settings['paginate'] && 'yes' === $settings['allow_order']) { |
| 122 | $ordering_args = WC()->query->get_catalog_ordering_args(); |
| 123 | } else { |
| 124 | $ordering_args = WC()->query->get_catalog_ordering_args($query_args['orderby'], $query_args['order']); |
| 125 | } |
| 126 | |
| 127 | $query_args['orderby'] = $ordering_args['orderby']; |
| 128 | $query_args['order'] = $ordering_args['order']; |
| 129 | if($ordering_args['meta_key']) { |
| 130 | $query_args['meta_key'] = $ordering_args['meta_key']; |
| 131 | } |
| 132 | |
| 133 | $query_args['posts_per_page'] = intval($settings['columns'] * $settings['rows']); |
| 134 | } // End if(). |
| 135 | |
| 136 | if('yes' === $settings['paginate']) { |
| 137 | //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- It's a fronted user part, not possible to verify nonce here |
| 138 | $page = empty($_GET['product-page']) ? 1 : absint($_GET['product-page']); |
| 139 | |
| 140 | if(1 < $page) { |
| 141 | $query_args['paged'] = $page; |
| 142 | } |
| 143 | |
| 144 | if('yes' !== $settings['allow_order']) { |
| 145 | remove_action('woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30); |
| 146 | } |
| 147 | |
| 148 | if('yes' !== $settings['show_result_count']) { |
| 149 | remove_action('woocommerce_before_shop_loop', 'woocommerce_result_count', 20); |
| 150 | } |
| 151 | } |
| 152 | $query_args['posts_per_page'] = intval($settings['columns'] * $settings['rows']); |
| 153 | |
| 154 | // Always query only IDs. |
| 155 | $query_args['fields'] = 'ids'; |
| 156 | |
| 157 | return $query_args; |
| 158 | } |
| 159 | |
| 160 | |
| 161 | protected function set_ids_query_args(&$query_args) { |
| 162 | switch($this->settings['query_post_type']) { |
| 163 | case 'by_id': |
| 164 | $post__in = $this->settings['query_posts_ids']; |
| 165 | break; |
| 166 | case 'sale': |
| 167 | $post__in = wc_get_product_ids_on_sale(); |
| 168 | break; |
| 169 | } |
| 170 | |
| 171 | if(!empty($post__in)) { |
| 172 | $query_args['post__in'] = $post__in; |
| 173 | remove_action('pre_get_posts', [wc()->query, 'product_query']); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | |
| 178 | protected function set_categories_query_args(&$query_args) { |
| 179 | $query_type = $this->settings['query_post_type']; |
| 180 | |
| 181 | if('by_id' === $query_type || 'current_query' === $query_type) { |
| 182 | return; |
| 183 | } |
| 184 | |
| 185 | if(!empty($this->settings['query_product_cat_ids'])) { |
| 186 | $query_args['tax_query'][] = [ |
| 187 | 'taxonomy' => 'product_cat', |
| 188 | 'terms' => $this->settings['query_product_cat_ids'], |
| 189 | 'field' => 'term_id', |
| 190 | ]; |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | |
| 195 | protected function set_tags_query_args(&$query_args) { |
| 196 | $query_type = $this->settings['query_post_type']; |
| 197 | |
| 198 | if('by_id' === $query_type || 'current_query' === $query_type) { |
| 199 | return; |
| 200 | } |
| 201 | |
| 202 | if(!empty($this->settings['query_product_tag_ids'])) { |
| 203 | $query_args['tax_query'][] = [ |
| 204 | 'taxonomy' => 'product_tag', |
| 205 | 'terms' => $this->settings['query_product_tag_ids'], |
| 206 | 'field' => 'term_id', |
| 207 | 'operator' => 'IN', |
| 208 | ]; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | |
| 213 | protected function set_featured_query_args(&$query_args) { |
| 214 | if('featured' === $this->settings['query_post_type']) { |
| 215 | $product_visibility_term_ids = wc_get_product_visibility_term_ids(); |
| 216 | |
| 217 | $query_args['tax_query'][] = [ |
| 218 | 'taxonomy' => 'product_visibility', |
| 219 | 'field' => 'term_taxonomy_id', |
| 220 | 'terms' => [$product_visibility_term_ids['featured']], |
| 221 | ]; |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | |
| 226 | /** |
| 227 | * Loop over found products. |
| 228 | * |
| 229 | * @since 3.2.0 |
| 230 | * @return string |
| 231 | */ |
| 232 | protected function product_loop() { |
| 233 | $columns = absint($this->attributes['columns']); |
| 234 | $classes = $this->get_wrapper_classes($columns); |
| 235 | $products = $this->get_query_results(); |
| 236 | |
| 237 | ob_start(); |
| 238 | |
| 239 | if($products && $products->ids) { |
| 240 | // Prime caches to reduce future queries. |
| 241 | if(is_callable('_prime_post_caches')) { |
| 242 | _prime_post_caches($products->ids); |
| 243 | } |
| 244 | |
| 245 | // Setup the loop. |
| 246 | wc_setup_loop( |
| 247 | array( |
| 248 | 'columns' => $columns, |
| 249 | 'name' => $this->type, |
| 250 | 'is_shortcode' => true, |
| 251 | 'is_search' => false, |
| 252 | 'is_paginated' => wc_string_to_bool($this->attributes['paginate']), |
| 253 | 'total' => $products->total, |
| 254 | 'total_pages' => $products->total_pages, |
| 255 | 'per_page' => $products->per_page, |
| 256 | 'current_page' => $products->current_page, |
| 257 | ) |
| 258 | ); |
| 259 | |
| 260 | $original_post = $GLOBALS['post']; |
| 261 | |
| 262 | do_action("woocommerce_shortcode_before_{$this->type}_loop", $this->attributes); |
| 263 | |
| 264 | // Fire standard shop loop hooks when paginating results so we can show result counts and so on. |
| 265 | if(wc_string_to_bool($this->attributes['paginate'])) { |
| 266 | |
| 267 | if(wc_string_to_bool($this->settings['allow_order'])) { |
| 268 | woocommerce_catalog_ordering(); |
| 269 | } |
| 270 | if(wc_string_to_bool($this->settings['show_result_count'])) { |
| 271 | woocommerce_result_count(); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | woocommerce_product_loop_start(); |
| 276 | |
| 277 | if(wc_get_loop_prop('total')) { |
| 278 | foreach($products->ids as $product_id) { |
| 279 | $GLOBALS['post'] = get_post($product_id); // WPCS: override ok. |
| 280 | setup_postdata($GLOBALS['post']); |
| 281 | |
| 282 | // Set custom product visibility when quering hidden products. |
| 283 | add_action('woocommerce_product_is_visible', array($this, 'set_product_as_visible')); |
| 284 | |
| 285 | // Render product template. |
| 286 | wc_get_template_part('content', 'product'); |
| 287 | |
| 288 | // Restore product visibility. |
| 289 | remove_action('woocommerce_product_is_visible', array($this, 'set_product_as_visible')); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | $GLOBALS['post'] = $original_post; // WPCS: override ok. |
| 294 | woocommerce_product_loop_end(); |
| 295 | |
| 296 | // Fire standard shop loop hooks when paginating results so we can show result counts and so on. |
| 297 | if(wc_string_to_bool($this->attributes['paginate'])) { |
| 298 | if($this->settings['query_post_type'] == 'preview') { |
| 299 | woocommerce_pagination(); |
| 300 | } else { |
| 301 | do_action('woocommerce_after_shop_loop'); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | do_action("woocommerce_shortcode_after_{$this->type}_loop", $this->attributes); |
| 306 | |
| 307 | wp_reset_postdata(); |
| 308 | wc_reset_loop(); |
| 309 | } else { |
| 310 | if($this->settings['nothing_found_message']) { |
| 311 | echo '<div class="woocommerce-info elementor-nothing-found elementor-products-nothing-found">' . esc_html($this->settings['nothing_found_message']) . '</div>'; |
| 312 | } else { |
| 313 | do_action("woocommerce_shortcode_{$this->type}_loop_no_results", $this->attributes); |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | return '<div class="' . esc_attr(implode(' ', $classes)) . '">' . ob_get_clean() . '</div>'; |
| 318 | } |
| 319 | |
| 320 | } |