$product_reference; $collection_args['relatedBy'] = ! isset( $query['relatedBy'] ) ? array( 'categories' => true, 'tags' => true, ) : array( 'categories' => isset( $query['relatedBy']['categories'] ) && true === $query['relatedBy']['categories'], 'tags' => isset( $query['relatedBy']['tags'] ) && true === $query['relatedBy']['tags'], ); return $collection_args; }, function ( $collection_args, $query, $request ) { $product_reference = $request->get_param( 'productReference' ); // In some cases the editor will send along block location context that we can infer the product reference from. if ( empty( $product_reference ) ) { $location = $collection_args['productCollectionLocation']; if ( isset( $location['type'] ) && 'product' === $location['type'] ) { $product_reference = $location['sourceData']['productId']; } } $collection_args['relatedProductReference'] = $product_reference; $related_by = $request->get_param( 'relatedBy' ); $collection_args['relatedBy'] = ! isset( $related_by ) ? array( 'categories' => true, 'tags' => true, ) : array( 'categories' => rest_sanitize_boolean( $related_by['categories'] ?? false ), 'tags' => rest_sanitize_boolean( $related_by['tags'] ?? false ), ); return $collection_args; } ); $this->register_collection_handlers( 'woocommerce/product-collection/upsells', function ( $collection_args ) { $product_reference = $collection_args['upsellsProductReferences'] ?? null; // No products should be shown if no upsells product reference is set. if ( empty( $product_reference ) ) { return array( 'post__in' => array( -1 ), ); } $products = array_map( 'wc_get_product', $product_reference ); if ( empty( $products ) ) { return array( 'post__in' => array( -1 ), ); } $all_upsells = array_reduce( $products, function ( $acc, $product ) { return array_merge( $acc, $product->get_upsell_ids() ); }, array() ); // Remove duplicates and product references. We don't want to display // what's already in cart. $unique_upsells = array_unique( $all_upsells ); $upsells = array_diff( $unique_upsells, $product_reference ); return array( 'post__in' => empty( $upsells ) ? array( -1 ) : $upsells, ); }, function ( $collection_args, $query ) { $product_references = isset( $query['productReference'] ) ? array( $query['productReference'] ) : null; // Infer the product reference from the location if an explicit product is not set. if ( empty( $product_references ) ) { $location = $collection_args['productCollectionLocation']; if ( isset( $location['type'] ) && 'product' === $location['type'] ) { $product_references = array( $location['sourceData']['productId'] ); } if ( isset( $location['type'] ) && 'cart' === $location['type'] ) { $product_references = $location['sourceData']['productIds']; } if ( isset( $location['type'] ) && 'order' === $location['type'] ) { $product_references = $this->get_product_ids_from_order( $location['sourceData']['orderId'] ?? 0 ); } } $collection_args['upsellsProductReferences'] = $product_references; return $collection_args; }, function ( $collection_args, $query, $request ) { $product_reference = $request->get_param( 'productReference' ); // In some cases the editor will send along block location context that we can infer the product reference from. if ( empty( $product_reference ) ) { $location = $collection_args['productCollectionLocation']; if ( isset( $location['type'] ) && 'product' === $location['type'] ) { $product_reference = $location['sourceData']['productId']; } } $collection_args['upsellsProductReferences'] = array( $product_reference ); return $collection_args; } ); $this->register_collection_handlers( 'woocommerce/product-collection/cross-sells', function ( $collection_args ) { $product_reference = $collection_args['crossSellsProductReferences'] ?? null; // No products should be shown if no cross-sells product reference is set. if ( empty( $product_reference ) ) { return array( 'post__in' => array( -1 ), ); } $products = array_filter( array_map( 'wc_get_product', $product_reference ) ); if ( empty( $products ) ) { return array( 'post__in' => array( -1 ), ); } $product_ids = array_map( function ( $product ) { return $product->get_id(); }, $products ); $all_cross_sells = array_reduce( $products, function ( $acc, $product ) { return array_merge( $acc, $product->get_cross_sell_ids() ); }, array() ); // Remove duplicates and product references. We don't want to display // what's already in cart. $unique_cross_sells = array_unique( $all_cross_sells ); $cross_sells = array_diff( $unique_cross_sells, $product_ids ); return array( 'post__in' => empty( $cross_sells ) ? array( -1 ) : $cross_sells, ); }, function ( $collection_args, $query ) { $product_references = isset( $query['productReference'] ) ? array( $query['productReference'] ) : null; // Infer the product reference from the location if an explicit product is not set. if ( empty( $product_references ) ) { $location = $collection_args['productCollectionLocation']; if ( isset( $location['type'] ) && 'product' === $location['type'] ) { $product_references = array( $location['sourceData']['productId'] ); } if ( isset( $location['type'] ) && 'cart' === $location['type'] ) { $product_references = $location['sourceData']['productIds']; } if ( isset( $location['type'] ) && 'order' === $location['type'] ) { $product_references = $this->get_product_ids_from_order( $location['sourceData']['orderId'] ?? 0 ); } } $collection_args['crossSellsProductReferences'] = $product_references; return $collection_args; }, function ( $collection_args, $query, $request ) { $product_reference = $request->get_param( 'productReference' ); // In some cases the editor will send along block location context that we can infer the product reference from. if ( empty( $product_reference ) ) { $location = $collection_args['productCollectionLocation']; if ( isset( $location['type'] ) && 'product' === $location['type'] ) { $product_reference = $location['sourceData']['productId']; } } $collection_args['crossSellsProductReferences'] = array( $product_reference ); return $collection_args; } ); return $this->collection_handler_store; } /** * Get collection handler by name. * * @param string $name Collection name. * @return array|null Collection handler array or null if not found. */ public function get_collection_handler( $name ) { return $this->collection_handler_store[ $name ] ?? null; } /** * Removes any custom collection handlers for the given collection. * * @param string $collection_name The name of the collection to unregister. */ public function unregister_collection_handlers( $collection_name ) { unset( $this->collection_handler_store[ $collection_name ] ); } /** * Get product IDs from an order. * * @param int $order_id The order ID. * @return array The product IDs. */ private function get_product_ids_from_order( $order_id ) { $product_references = array(); if ( empty( $order_id ) ) { return $product_references; } $order = wc_get_order( $order_id ); if ( $order ) { $product_references = array_filter( array_map( function ( $item ) { return $item->get_product_id(); }, $order->get_items( 'line_item' ) ) ); } return $product_references; } }