resolver = $resolver ? $resolver : new CostResolver(); } /** * Aggregate coverage for supported simple products and purchasable variations. * * @param int $missing_limit Max missing rows to return for UI. * @return array */ public function summarize( $missing_limit = 50 ) { $exact = 0; $estimated = 0; $missing = 0; $total = 0; $missing_rows = array(); $product_ids = $this->query_supported_parent_ids(); $population = $this->published_product_count(); $hit_target_cap = false; foreach ( $product_ids as $product_id ) { if ( $total >= self::TARGET_CAP ) { $hit_target_cap = true; break; } $product = wc_get_product( $product_id ); if ( ! $product ) { continue; } $type = $product->get_type(); if ( 'variable' === $type ) { $children = $product->get_children(); foreach ( $children as $variation_id ) { if ( $total >= self::TARGET_CAP ) { $hit_target_cap = true; break 2; } $variation = wc_get_product( $variation_id ); if ( ! $variation || ! $variation->is_purchasable() ) { continue; } $this->tally( $product_id, (int) $variation_id, $variation->get_name(), $exact, $estimated, $missing, $total, $missing_rows, $missing_limit ); } continue; } if ( ! in_array( $type, array( 'simple', 'virtual', 'downloadable' ), true ) && ! $product->is_type( 'simple' ) ) { // External/grouped/subscription parents skipped for Free coverage denominator. if ( 'external' === $type || 'grouped' === $type ) { continue; } } if ( ! $product->is_purchasable() && 'simple' === $type ) { continue; } $this->tally( $product_id, 0, $product->get_name(), $exact, $estimated, $missing, $total, $missing_rows, $missing_limit ); } $partial = $population > self::CATALOG_CAP || $hit_target_cap; $coverage_pct = $total > 0 ? round( ( ( $exact + $estimated ) / $total ) * 100, 1 ) : 0.0; $exact_pct = $total > 0 ? round( ( $exact / $total ) * 100, 1 ) : 0.0; $est_pct = $total > 0 ? round( ( $estimated / $total ) * 100, 1 ) : 0.0; $miss_pct = $total > 0 ? round( ( $missing / $total ) * 100, 1 ) : 0.0; return array( 'total' => $total, 'exact' => $exact, 'estimated' => $estimated, 'missing' => $missing, 'coverage_percent' => $coverage_pct, 'exact_percent' => $exact_pct, 'estimated_percent' => $est_pct, 'missing_percent' => $miss_pct, 'missing_items' => $missing_rows, 'status' => $partial ? 'partial' : 'complete', 'catalog_cap' => self::CATALOG_CAP, 'target_cap' => self::TARGET_CAP, 'parent_population' => $population, 'evaluated_population' => $total, 'catalog_population' => $population, 'partial' => $partial, 'summary_line' => sprintf( /* translators: 1: coverage percent, 2: resolved count, 3: total */ __( 'Cost coverage: %1$s%% (%2$d of %3$d supported items)', 'marginbeacon' ), (string) $coverage_pct, $exact + $estimated, $total ), 'breakdown_line' => sprintf( /* translators: 1: exact pct, 2: estimated pct, 3: missing pct */ __( 'Exact: %1$s%% · Estimated: %2$s%% · Missing: %3$s%%', 'marginbeacon' ), (string) $exact_pct, (string) $est_pct, (string) $miss_pct ), ); } /** * @return int */ private function published_product_count() { $counts = wp_count_posts( 'product' ); return isset( $counts->publish ) ? (int) $counts->publish : 0; } /** * @return int[] */ private function query_supported_parent_ids() { $ids = get_posts( array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => self::CATALOG_CAP, 'fields' => 'ids', 'orderby' => 'ID', 'order' => 'ASC', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); return is_array( $ids ) ? array_map( 'intval', $ids ) : array(); } /** * @param int $product_id Parent ID. * @param int $variation_id Variation ID. * @param string $name Display name. * @param int $exact Exact counter. * @param int $estimated Estimated counter. * @param int $missing Missing counter. * @param int $total Total counter. * @param array $missing_rows Missing rows. * @param int $missing_limit Limit. * @return void */ private function tally( $product_id, $variation_id, $name, &$exact, &$estimated, &$missing, &$total, array &$missing_rows, $missing_limit ) { ++$total; $resolved = $this->resolver->resolve_catalog( $product_id, $variation_id ); $conf = $resolved->confidence(); if ( 'exact' === $conf && $resolved->is_resolved() ) { ++$exact; return; } if ( 'estimated' === $conf && $resolved->is_resolved() ) { ++$estimated; return; } ++$missing; if ( count( $missing_rows ) < $missing_limit ) { $missing_rows[] = array( 'product_id' => $product_id, 'variation_id' => $variation_id, 'name' => $name, 'reason_code' => $resolved->reason_code() ? $resolved->reason_code() : 'MB_COST_MISSING', 'edit_url' => get_edit_post_link( $variation_id > 0 ? $variation_id : $product_id, 'raw' ), ); } } }