PluginProbe
Orderable – Restaurant & Food Ordering System / 0.2.0
Orderable – Restaurant & Food Ordering System v0.2.0
0.1.4 0.1.5 0.2.0 1.0.0 1.1.0 1.1.1 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.13.0 1.14.0 1.15.0 1.16.0 1.17.0 1.17.1 1.18.0 1.19.0 1.19.1 1.19.2 1.2.0 1.20.0 1.20.1 All 44 releases
orderable / inc / class-products.php

class-products.php in Orderable – Restaurant & Food Ordering System 0.2.0, at inc/class-products.php

536 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Product methods.
4 *
5 * @package Orderable/Classes
6 */
7
8 defined( 'ABSPATH' ) || exit;
9
10 /**
11 * Products class.
12 */
13 class Orderable_Products {
14 /**
15 * Init
16 */
17 public static function run() {
18 add_filter( 'woocommerce_format_price_range', array( __CLASS__, 'format_price_range' ), 10, 3 );
19 add_filter( 'woocommerce_product_is_visible', array( __CLASS__, 'set_product_visibility' ), 10, 2 );
20 add_action( 'template_redirect', array( __CLASS__, 'products_404' ) );
21 add_filter( 'woocommerce_cart_item_permalink', array( __CLASS__, 'disable_cart_link' ), 10, 3 );
22 add_filter( 'woocommerce_product_query_tax_query', array( __CLASS__, 'remove_hidden_categories_from_products_query' ), 10, 2 );
23 add_filter( 'get_terms_args', array( __CLASS__, 'remove_hidden_categories_from_terms_query' ), 10, 2 );
24 }
25
26 /**
27 * Format price range.
28 *
29 * @param string $price
30 * @param float $from
31 * @param float $to
32 *
33 * @return string
34 */
35 public static function format_price_range( $price, $from, $to ) {
36 return sprintf( '%s: %s', __( 'From', 'iconic' ), wc_price( $from ) );
37 }
38
39 /**
40 * Get products, sorted by category.
41 *
42 * @param array $args
43 *
44 * @return array
45 */
46 public static function get_products_by_category( $args = array() ) {
47 // Disable this filter as we don't want to disable categories during our own calls to them.
48 remove_filter( 'get_terms_args', array( __CLASS__, 'remove_hidden_categories_from_terms_query' ), 10 );
49
50 $categories = ! empty( $args['categories'] ) ? $args['categories'] : null;
51 $categories = is_string( $categories ) ? array_filter( explode( ',', $categories ) ) : $categories;
52 $has_categories = ! empty( $categories );
53
54 $categories = self::order_categories_by_menu_order( $categories );
55
56 $products = array();
57
58 if ( $has_categories ) {
59 foreach ( $categories as $category_id ) {
60 $category = get_term( $category_id, 'product_cat' );
61
62 if ( is_wp_error( $category ) || empty( $category ) ) {
63 continue;
64 }
65
66 $products[ $category_id ] = array(
67 'category' => array(
68 'name' => $category->name,
69 'slug' => $category->slug,
70 'description' => $category->description,
71 'depth' => 0,
72 'children' => null,
73 ),
74 'products' => array(),
75 );
76
77 $children_categories = get_terms(
78 array(
79 'taxonomy' => 'product_cat',
80 'orderby' => 'menu_order',
81 'hide_empty' => true,
82 'parent' => $category->term_id,
83 )
84 );
85
86 if ( ! empty( $children_categories ) ) {
87 $products[ $category_id ]['category']['children'] = array();
88
89 foreach ( $children_categories as $child_category ) {
90 $category_products = self::get_products(
91 array(
92 'limit' => - 1,
93 'category' => array( $child_category->slug ),
94 'orderby' => 'menu_order',
95 )
96 );
97
98 if ( ! empty( $category_products ) ) {
99 $products[ $category_id ]['category']['children'][ $child_category->term_id ] = array(
100 'category' => array(
101 'name' => $child_category->name,
102 'description' => $child_category->description,
103 'depth' => 1,
104 ),
105 'products' => $category_products,
106 );
107 }
108 }
109 } else {
110 $category_products = self::get_products(
111 array(
112 'limit' => - 1,
113 'category' => array( $category->slug ),
114 'orderby' => 'menu_order',
115 )
116 );
117
118 if ( ! empty( $category_products ) ) {
119 $products[ $category_id ]['products'] = $category_products;
120 }
121 }
122 }
123 } else {
124 $category_products = self::get_products(
125 array(
126 'limit' => - 1,
127 'orderby' => 'menu_order',
128 )
129 );
130
131 if ( ! empty( $category_products ) ) {
132 $products[] = array(
133 'category' => null,
134 'products' => $category_products,
135 );
136 }
137 }
138
139 // Remove categories with no products.
140 foreach ( $products as $key => $product_collection ) {
141 if ( ! empty( $product_collection['products'] ) || ! empty( $product_collection['category']['children'] ) ) {
142 continue;
143 }
144
145 unset( $products[ $key ] );
146 }
147
148 // Turn this back on to re-disable hidden categories.
149 add_filter( 'get_terms_args', array( __CLASS__, 'remove_hidden_categories_from_terms_query' ), 10, 2 );
150
151 $products = self::maybe_flatten_products_by_category( array_filter( $products ), $args );
152
153 return apply_filters( 'orderable_get_products_by_category', $products, $args );
154 }
155
156 /**
157 * Get Products.
158 *
159 * A wrapper around the WooCommerce `wc_get_products` and `wc_products_array_orderby` functions.
160 *
161 * @param array $args Arguments.
162 * @return array
163 */
164 public static function get_products( $args ) {
165 $products = wc_get_products( $args );
166
167 if ( ! empty( $products ) && isset( $args['orderby'] ) ) {
168 $order = isset( $args['order'] ) ? $args['order'] : 'asc';
169 $products = wc_products_array_orderby( $products, $args['orderby'], $order );
170 }
171
172 return apply_filters( 'orderable_get_products', $products, $args );
173 }
174
175 /**
176 * Order Categories by menu order
177 *
178 * @param array $categories Categories.
179 * @return array
180 */
181 public static function order_categories_by_menu_order( $categories ) {
182 $categories_ordered = array();
183 $category_terms = get_terms(
184 array(
185 'taxonomy' => 'product_cat',
186 'orderby' => 'menu_order',
187 'hide_empty' => true,
188 )
189 );
190
191 foreach ( $category_terms as $term ) {
192 if ( ! in_array( $term->term_id, $categories, true ) ) {
193 continue;
194 }
195
196 $categories_ordered[] = $term->term_id;
197 }
198
199 return $categories_ordered;
200 }
201
202 /**
203 * Maybe flatten products by category.
204 *
205 * @param array $products
206 * @param array $args
207 *
208 * @return array
209 */
210 public static function maybe_flatten_products_by_category( $products, $args = array() ) {
211 // If we're already listing all products with no categories, escape.
212 if ( empty( $products ) || isset( $products[0] ) ) {
213 return $products;
214 }
215
216 $flatten_level = apply_filters( 'orderable_flatten_products_by_category_level', 'all', $args );
217
218 // Don't flatten if set to "none".
219 if ( 'none' === $flatten_level ) {
220 return $products;
221 }
222
223 $flattened_products = array();
224
225 if ( 'children' === $flatten_level ) {
226 foreach ( $products as $category_id => $product_group ) {
227 $flattened_products[ $category_id ] = $product_group;
228
229 if ( ! empty( $product_group['category']['children'] ) ) {
230 $flattened_products[ $category_id ]['category']['children'] = array(
231 array(
232 'category' => null,
233 'products' => array(),
234 ),
235 );
236
237 foreach ( $product_group['category']['children'] as $child_product_group ) {
238 // Add category products to list.
239 $flattened_products[ $category_id ]['category']['children'][0]['products'] = array_merge( $flattened_products[ $category_id ]['category']['children'][0]['products'], $child_product_group['products'] );
240
241 continue;
242 }
243 }
244 }
245 } else {
246 $flattened_products[] = array(
247 'category' => null,
248 'products' => array(),
249 );
250
251 foreach ( $products as $product_group ) {
252 if ( ! empty( $product_group['products'] ) ) {
253 // Add category products to list.
254 $flattened_products[0]['products'] = array_merge( $flattened_products[0]['products'], $product_group['products'] );
255
256 continue;
257 } elseif ( ! empty( $product_group['category']['children'] ) ) {
258 foreach ( $product_group['category']['children'] as $child_product_group ) {
259 // Add category products to list.
260 $flattened_products[0]['products'] = array_merge( $flattened_products[0]['products'], $child_product_group['products'] );
261
262 continue;
263 }
264 }
265 }
266 }
267
268 return $flattened_products;
269 }
270
271 /**
272 * Get add to cart button.
273 *
274 * @param WC_Product $product Product.
275 * @param string $classes Button classes.
276 *
277 * @return string
278 */
279 public static function get_add_to_cart_button( $product, $classes = '' ) {
280 global $orderable_single_product;
281
282 $args = array(
283 'trigger' => 'add-to-cart',
284 'product_id' => $product->get_id(),
285 'product_type' => $product->get_type(),
286 'variation_id' => null,
287 'variation_attributes' => array(),
288 'text' => __( 'Add', 'orderable' ),
289 'classes' => $classes,
290 );
291
292 if ( 'variable' === $args['product_type'] ) {
293 $args['trigger'] = 'product-options';
294 $args['text'] = empty( $orderable_single_product ) ? __( 'Select', 'orderable' ) : $args['text'];
295 } elseif ( 'variation' === $args['product_type'] ) {
296 $args['product_id'] = $product->get_parent_id();
297 $args['variation_id'] = $product->get_id();
298 }
299
300 if ( ! $product->is_in_stock() ) {
301 $args['classes'] .= ' orderable-button--out-of-stock';
302 $args['text'] = __( 'Out of Stock', 'orderable' );
303 }
304
305 $args = apply_filters( 'orderable_add_to_cart_button_args', $args, $product );
306
307 return sprintf( '<button class="orderable-button %s" data-orderable-trigger="%s" data-orderable-product-id="%d" data-orderable-product-type="%s" data-orderable-variation-id="%d" data-orderable-variation-attributes="">%s</button>', esc_attr( $args['classes'] ), esc_attr( $args['trigger'] ), esc_attr( $args['product_id'] ), esc_attr( $args['product_type'] ), esc_attr( $args['variation_id'] ), wp_kses_post( $args['text'] ) );
308 }
309
310 /**
311 * Get a list of attributes for available variations.
312 *
313 * @param WC_Product_Variable $product
314 *
315 * @return array
316 */
317 public static function get_available_variation_attributes( $product ) {
318 $available_variation_attributes = array();
319 $available_variations = $product->get_available_variations();
320 $available_variations = wc_list_pluck( $available_variations, 'attributes' );
321
322 if ( empty( $available_variations ) ) {
323 return $available_variation_attributes;
324 }
325
326 foreach ( $available_variations as $available_variation ) {
327 foreach ( $available_variation as $attribute_slug => $attribute_value ) {
328 if ( ! isset( $available_variation_attributes[ $attribute_slug ] ) ) {
329 $available_variation_attributes[ $attribute_slug ] = array();
330 }
331
332 $available_variation_attributes[ $attribute_slug ][] = $attribute_value;
333 }
334 }
335
336 return $available_variation_attributes;
337 }
338
339 /**
340 * Get available attributes.
341 *
342 * This method remove an attributes which don't belong to
343 * an active variation.
344 *
345 * @param WC_Product_Variable $product
346 *
347 * @return array
348 */
349 public static function get_available_attributes( $product ) {
350 $available_attributes = array();
351 $attributes = $product->get_variation_attributes();
352 $available_variation_attributes = self::get_available_variation_attributes( $product );
353
354 if ( empty( $attributes ) ) {
355 return $available_attributes;
356 }
357
358 foreach ( $attributes as $attribute_name => $attribute_terms ) {
359 $attribute_name_sanitized = wc_variation_attribute_name( $attribute_name );
360
361 if ( empty( $available_variation_attributes[ $attribute_name_sanitized ] ) ) {
362 continue;
363 }
364
365 if ( ! isset( $available_attributes[ $attribute_name ] ) ) {
366 $available_attributes[ $attribute_name ] = array();
367 }
368
369 foreach ( $attribute_terms as $attribute_term ) {
370 if ( ! in_array( $attribute_term, $available_variation_attributes[ $attribute_name_sanitized ], true ) && ! in_array( '', $available_variation_attributes[ $attribute_name_sanitized ], true ) ) {
371 continue;
372 }
373
374 $available_attributes[ $attribute_name ][] = $attribute_term;
375 }
376 }
377
378 return $available_attributes;
379 }
380
381 /**
382 * Is product single page hidden?
383 *
384 * @param int|WC_Product $product
385 *
386 * @return bool
387 */
388 public static function is_product_hidden( $product ) {
389 if ( is_numeric( $product ) ) {
390 $product = wc_get_product( $product );
391 }
392
393 if ( empty( $product ) ) {
394 return false;
395 }
396
397 $categories = $product->get_category_ids();
398
399 if ( empty( $categories ) ) {
400 return false;
401 }
402
403 foreach ( $categories as $category_id ) {
404 if ( self::is_category_hidden( $category_id ) ) {
405 return true;
406 }
407 }
408
409 return false;
410 }
411
412 /**
413 * Is this category hidden?
414 *
415 * @param $category_id
416 *
417 * @return bool
418 */
419 public static function is_category_hidden( $category_id ) {
420 $hidden_categories = Orderable_Settings::get_hidden_categories();
421
422 return in_array( $category_id, $hidden_categories, true );
423 }
424
425 /**
426 * Set if product is visible, based on category settings.
427 *
428 * @param bool $visible
429 * @param int $product_id
430 *
431 * @return bool
432 */
433 public static function set_product_visibility( $visible, $product_id ) {
434 $hidden = self::is_product_hidden( $product_id );
435
436 return $hidden ? false : $visible;
437 }
438
439 /**
440 * Disable cart permalink.
441 *
442 * @param string $permalink
443 *
444 * @return bool
445 */
446 public static function disable_cart_link( $permalink, $cart_item, $cart_item_key ) {
447 if ( ! isset( $cart_item['data'] ) ) {
448 return $permalink;
449 }
450
451 return self::is_product_hidden( $cart_item['data'] ) ? false : $permalink;
452 }
453
454 /**
455 * Set product page to 404 if required.
456 */
457 public static function products_404() {
458 if ( is_admin() || ! ( is_product() && is_single() ) ) {
459 return;
460 }
461
462 global $post;
463
464 if ( empty( $post ) ) {
465 return;
466 }
467
468 $product = wc_get_product( $post->ID );
469
470 if ( empty( $product ) || ! self::is_product_hidden( $product ) ) {
471 return;
472 }
473
474 global $wp_query;
475
476 $wp_query->set_404();
477 status_header( 404 );
478 nocache_headers();
479 }
480
481 /**
482 * Exclude hidden categories from queries.
483 *
484 * @param $tax_query
485 * @param $wc_query
486 *
487 * @return array
488 */
489 public static function remove_hidden_categories_from_products_query( $tax_query, $wc_query ) {
490 $hidden_categories = Orderable_Settings::get_hidden_categories();
491
492 if ( empty( $hidden_categories ) ) {
493 return $tax_query;
494 }
495
496 $tax_query[] = array(
497 'taxonomy' => 'product_cat',
498 'field' => 'term_id',
499 'terms' => $hidden_categories,
500 'operator' => 'NOT IN',
501 );
502
503 return $tax_query;
504 }
505
506 /**
507 * Remove hidden categories from terms query.
508 *
509 * As well as hiding from terms lists (sidebar widgets, etc),
510 * this also disables the archive page.
511 *
512 * @param array $args
513 * @param array $taxonomies
514 *
515 * @return mixed
516 */
517 public static function remove_hidden_categories_from_terms_query( $args, $taxonomies ) {
518 if ( is_admin() ) {
519 return $args;
520 }
521
522 $taxonomy = ! empty( $args['taxonomy'] ) && isset( $args['taxonomy'][0] ) ? $args['taxonomy'][0] : false;
523
524 if ( 'product_cat' !== $taxonomy || is_admin() ) {
525 return $args;
526 }
527
528 // Exclude hidden categories.
529 $hidden_categories = Orderable_Settings::get_hidden_categories();
530 $args['exclude'] = ! is_array( $args['exclude'] ) ? array() : $args['exclude'];
531 $args['exclude'] = array_merge( $args['exclude'], $hidden_categories );
532
533 return $args;
534 }
535 }
536