wpdb = $wpdb; $this->product_repository = $product_repository; } /** * Retrieve an array of product information using the request params. * * @param WP_REST_Request $request * * @return array * * @throws InvalidValue If the orderby value isn't valid. * @throws Exception If the status data can't be retrieved from Google. */ public function get( WP_REST_Request $request ): array { $this->request = $request; $products = []; $args = $this->prepare_query_args(); $refresh_status_data_job = null; list( $limit, $offset ) = $this->prepare_query_pagination(); $mc_service = $this->container->get( MerchantCenterService::class ); if ( $mc_service->is_connected() ) { $refresh_status_data_job = $this->container->get( MerchantStatuses::class )->maybe_refresh_status_data(); } /** @var ProductHelper $product_helper */ $product_helper = $this->container->get( ProductHelper::class ); add_filter( 'posts_where', [ $this, 'title_filter' ], 10, 2 ); foreach ( $this->product_repository->find( $args, $limit, $offset ) as $product ) { $id = $product->get_id(); $errors = $product_helper->get_validation_errors( $product ); $mc_status = $product_helper->get_mc_status( $product ) ?: $product_helper->get_sync_status( $product ); // If the refresh_status_data_job is scheduled, we don't know the status yet as it is being refreshed. if ( $refresh_status_data_job && $refresh_status_data_job->is_scheduled() ) { $mc_status = null; } $products[ $id ] = [ 'id' => $id, 'title' => $product->get_name(), 'visible' => $product_helper->get_channel_visibility( $product ) !== ChannelVisibility::DONT_SYNC_AND_SHOW, 'status' => $mc_status, 'image_url' => wp_get_attachment_image_url( $product->get_image_id(), 'full' ), 'price' => $product->get_price(), 'errors' => array_values( $errors ), ]; } remove_filter( 'posts_where', [ $this, 'title_filter' ] ); return array_values( $products ); } /** * Count the number of products (using title filter if present). * * @param WP_REST_Request $request * * @return int * * @throws InvalidValue If the orderby value isn't valid. */ public function count( WP_REST_Request $request ): int { $this->request = $request; $args = $this->prepare_query_args(); add_filter( 'posts_where', [ $this, 'title_filter' ], 10, 2 ); $ids = $this->product_repository->find_ids( $args ); remove_filter( 'posts_where', [ $this, 'title_filter' ] ); return count( $ids ); } /** * Prepare the args to be used to retrieve the products, namely orderby, meta_query and type. * * @return array * * @throws InvalidValue If the orderby value isn't valid. */ protected function prepare_query_args(): array { $product_types = ProductSyncer::get_supported_product_types(); $product_types = array_diff( $product_types, [ 'variation' ] ); $args = [ 'type' => $product_types, 'status' => 'publish', 'orderby' => [ 'title' => 'ASC' ], ]; if ( ! empty( $this->request['ids'] ) ) { $args['include'] = explode( ',', $this->request['ids'] ); } if ( ! empty( $this->request['search'] ) ) { $args['gla_search'] = $this->request['search']; } if ( empty( $this->request['orderby'] ) ) { return $args; } switch ( $this->request['orderby'] ) { case 'title': $args['orderby']['title'] = $this->get_order(); break; case 'id': $args['orderby'] = [ 'ID' => $this->get_order() ] + $args['orderby']; break; case 'visible': $args['meta_key'] = $this->prefix_meta_key( ProductMetaHandler::KEY_VISIBILITY ); $args['orderby'] = [ 'meta_value' => $this->get_order() ] + $args['orderby']; break; case 'status': $args['meta_key'] = $this->prefix_meta_key( ProductMetaHandler::KEY_MC_STATUS ); $args['orderby'] = [ 'meta_value' => $this->get_order() ] + $args['orderby']; break; case 'total_sales': $args['meta_key'] = self::META_KEY_TOTAL_SALES; $args['orderby'] = [ 'meta_value_num' => $this->get_order() ] + $args['orderby']; break; default: throw InvalidValue::not_in_allowed_list( 'orderby', [ 'title', 'id', 'visible', 'status', 'total_sales' ] ); } return $args; } /** * Convert the per_page and page parameters into limit and offset values. * * @return array Containing limit and offset values. */ protected function prepare_query_pagination(): array { $limit = -1; $offset = 0; if ( ! empty( $this->request['per_page'] ) ) { $limit = intval( $this->request['per_page'] ); $page = max( 1, intval( $this->request['page'] ) ); $offset = $limit * ( $page - 1 ); } return [ $limit, $offset ]; } /** * Filter for the posts_where hook, adds WHERE clause to search * for the 'search' parameter in the product titles (when present). * * @param string $where The WHERE clause of the query. * @param WP_Query $wp_query The WP_Query instance (passed by reference). * * @return string The updated WHERE clause. */ public function title_filter( string $where, WP_Query $wp_query ): string { $gla_search = $wp_query->get( 'gla_search' ); if ( $gla_search ) { $title_search = '%' . $this->wpdb->esc_like( $gla_search ) . '%'; $where .= $this->wpdb->prepare( " AND `{$this->wpdb->posts}`.`post_title` LIKE %s", $title_search ); // phpcs:ignore WordPress.DB.PreparedSQL } return $where; } /** * Return the ORDER BY order based on the order request parameter value. * * @return string */ protected function get_order(): string { return strtoupper( $this->request['order'] ?? '' ) === 'DESC' ? 'DESC' : 'ASC'; } }