WC_Shortcodes

WC_Shortcodes class.

Defined (1)

The class is defined in the following location(s).

/includes/class-wc-shortcodes.php  
  1. class WC_Shortcodes { 
  2.  
  3. /** 
  4. * Init shortcodes. 
  5. */ 
  6. public static function init() { 
  7. $shortcodes = array( 
  8. 'product' => __CLASS__ . '::product',  
  9. 'product_page' => __CLASS__ . '::product_page',  
  10. 'product_category' => __CLASS__ . '::product_category',  
  11. 'product_categories' => __CLASS__ . '::product_categories',  
  12. 'add_to_cart' => __CLASS__ . '::product_add_to_cart',  
  13. 'add_to_cart_url' => __CLASS__ . '::product_add_to_cart_url',  
  14. 'products' => __CLASS__ . '::products',  
  15. 'recent_products' => __CLASS__ . '::recent_products',  
  16. 'sale_products' => __CLASS__ . '::sale_products',  
  17. 'best_selling_products' => __CLASS__ . '::best_selling_products',  
  18. 'top_rated_products' => __CLASS__ . '::top_rated_products',  
  19. 'featured_products' => __CLASS__ . '::featured_products',  
  20. 'product_attribute' => __CLASS__ . '::product_attribute',  
  21. 'related_products' => __CLASS__ . '::related_products',  
  22. 'shop_messages' => __CLASS__ . '::shop_messages',  
  23. 'woocommerce_order_tracking' => __CLASS__ . '::order_tracking',  
  24. 'woocommerce_cart' => __CLASS__ . '::cart',  
  25. 'woocommerce_checkout' => __CLASS__ . '::checkout',  
  26. 'woocommerce_my_account' => __CLASS__ . '::my_account',  
  27. ); 
  28.  
  29. foreach ( $shortcodes as $shortcode => $function ) { 
  30. add_shortcode( apply_filters( "{$shortcode}_shortcode_tag", $shortcode ), $function ); 
  31.  
  32. // Alias for pre 2.1 compatibility 
  33. add_shortcode( 'woocommerce_messages', __CLASS__ . '::shop_messages' ); 
  34.  
  35. /** 
  36. * Shortcode Wrapper. 
  37. * @param string[] $function 
  38. * @param array $atts (default: array()) 
  39. * @return string 
  40. */ 
  41. public static function shortcode_wrapper( 
  42. $function,  
  43. $atts = array(),  
  44. $wrapper = array( 
  45. 'class' => 'woocommerce',  
  46. 'before' => null,  
  47. 'after' => null,  
  48. ) { 
  49. ob_start(); 
  50.  
  51. echo empty( $wrapper['before'] ) ? '<div class="' . esc_attr( $wrapper['class'] ) . '">' : $wrapper['before']; 
  52. call_user_func( $function, $atts ); 
  53. echo empty( $wrapper['after'] ) ? '</div>' : $wrapper['after']; 
  54.  
  55. return ob_get_clean(); 
  56.  
  57. /** 
  58. * Loop over found products. 
  59. * @param array $query_args 
  60. * @param array $atts 
  61. * @param string $loop_name 
  62. * @return string 
  63. */ 
  64. private static function product_loop( $query_args, $atts, $loop_name ) { 
  65. global $woocommerce_loop; 
  66.  
  67. $columns = absint( $atts['columns'] ); 
  68. $woocommerce_loop['columns'] = $columns; 
  69. $woocommerce_loop['name'] = $loop_name; 
  70. $query_args = apply_filters( 'woocommerce_shortcode_products_query', $query_args, $atts, $loop_name ); 
  71. $transient_name = 'wc_loop' . substr( md5( json_encode( $query_args ) . $loop_name ), 28 ) . WC_Cache_Helper::get_transient_version( 'product_query' ); 
  72. $products = get_transient( $transient_name ); 
  73.  
  74. if ( false === $products || ! is_a( $products, 'WP_Query' ) ) { 
  75. $products = new WP_Query( $query_args ); 
  76. set_transient( $transient_name, $products, DAY_IN_SECONDS * 30 ); 
  77.  
  78. ob_start(); 
  79.  
  80. if ( $products->have_posts() ) { 
  81. ?> 
  82.  
  83. <?php do_action( "woocommerce_shortcode_before_{$loop_name}_loop", $atts ); ?> 
  84.  
  85. <?php woocommerce_product_loop_start(); ?> 
  86.  
  87. <?php while ( $products->have_posts() ) : $products->the_post(); ?> 
  88.  
  89. <?php wc_get_template_part( 'content', 'product' ); ?> 
  90.  
  91. <?php endwhile; // end of the loop. ?> 
  92.  
  93. <?php woocommerce_product_loop_end(); ?> 
  94.  
  95. <?php do_action( "woocommerce_shortcode_after_{$loop_name}_loop", $atts ); ?> 
  96.  
  97. <?php 
  98. } else { 
  99. do_action( "woocommerce_shortcode_{$loop_name}_loop_no_results", $atts ); 
  100.  
  101. woocommerce_reset_loop(); 
  102. wp_reset_postdata(); 
  103.  
  104. return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>'; 
  105.  
  106. /** 
  107. * Cart page shortcode. 
  108. * @return string 
  109. */ 
  110. public static function cart() { 
  111. return is_null( WC()->cart ) ? '' : self::shortcode_wrapper( array( 'WC_Shortcode_Cart', 'output' ) ); 
  112.  
  113. /** 
  114. * Checkout page shortcode. 
  115. * @param mixed $atts 
  116. * @return string 
  117. */ 
  118. public static function checkout( $atts ) { 
  119. return self::shortcode_wrapper( array( 'WC_Shortcode_Checkout', 'output' ), $atts ); 
  120.  
  121. /** 
  122. * Order tracking page shortcode. 
  123. * @param mixed $atts 
  124. * @return string 
  125. */ 
  126. public static function order_tracking( $atts ) { 
  127. return self::shortcode_wrapper( array( 'WC_Shortcode_Order_Tracking', 'output' ), $atts ); 
  128.  
  129. /** 
  130. * My account page shortcode. 
  131. * @param mixed $atts 
  132. * @return string 
  133. */ 
  134. public static function my_account( $atts ) { 
  135. return self::shortcode_wrapper( array( 'WC_Shortcode_My_Account', 'output' ), $atts ); 
  136.  
  137. /** 
  138. * List products in a category shortcode. 
  139. * @param array $atts 
  140. * @return string 
  141. */ 
  142. public static function product_category( $atts ) { 
  143. $atts = shortcode_atts( array( 
  144. 'per_page' => '12',  
  145. 'columns' => '4',  
  146. 'orderby' => 'menu_order title',  
  147. 'order' => 'asc',  
  148. 'category' => '', // Slugs 
  149. 'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'. 
  150. ), $atts, 'product_category' ); 
  151.  
  152. if ( ! $atts['category'] ) { 
  153. return ''; 
  154.  
  155. // Default ordering args 
  156. $ordering_args = WC()->query->get_catalog_ordering_args( $atts['orderby'], $atts['order'] ); 
  157. $meta_query = WC()->query->get_meta_query(); 
  158. $query_args = array( 
  159. 'post_type' => 'product',  
  160. 'post_status' => 'publish',  
  161. 'ignore_sticky_posts' => 1,  
  162. 'orderby' => $ordering_args['orderby'],  
  163. 'order' => $ordering_args['order'],  
  164. 'posts_per_page' => $atts['per_page'],  
  165. 'meta_query' => $meta_query,  
  166. 'tax_query' => WC()->query->get_tax_query(),  
  167. ); 
  168.  
  169. $query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] ); 
  170.  
  171. if ( isset( $ordering_args['meta_key'] ) ) { 
  172. $query_args['meta_key'] = $ordering_args['meta_key']; 
  173.  
  174. $return = self::product_loop( $query_args, $atts, 'product_cat' ); 
  175.  
  176. // Remove ordering query arguments 
  177. WC()->query->remove_ordering_args(); 
  178.  
  179. return $return; 
  180.  
  181.  
  182. /** 
  183. * List all (or limited) product categories. 
  184. * @param array $atts 
  185. * @return string 
  186. */ 
  187. public static function product_categories( $atts ) { 
  188. global $woocommerce_loop; 
  189.  
  190. $atts = shortcode_atts( array( 
  191. 'number' => null,  
  192. 'orderby' => 'name',  
  193. 'order' => 'ASC',  
  194. 'columns' => '4',  
  195. 'hide_empty' => 1,  
  196. 'parent' => '',  
  197. 'ids' => '',  
  198. ), $atts, 'product_categories' ); 
  199.  
  200. $ids = array_filter( array_map( 'trim', explode( ', ', $atts['ids'] ) ) ); 
  201. $hide_empty = ( true === $atts['hide_empty'] || 'true' === $atts['hide_empty'] || 1 === $atts['hide_empty'] || '1' === $atts['hide_empty'] ) ? 1 : 0; 
  202.  
  203. // get terms and workaround WP bug with parents/pad counts 
  204. $args = array( 
  205. 'orderby' => $atts['orderby'],  
  206. 'order' => $atts['order'],  
  207. 'hide_empty' => $hide_empty,  
  208. 'include' => $ids,  
  209. 'pad_counts' => true,  
  210. 'child_of' => $atts['parent'],  
  211. ); 
  212.  
  213. $product_categories = get_terms( 'product_cat', $args ); 
  214.  
  215. if ( '' !== $atts['parent'] ) { 
  216. $product_categories = wp_list_filter( $product_categories, array( 'parent' => $atts['parent'] ) ); 
  217.  
  218. if ( $hide_empty ) { 
  219. foreach ( $product_categories as $key => $category ) { 
  220. if ( 0 == $category->count ) { 
  221. unset( $product_categories[ $key ] ); 
  222.  
  223. if ( $atts['number'] ) { 
  224. $product_categories = array_slice( $product_categories, 0, $atts['number'] ); 
  225.  
  226. $columns = absint( $atts['columns'] ); 
  227. $woocommerce_loop['columns'] = $columns; 
  228.  
  229. ob_start(); 
  230.  
  231. if ( $product_categories ) { 
  232. woocommerce_product_loop_start(); 
  233.  
  234. foreach ( $product_categories as $category ) { 
  235. wc_get_template( 'content-product_cat.php', array( 
  236. 'category' => $category,  
  237. ) ); 
  238.  
  239. woocommerce_product_loop_end(); 
  240.  
  241. woocommerce_reset_loop(); 
  242.  
  243. return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>'; 
  244.  
  245. /** 
  246. * Recent Products shortcode. 
  247. * @param array $atts 
  248. * @return string 
  249. */ 
  250. public static function recent_products( $atts ) { 
  251. $atts = shortcode_atts( array( 
  252. 'per_page' => '12',  
  253. 'columns' => '4',  
  254. 'orderby' => 'date',  
  255. 'order' => 'desc',  
  256. 'category' => '', // Slugs 
  257. 'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'. 
  258. ), $atts, 'recent_products' ); 
  259.  
  260. $query_args = array( 
  261. 'post_type' => 'product',  
  262. 'post_status' => 'publish',  
  263. 'ignore_sticky_posts' => 1,  
  264. 'posts_per_page' => $atts['per_page'],  
  265. 'orderby' => $atts['orderby'],  
  266. 'order' => $atts['order'],  
  267. 'meta_query' => WC()->query->get_meta_query(),  
  268. 'tax_query' => WC()->query->get_tax_query(),  
  269. ); 
  270.  
  271. $query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] ); 
  272.  
  273. return self::product_loop( $query_args, $atts, 'recent_products' ); 
  274.  
  275. /** 
  276. * List multiple products shortcode. 
  277. * @param array $atts 
  278. * @return string 
  279. */ 
  280. public static function products( $atts ) { 
  281. $atts = shortcode_atts( array( 
  282. 'columns' => '4',  
  283. 'orderby' => 'title',  
  284. 'order' => 'asc',  
  285. 'ids' => '',  
  286. 'skus' => '',  
  287. ), $atts, 'products' ); 
  288.  
  289. $query_args = array( 
  290. 'post_type' => 'product',  
  291. 'post_status' => 'publish',  
  292. 'ignore_sticky_posts' => 1,  
  293. 'orderby' => $atts['orderby'],  
  294. 'order' => $atts['order'],  
  295. 'posts_per_page' => -1,  
  296. 'meta_query' => WC()->query->get_meta_query(),  
  297. 'tax_query' => WC()->query->get_tax_query(),  
  298. ); 
  299.  
  300. if ( ! empty( $atts['skus'] ) ) { 
  301. $query_args['meta_query'][] = array( 
  302. 'key' => '_sku',  
  303. 'value' => array_map( 'trim', explode( ', ', $atts['skus'] ) ),  
  304. 'compare' => 'IN',  
  305. ); 
  306.  
  307. if ( ! empty( $atts['ids'] ) ) { 
  308. $query_args['post__in'] = array_map( 'trim', explode( ', ', $atts['ids'] ) ); 
  309.  
  310. return self::product_loop( $query_args, $atts, 'products' ); 
  311.  
  312. /** 
  313. * Display a single product. 
  314. * @param array $atts 
  315. * @return string 
  316. */ 
  317. public static function product( $atts ) { 
  318. if ( empty( $atts ) ) { 
  319. return ''; 
  320.  
  321. $meta_query = WC()->query->get_meta_query(); 
  322.  
  323. $args = array( 
  324. 'post_type' => 'product',  
  325. 'posts_per_page' => 1,  
  326. 'no_found_rows' => 1,  
  327. 'post_status' => 'publish',  
  328. 'meta_query' => $meta_query,  
  329. 'tax_query' => WC()->query->get_tax_query(),  
  330. ); 
  331.  
  332. if ( isset( $atts['sku'] ) ) { 
  333. $args['meta_query'][] = array( 
  334. 'key' => '_sku',  
  335. 'value' => $atts['sku'],  
  336. 'compare' => '=',  
  337. ); 
  338.  
  339. if ( isset( $atts['id'] ) ) { 
  340. $args['p'] = $atts['id']; 
  341.  
  342. ob_start(); 
  343.  
  344. $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) ); 
  345.  
  346. if ( $products->have_posts() ) : ?> 
  347.  
  348. <?php woocommerce_product_loop_start(); ?> 
  349.  
  350. <?php while ( $products->have_posts() ) : $products->the_post(); ?> 
  351.  
  352. <?php wc_get_template_part( 'content', 'product' ); ?> 
  353.  
  354. <?php endwhile; // end of the loop. ?> 
  355.  
  356. <?php woocommerce_product_loop_end(); ?> 
  357.  
  358. <?php endif; 
  359.  
  360. wp_reset_postdata(); 
  361.  
  362. $css_class = 'woocommerce'; 
  363.  
  364. if ( isset( $atts['class'] ) ) { 
  365. $css_class .= ' ' . $atts['class']; 
  366.  
  367. return '<div class="' . esc_attr( $css_class ) . '">' . ob_get_clean() . '</div>'; 
  368.  
  369. /** 
  370. * Display a single product price + cart button. 
  371. * @param array $atts 
  372. * @return string 
  373. */ 
  374. public static function product_add_to_cart( $atts ) { 
  375. global $wpdb, $post; 
  376.  
  377. if ( empty( $atts ) ) { 
  378. return ''; 
  379.  
  380. $atts = shortcode_atts( array( 
  381. 'id' => '',  
  382. 'class' => '',  
  383. 'quantity' => '1',  
  384. 'sku' => '',  
  385. 'style' => 'border:4px solid #ccc; padding: 12px;',  
  386. 'show_price' => 'true',  
  387. ), $atts, 'product_add_to_cart' ); 
  388.  
  389. if ( ! empty( $atts['id'] ) ) { 
  390. $product_data = get_post( $atts['id'] ); 
  391. } elseif ( ! empty( $atts['sku'] ) ) { 
  392. $product_id = wc_get_product_id_by_sku( $atts['sku'] ); 
  393. $product_data = get_post( $product_id ); 
  394. } else { 
  395. return ''; 
  396.  
  397. $product = is_object( $product_data ) && in_array( $product_data->post_type, array( 'product', 'product_variation' ) ) ? wc_setup_product_data( $product_data ) : false; 
  398.  
  399. if ( ! $product ) { 
  400. return ''; 
  401.  
  402. $styles = empty( $atts['style'] ) ? '' : ' style="' . esc_attr( $atts['style'] ) . '"'; 
  403.  
  404. ob_start(); 
  405. ?> 
  406. <p class="product woocommerce add_to_cart_inline <?php echo esc_attr( $atts['class'] ); ?>"<?php echo $styles; ?>> 
  407.  
  408. <?php if ( 'true' == $atts['show_price'] ) : ?> 
  409. <?php echo $product->get_price_html(); ?> 
  410. <?php endif; ?> 
  411.  
  412. <?php woocommerce_template_loop_add_to_cart( array( 'quantity' => $atts['quantity'] ) ); ?> 
  413.  
  414. </p><?php 
  415.  
  416. // Restore Product global in case this is shown inside a product post 
  417. wc_setup_product_data( $post ); 
  418.  
  419. return ob_get_clean(); 
  420.  
  421. /** 
  422. * Get the add to cart URL for a product. 
  423. * @param array $atts 
  424. * @return string 
  425. */ 
  426. public static function product_add_to_cart_url( $atts ) { 
  427. global $wpdb; 
  428.  
  429. if ( empty( $atts ) ) { 
  430. return ''; 
  431.  
  432. if ( isset( $atts['id'] ) ) { 
  433. $product_data = get_post( $atts['id'] ); 
  434. } elseif ( isset( $atts['sku'] ) ) { 
  435. $product_id = wc_get_product_id_by_sku( $atts['sku'] ); 
  436. $product_data = get_post( $product_id ); 
  437. } else { 
  438. return ''; 
  439.  
  440. $product = is_object( $product_data ) && in_array( $product_data->post_type, array( 'product', 'product_variation' ) ) ? wc_setup_product_data( $product_data ) : false; 
  441.  
  442. if ( ! $product ) { 
  443. return ''; 
  444.  
  445. $_product = wc_get_product( $product_data ); 
  446.  
  447. return esc_url( $_product->add_to_cart_url() ); 
  448.  
  449. /** 
  450. * List all products on sale. 
  451. * @param array $atts 
  452. * @return string 
  453. */ 
  454. public static function sale_products( $atts ) { 
  455. $atts = shortcode_atts( array( 
  456. 'per_page' => '12',  
  457. 'columns' => '4',  
  458. 'orderby' => 'title',  
  459. 'order' => 'asc',  
  460. 'category' => '', // Slugs 
  461. 'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'. 
  462. ), $atts, 'sale_products' ); 
  463.  
  464. $query_args = array( 
  465. 'posts_per_page' => $atts['per_page'],  
  466. 'orderby' => $atts['orderby'],  
  467. 'order' => $atts['order'],  
  468. 'no_found_rows' => 1,  
  469. 'post_status' => 'publish',  
  470. 'post_type' => 'product',  
  471. 'meta_query' => WC()->query->get_meta_query(),  
  472. 'tax_query' => WC()->query->get_tax_query(),  
  473. 'post__in' => array_merge( array( 0 ), wc_get_product_ids_on_sale() ),  
  474. ); 
  475.  
  476. $query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] ); 
  477.  
  478. return self::product_loop( $query_args, $atts, 'sale_products' ); 
  479.  
  480. /** 
  481. * List best selling products on sale. 
  482. * @param array $atts 
  483. * @return string 
  484. */ 
  485. public static function best_selling_products( $atts ) { 
  486. $atts = shortcode_atts( array( 
  487. 'per_page' => '12',  
  488. 'columns' => '4',  
  489. 'category' => '', // Slugs 
  490. 'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'. 
  491. ), $atts, 'best_selling_products' ); 
  492.  
  493. $query_args = array( 
  494. 'post_type' => 'product',  
  495. 'post_status' => 'publish',  
  496. 'ignore_sticky_posts' => 1,  
  497. 'posts_per_page' => $atts['per_page'],  
  498. 'meta_key' => 'total_sales',  
  499. 'orderby' => 'meta_value_num',  
  500. 'meta_query' => WC()->query->get_meta_query(),  
  501. 'tax_query' => WC()->query->get_tax_query(),  
  502. ); 
  503.  
  504. $query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] ); 
  505.  
  506. return self::product_loop( $query_args, $atts, 'best_selling_products' ); 
  507.  
  508. /** 
  509. * List top rated products on sale. 
  510. * @param array $atts 
  511. * @return string 
  512. */ 
  513. public static function top_rated_products( $atts ) { 
  514. $atts = shortcode_atts( array( 
  515. 'per_page' => '12',  
  516. 'columns' => '4',  
  517. 'orderby' => 'title',  
  518. 'order' => 'asc',  
  519. 'category' => '', // Slugs 
  520. 'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'. 
  521. ), $atts, 'top_rated_products' ); 
  522.  
  523. $query_args = array( 
  524. 'post_type' => 'product',  
  525. 'post_status' => 'publish',  
  526. 'ignore_sticky_posts' => 1,  
  527. 'orderby' => $atts['orderby'],  
  528. 'order' => $atts['order'],  
  529. 'posts_per_page' => $atts['per_page'],  
  530. 'meta_query' => WC()->query->get_meta_query(),  
  531. 'tax_query' => WC()->query->get_tax_query(),  
  532. ); 
  533.  
  534. $query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] ); 
  535.  
  536. add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) ); 
  537.  
  538. $return = self::product_loop( $query_args, $atts, 'top_rated_products' ); 
  539.  
  540. remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) ); 
  541.  
  542. return $return; 
  543.  
  544. /** 
  545. * Output featured products. 
  546. * @param array $atts 
  547. * @return string 
  548. */ 
  549. public static function featured_products( $atts ) { 
  550. $atts = shortcode_atts( array( 
  551. 'per_page' => '12',  
  552. 'columns' => '4',  
  553. 'orderby' => 'date',  
  554. 'order' => 'desc',  
  555. 'category' => '', // Slugs 
  556. 'operator' => 'IN', // Possible values are 'IN', 'NOT IN', 'AND'. 
  557. ), $atts, 'featured_products' ); 
  558.  
  559. $meta_query = WC()->query->get_meta_query(); 
  560. $tax_query = WC()->query->get_tax_query(); 
  561. $tax_query[] = array( 
  562. 'taxonomy' => 'product_visibility',  
  563. 'field' => 'name',  
  564. 'terms' => 'featured',  
  565. 'operator' => 'IN',  
  566. ); 
  567.  
  568. $query_args = array( 
  569. 'post_type' => 'product',  
  570. 'post_status' => 'publish',  
  571. 'ignore_sticky_posts' => 1,  
  572. 'posts_per_page' => $atts['per_page'],  
  573. 'orderby' => $atts['orderby'],  
  574. 'order' => $atts['order'],  
  575. 'meta_query' => $meta_query,  
  576. 'tax_query' => $tax_query,  
  577. ); 
  578.  
  579. $query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] ); 
  580.  
  581. return self::product_loop( $query_args, $atts, 'featured_products' ); 
  582.  
  583. /** 
  584. * Show a single product page. 
  585. * @param array $atts 
  586. * @return string 
  587. */ 
  588. public static function product_page( $atts ) { 
  589. if ( empty( $atts ) ) { 
  590. return ''; 
  591.  
  592. if ( ! isset( $atts['id'] ) && ! isset( $atts['sku'] ) ) { 
  593. return ''; 
  594.  
  595. $args = array( 
  596. 'posts_per_page' => 1,  
  597. 'post_type' => 'product',  
  598. 'post_status' => 'publish',  
  599. 'ignore_sticky_posts' => 1,  
  600. 'no_found_rows' => 1,  
  601. ); 
  602.  
  603. if ( isset( $atts['sku'] ) ) { 
  604. $args['meta_query'][] = array( 
  605. 'key' => '_sku',  
  606. 'value' => sanitize_text_field( $atts['sku'] ),  
  607. 'compare' => '=',  
  608. ); 
  609.  
  610. $args['post_type'] = array( 'product', 'product_variation' ); 
  611.  
  612. if ( isset( $atts['id'] ) ) { 
  613. $args['p'] = absint( $atts['id'] ); 
  614.  
  615. $single_product = new WP_Query( $args ); 
  616.  
  617. $preselected_id = '0'; 
  618.  
  619. // check if sku is a variation 
  620. if ( isset( $atts['sku'] ) && $single_product->have_posts() && 'product_variation' === $single_product->post->post_type ) { 
  621.  
  622. $variation = new WC_Product_Variation( $single_product->post->ID ); 
  623. $attributes = $variation->get_attributes(); 
  624.  
  625. // set preselected id to be used by JS to provide context 
  626. $preselected_id = $single_product->post->ID; 
  627.  
  628. // get the parent product object 
  629. $args = array( 
  630. 'posts_per_page' => 1,  
  631. 'post_type' => 'product',  
  632. 'post_status' => 'publish',  
  633. 'ignore_sticky_posts' => 1,  
  634. 'no_found_rows' => 1,  
  635. 'p' => $single_product->post->post_parent,  
  636. ); 
  637.  
  638. $single_product = new WP_Query( $args ); 
  639. ?> 
  640. <script type="text/javascript"> 
  641. jQuery( document ).ready( function( $ ) { 
  642. var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' ); 
  643.  
  644. <?php foreach ( $attributes as $attr => $value ) { ?> 
  645. $variations_form.find( 'select[name="<?php echo esc_attr( $attr ); ?>"]' ).val( '<?php echo esc_js( $value ); ?>' ); 
  646. <?php } ?> 
  647. }); 
  648. </script> 
  649. <?php 
  650.  
  651. ob_start(); 
  652.  
  653. while ( $single_product->have_posts() ) : 
  654. $single_product->the_post(); 
  655. wp_enqueue_script( 'wc-single-product' ); 
  656. ?> 
  657.  
  658. <div class="single-product" data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"> 
  659.  
  660. <?php wc_get_template_part( 'content', 'single-product' ); ?> 
  661.  
  662. </div> 
  663.  
  664. <?php endwhile; // end of the loop. 
  665.  
  666. wp_reset_postdata(); 
  667.  
  668. return '<div class="woocommerce">' . ob_get_clean() . '</div>'; 
  669.  
  670. /** 
  671. * Show messages. 
  672. * @return string 
  673. */ 
  674. public static function shop_messages() { 
  675. ob_start(); 
  676. wc_print_notices(); 
  677. return '<div class="woocommerce">' . ob_get_clean() . '</div>'; 
  678.  
  679. /** 
  680. * woocommerce_order_by_rating_post_clauses function. 
  681. * @param array $args 
  682. * @return array 
  683. */ 
  684. public static function order_by_rating_post_clauses( $args ) { 
  685. global $wpdb; 
  686.  
  687. $args['where'] .= " AND $wpdb->commentmeta.meta_key = 'rating' "; 
  688. $args['join'] .= "LEFT JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID) LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)"; 
  689. $args['orderby'] = "$wpdb->commentmeta.meta_value DESC"; 
  690. $args['groupby'] = "$wpdb->posts.ID"; 
  691.  
  692. return $args; 
  693.  
  694. /** 
  695. * List products with an attribute shortcode. 
  696. * Example [product_attribute attribute='color' filter='black']. 
  697. * @param array $atts 
  698. * @return string 
  699. */ 
  700. public static function product_attribute( $atts ) { 
  701. $atts = shortcode_atts( array( 
  702. 'per_page' => '12',  
  703. 'columns' => '4',  
  704. 'orderby' => 'title',  
  705. 'order' => 'asc',  
  706. 'attribute' => '',  
  707. 'filter' => '',  
  708. ), $atts, 'product_attribute' ); 
  709.  
  710. $query_args = array( 
  711. 'post_type' => 'product',  
  712. 'post_status' => 'publish',  
  713. 'ignore_sticky_posts' => 1,  
  714. 'posts_per_page' => $atts['per_page'],  
  715. 'orderby' => $atts['orderby'],  
  716. 'order' => $atts['order'],  
  717. 'meta_query' => WC()->query->get_meta_query(),  
  718. 'tax_query' => WC()->query->get_tax_query(),  
  719. ); 
  720.  
  721. $query_args['tax_query'][] = array( 
  722. 'taxonomy' => strstr( $atts['attribute'], 'pa_' ) ? sanitize_title( $atts['attribute'] ) : 'pa_' . sanitize_title( $atts['attribute'] ),  
  723. 'terms' => array_map( 'sanitize_title', explode( ', ', $atts['filter'] ) ),  
  724. 'field' => 'slug',  
  725. ); 
  726.  
  727. return self::product_loop( $query_args, $atts, 'product_attribute' ); 
  728.  
  729. /** 
  730. * List related products. 
  731. * @param array $atts 
  732. * @return string 
  733. */ 
  734. public static function related_products( $atts ) { 
  735. $atts = shortcode_atts( array( 
  736. 'per_page' => '4',  
  737. 'columns' => '4',  
  738. 'orderby' => 'rand',  
  739. ), $atts, 'related_products' ); 
  740.  
  741. ob_start(); 
  742.  
  743. // Rename arg 
  744. $atts['posts_per_page'] = absint( $atts['per_page'] ); 
  745.  
  746. woocommerce_related_products( $atts ); 
  747.  
  748. return ob_get_clean(); 
  749.  
  750. /** 
  751. * Adds a tax_query index to the query to filter by category. 
  752. * @param array $args 
  753. * @param string $category 
  754. * @param string $operator 
  755. * @return array; 
  756. * @access private 
  757. */ 
  758. private static function _maybe_add_category_args( $args, $category, $operator ) { 
  759. if ( ! empty( $category ) ) { 
  760. if ( empty( $args['tax_query'] ) ) { 
  761. $args['tax_query'] = array(); 
  762. $args['tax_query'][] = array( 
  763. array( 
  764. 'taxonomy' => 'product_cat',  
  765. 'terms' => array_map( 'sanitize_title', explode( ', ', $category ) ),  
  766. 'field' => 'slug',  
  767. 'operator' => $operator,  
  768. ),  
  769. ); 
  770.  
  771. return $args;