WC_POS_APIv2_Products

POS Product Class duck punches the WC REST API.

Defined (1)

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

/includes/apiv2/class-wc-pos-products.php  
  1. class WC_POS_APIv2_Products extends WC_POS_APIv2_Abstract { 
  2.  
  3. /** 
  4. * Product fields used by the POS 
  5. * @var array 
  6. */ 
  7. private $whitelist = array( 
  8. 'id',  
  9. 'title', // backward compat 
  10. 'name',  
  11. 'slug',  
  12. 'permalink',  
  13. 'date_created', 'created_at',  
  14. 'date_created_gmt',  
  15. 'date_modified', 'updated_at',  
  16. 'date_modified_gmt',  
  17. 'type',  
  18. 'status',  
  19. 'featured',  
  20. // 'catalog_visibility',  
  21. // 'description',  
  22. // 'short_description',  
  23. 'sku',  
  24. 'price',  
  25. 'regular_price',  
  26. 'sale_price',  
  27. 'date_on_sale_from',  
  28. 'date_on_sale_from_gmt',  
  29. 'date_on_sale_to',  
  30. 'date_on_sale_to_gmt',  
  31. // 'price_html',  
  32. 'on_sale',  
  33. 'purchaseable',  
  34. 'total_sales',  
  35. 'virtual',  
  36. 'downloadable',  
  37. // 'downloads',  
  38. // 'download_limit',  
  39. // 'download_expiry',  
  40. // 'external_url',  
  41. // 'button_text',  
  42. 'tax_status', 'taxable',  
  43. 'tax_class',  
  44. 'manage_stock', 'managing_stock',  
  45. 'stock_quantity',  
  46. 'in_stock',  
  47. 'backorders',  
  48. 'backorders_allowed',  
  49. 'backordered',  
  50. 'sold_individually',  
  51. // 'weight',  
  52. // 'dimensions',  
  53. 'shipping_required',  
  54. 'shipping_taxable',  
  55. 'shipping_class',  
  56. 'shipping_class_id',  
  57. // 'reviews_allowed',  
  58. // 'average_rating',  
  59. // 'rating_count',  
  60. // 'related_ids',  
  61. // 'upsell_ids',  
  62. // 'cross_sell_ids',  
  63. 'parent_id',  
  64. 'purchase_note',  
  65. 'categories',  
  66. 'tags',  
  67. // 'images',  
  68. 'attributes',  
  69. 'default_attributes',  
  70. 'variations',  
  71. 'grouped_products',  
  72. 'menu_order',  
  73. 'meta_data',  
  74.  
  75. /** 
  76. * Fields add by POS 
  77. * - product thumbnail 
  78. * - barcode 
  79. */ 
  80. 'featured_src',  
  81. 'barcode' 
  82. ); 
  83.  
  84. /** 
  85. */ 
  86. public function __construct() { 
  87. add_filter( 'woocommerce_rest_prepare_product_object', array( $this, 'product_response' ), 10, 3 ); 
  88. add_filter( 'woocommerce_rest_prepare_product_variation_object', array( $this, 'product_response' ), 10, 3 ); 
  89. add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) ); 
  90. add_filter( 'posts_where', array( $this, 'posts_where' ), 10 , 2 ); 
  91.  
  92. /** 
  93. * Filter each product response from WC REST API for easier handling by the POS 
  94. * - use the thumbnails rather than fullsize 
  95. * - add barcode field 
  96. * - unset unnecessary data 
  97. * @param $response 
  98. * @param $product 
  99. * @return array modified data array $product_data 
  100. */ 
  101. public function product_response( $response, $product, $request ) { 
  102. $data = $response->get_data(); 
  103. $type = isset( $data['type'] ) ? $data['type'] : ''; 
  104.  
  105. if( $type == 'variable' ) : 
  106. // nested variations 
  107. foreach( $data['variations'] as &$variation ) : 
  108. $response = WC()->api->WC_REST_Product_Variations_Controller->get_item( 
  109. array( 
  110. 'id' => $variation,  
  111. 'product_id' => $variation 
  112. ); 
  113. $variation = $response->get_data(); 
  114. endforeach; 
  115. endif; 
  116.  
  117.  
  118. // 
  119. // // variable products 
  120. // if( $type == 'variable' ) : 
  121. // // nested variations 
  122. // foreach( $data['variations'] as &$variation ) : 
  123. // $_product = wc_get_product( $variation['id'] ); 
  124. // $variation = $this->filter_response_data( $variation, $_product ); 
  125. // $variation['attributes'] = $this->patch_variation_attributes( $_product ); 
  126. // endforeach; 
  127. // endif; 
  128. // 
  129. // // variation 
  130. // if( $type == 'variation' ) : 
  131. // $data['attributes'] = $this->patch_variation_attributes( $product ); 
  132. // endif; 
  133.  
  134. $data = $this->filter_response_data( $data, $product ); 
  135.  
  136. $response->set_data($data); 
  137. return $response; 
  138.  
  139. /** 
  140. * https://github.com/woothemes/woocommerce/issues/8457 
  141. * patches WC_Product_Variable->get_variation_attributes() 
  142. * @param $product 
  143. * @return array 
  144. */ 
  145. private function patch_variation_attributes( $product ) { 
  146. $patched_attributes = array(); 
  147. $attributes = $product->get_attributes(); 
  148. $variation_attributes = $product->get_variation_attributes(); 
  149.  
  150. // patch for corrupted data, depreciate asap 
  151. if( empty( $attributes ) ) { 
  152. $attributes = $product->parent->product_attributes; 
  153. delete_post_meta( $product->variation_id, '_product_attributes' ); 
  154.  
  155. foreach( $variation_attributes as $slug => $option ) { 
  156. $slug = str_replace( 'attribute_', '', $slug ); 
  157.  
  158. if( isset( $attributes[$slug] ) ) { 
  159. $patched_attributes[] = array( 
  160. 'name' => $this->get_variation_name( $attributes[$slug] ),  
  161. 'option' => $this->get_variation_option( $product, $attributes[$slug], $option ) 
  162. ); 
  163.  
  164.  
  165. return $patched_attributes; 
  166.  
  167. /** 
  168. * @param $attribute 
  169. * @return null|string 
  170. */ 
  171. private function get_variation_name( $attribute ) { 
  172. if( $attribute['is_taxonomy'] ) { 
  173. global $wpdb; 
  174. $name = str_replace( 'pa_', '', $attribute['name'] ); 
  175.  
  176. $label = $wpdb->get_var( 
  177. $wpdb->prepare(" 
  178. SELECT attribute_label 
  179. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies 
  180. WHERE attribute_name = %s; 
  181. ", $name ) ); 
  182.  
  183. return $label ? $label : $name; 
  184.  
  185. return $attribute['name']; 
  186.  
  187. /** 
  188. * @param $product 
  189. * @param $option 
  190. * @param $attribute 
  191. * @return mixed 
  192. */ 
  193. private function get_variation_option( $product, $attribute, $option ) { 
  194. $name = $option; 
  195.  
  196. // taxonomy attributes 
  197. if ( $attribute['is_taxonomy'] ) { 
  198. $terms = wp_get_post_terms( $product->parent->id, $attribute['name'] ); 
  199. if( !is_wp_error($terms) ) : foreach( $terms as $term ) : 
  200. if( $option === $term->slug ) $name = $term->name; 
  201. endforeach; endif; 
  202.  
  203. // piped attributes 
  204. } else { 
  205. $values = array_map( 'trim', explode( WC_DELIMITER, $attribute['value'] ) ); 
  206. $options = array_combine( array_map( 'sanitize_title', $values) , $values ); 
  207. if( $options && isset( $options[$option] ) ) { 
  208. $name = $options[$option]; 
  209.  
  210. return $name; 
  211.  
  212. /** 
  213. * Filter product response data 
  214. * - add featured_src 
  215. * - add special key for barcode, defaults to sku 
  216. * @param array $data 
  217. * @param $product 
  218. * @return array 
  219. */ 
  220. private function filter_response_data( array $data, $product ) { 
  221. $id = isset( $data['id'] ) ? $data['id'] : ''; 
  222. $barcode = isset( $data['sku'] ) ? $data['sku'] : ''; 
  223.  
  224. // allow custom barcode field 
  225. $barcode_meta_key = apply_filters( 'woocommerce_pos_barcode_meta_key', '_sku' ); 
  226. if( $barcode_meta_key !== '_sku' ) { 
  227. $barcode = get_post_meta( $id, $barcode_meta_key, true ); 
  228.  
  229. $data['featured_src'] = $this->get_thumbnail( $id ); 
  230. $data['barcode'] = apply_filters( 'woocommerce_pos_product_barcode', $barcode, $id ); 
  231.  
  232. // allow decimal stock quantities, fixed in WC 2.4 
  233. if( version_compare( WC()->version, '2.4', '<' ) ) { 
  234. $data['stock_quantity'] = $product->get_stock_quantity(); 
  235.  
  236. // backwards compatibility 
  237. if( isset($data['date_modified']) ) { 
  238. $data['updated_at'] = $data['date_modified']; 
  239.  
  240. if( isset($data['manage_stock']) ) { 
  241. $data['managing_stock'] = $data['manage_stock']; 
  242.  
  243. if( isset($data['tax_status']) ) { 
  244. $data['taxable'] = $data['tax_status'] == 'taxable'; 
  245.  
  246. // filter by whitelist 
  247. // - note, this uses the same method as WC REST API fields parameter 
  248. // - this doesn't speed up queries as it should 
  249. // - when WC REST API properly filters requests POS should use fields param 
  250. return array_intersect_key( $data, array_flip( $this->whitelist ) ); 
  251.  
  252. /** 
  253. * Returns thumbnail if it exists, if not, returns the WC placeholder image 
  254. * @param int $id 
  255. * @return string 
  256. */ 
  257. private function get_thumbnail($id) { 
  258. $image = false; 
  259. $thumb_id = get_post_thumbnail_id( $id ); 
  260.  
  261. if( $thumb_id ) 
  262. $image = wp_get_attachment_image_src( $thumb_id, 'shop_thumbnail' ); 
  263.  
  264. if( is_array($image) ) 
  265. return $image[0]; 
  266.  
  267. return wc_placeholder_img_src(); 
  268.  
  269. /** 
  270. * @param $query 
  271. */ 
  272. public function pre_get_posts($query) { 
  273. // order product alphabetically 
  274. $query->set('orderby', 'post_title'); 
  275. $query->set('order', 'ASC'); 
  276.  
  277. /** 
  278. * @param $where 
  279. * @param $query 
  280. * @return mixed 
  281. */ 
  282. public function posts_where( $where, $query ) { 
  283. global $wpdb; 
  284.  
  285. if( isset( $_GET['filter'] ) ) { 
  286.  
  287. $filter = $_GET['filter']; 
  288.  
  289. if( isset($filter['barcode']) ) { 
  290.  
  291. $barcode_meta_key = apply_filters( 'woocommerce_pos_barcode_meta_key', '_sku' ); 
  292.  
  293. // gets post ids and parent ids 
  294. $result = $wpdb->get_results( 
  295. $wpdb->prepare(" 
  296. SELECT p.ID, p.post_parent 
  297. FROM $wpdb->posts AS p 
  298. JOIN $wpdb->postmeta AS pm 
  299. ON p.ID = pm.post_id 
  300. WHERE pm.meta_key = %s 
  301. AND pm.meta_value LIKE %s 
  302. ", $barcode_meta_key, '%'.$filter['barcode'].'%' ),  
  303. ARRAY_N 
  304. ); 
  305.  
  306. if($result) { 
  307. $ids = call_user_func_array('array_merge', $result); 
  308. $where .= " AND ID IN (" . implode( ', ', array_unique($ids) ) . ")"; 
  309. } else { 
  310. // no matches 
  311. $where .= " AND 1=0"; 
  312.  
  313.  
  314.  
  315. return $where; 
  316.  
  317. /** 
  318. * Returns array of all product ids 
  319. * @param $date_modified 
  320. * @return array 
  321. */ 
  322. public function get_ids($date_modified) { 
  323. $args = array( 
  324. 'post_type' => array('product'),  
  325. 'post_status' => array('publish'),  
  326. 'posts_per_page'=> -1,  
  327. 'fields' => 'ids' 
  328. ); 
  329.  
  330. if($date_modified) { 
  331. $args['date_query'][] = array( 
  332. 'column' => 'post_modified_gmt',  
  333. 'after' => $date_modified,  
  334. 'inclusive' => false 
  335. ); 
  336.  
  337. $query = new WP_Query( $args ); 
  338. return array_map( 'intval', $query->posts ); 
  339.