meta_handler = $meta_handler; $this->product_helper = $product_helper; $this->validator = $validator; $this->product_factory = $product_factory; $this->target_audience = $target_audience; $this->attribute_mapping_rules_query = $attribute_mapping_rules_query; } /** * Filters and returns only the products already synced with Google Merchant Center. * * @param WC_Product[] $products * * @return WC_Product[] The synced products. */ public function filter_synced_products( array $products ): array { return array_filter( $products, [ $this->product_helper, 'is_product_synced' ] ); } /** * @param BatchProductEntry $product_entry */ public function mark_as_synced( BatchProductEntry $product_entry ) { $wc_product = $this->product_helper->get_wc_product( $product_entry->get_wc_product_id() ); $google_product = $product_entry->get_google_product(); $this->validate_instanceof( $google_product, GoogleProduct::class ); $this->product_helper->mark_as_synced( $wc_product, $google_product ); } /** * @param BatchProductEntry $product_entry */ public function mark_as_unsynced( BatchProductEntry $product_entry ) { try { $wc_product = $this->product_helper->get_wc_product( $product_entry->get_wc_product_id() ); } catch ( InvalidValue $exception ) { return; } $this->product_helper->mark_as_unsynced( $wc_product ); } /** * Mark a batch of WooCommerce product IDs as unsynced. * Invalid products will be skipped. * * @since 1.12.0 * * @param array $product_ids */ public function mark_batch_as_unsynced( array $product_ids ) { foreach ( $product_ids as $product_id ) { try { $product = $this->product_helper->get_wc_product( $product_id ); } catch ( InvalidValue $exception ) { continue; } $this->product_helper->mark_as_unsynced( $product ); } } /** * Marks a WooCommerce product as invalid and stores the errors in a meta data key. * * Note: If a product variation is invalid then the parent product is also marked as invalid. * * @param BatchInvalidProductEntry $product_entry */ public function mark_as_invalid( BatchInvalidProductEntry $product_entry ) { $wc_product = $this->product_helper->get_wc_product( $product_entry->get_wc_product_id() ); $errors = $product_entry->get_errors(); $this->product_helper->mark_as_invalid( $wc_product, $errors ); } /** * Generates an array map containing the Google product IDs as key and the WooCommerce product IDs as values. * * @param WC_Product[] $products * * @return BatchProductIDRequestEntry[] */ public function generate_delete_request_entries( array $products ): array { $request_entries = []; foreach ( $products as $product ) { $this->validate_instanceof( $product, WC_Product::class ); if ( $product instanceof WC_Product_Variable ) { $request_entries = array_merge( $request_entries, $this->generate_delete_request_entries( $product->get_available_variations( 'objects' ) ) ); continue; } $google_ids = $this->product_helper->get_synced_google_product_ids( $product ); if ( empty( $google_ids ) ) { continue; } foreach ( $google_ids as $google_id ) { $request_entries[ $google_id ] = new BatchProductIDRequestEntry( $product->get_id(), $google_id ); } } return $request_entries; } /** * @param WC_Product[] $products * * @return BatchProductRequestEntry[] */ public function validate_and_generate_update_request_entries( array $products ): array { $request_entries = []; $mapping_rules = $this->attribute_mapping_rules_query->get_results(); foreach ( $products as $product ) { $this->validate_instanceof( $product, WC_Product::class ); try { if ( ! $this->product_helper->is_sync_ready( $product ) ) { do_action( 'woocommerce_gla_debug_message', sprintf( 'Skipping product (ID: %s) because it is not ready to be synced.', $product->get_id() ), __METHOD__ ); continue; } if ( $product instanceof WC_Product_Variable ) { $request_entries = array_merge( $request_entries, $this->validate_and_generate_update_request_entries( $product->get_available_variations( 'objects' ) ) ); continue; } $target_countries = $this->target_audience->get_target_countries(); $main_target_country = $this->target_audience->get_main_target_country(); // validate the product $adapted_product = $this->product_factory->create( $product, $main_target_country, $mapping_rules ); $validation_result = $this->validate_product( $adapted_product ); if ( $validation_result instanceof BatchInvalidProductEntry ) { $this->mark_as_invalid( $validation_result ); do_action( 'woocommerce_gla_debug_message', sprintf( 'Skipping product (ID: %s) because it does not pass validation: %s', $product->get_id(), wp_json_encode( $validation_result ) ), __METHOD__ ); continue; } // add shipping for all selected target countries array_walk( $target_countries, [ $adapted_product, 'add_shipping_country' ] ); $request_entries[] = new BatchProductRequestEntry( $product->get_id(), $adapted_product ); } catch ( GoogleListingsAndAdsException $exception ) { do_action( 'woocommerce_gla_error', sprintf( 'Skipping product (ID: %s) due to exception: %s', $product->get_id(), $exception->getMessage() ), __METHOD__ ); continue; } } return $request_entries; } /** * @param WCProductAdapter $product * * @return BatchInvalidProductEntry|true */ protected function validate_product( WCProductAdapter $product ) { $violations = $this->validator->validate( $product ); if ( 0 !== count( $violations ) ) { $invalid_product = new BatchInvalidProductEntry( $product->get_wc_product()->get_id() ); $invalid_product->map_validation_violations( $violations ); return $invalid_product; } return true; } /** * Filters and returns an array of request entries for Google products that should no longer be submitted for the selected target audience. * * @param WC_Product[] $products * * @return BatchProductIDRequestEntry[] */ public function generate_stale_products_request_entries( array $products ): array { $target_audience = $this->target_audience->get_target_countries(); $request_entries = []; foreach ( $products as $product ) { $google_ids = $this->meta_handler->get_google_ids( $product ) ?: []; $stale_ids = array_diff_key( $google_ids, array_flip( $target_audience ) ); foreach ( $stale_ids as $stale_id ) { $request_entries[ $stale_id ] = new BatchProductIDRequestEntry( $product->get_id(), $stale_id ); } } return $request_entries; } /** * Returns an array of request entries for Google products that should no * longer be submitted for every target country. * * @since 1.1.0 * * @param WC_Product[] $products * * @return BatchProductIDRequestEntry[] */ public function generate_stale_countries_request_entries( array $products ): array { $main_target_country = $this->target_audience->get_main_target_country(); $request_entries = []; foreach ( $products as $product ) { $google_ids = $this->meta_handler->get_google_ids( $product ) ?: []; $stale_ids = array_diff_key( $google_ids, array_flip( [ $main_target_country ] ) ); foreach ( $stale_ids as $stale_id ) { $request_entries[ $stale_id ] = new BatchProductIDRequestEntry( $product->get_id(), $stale_id ); } } return $request_entries; } }