Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
62.50% |
25 / 40 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
| Wc_Block_Helpers | |
62.50% |
25 / 40 |
|
0.00% |
0 / 2 |
38.04 | |
0.00% |
0 / 1 |
| get_currency_display | |
88.24% |
15 / 17 |
|
0.00% |
0 / 1 |
9.13 | |||
| get_catalog_price_extents | |
43.48% |
10 / 23 |
|
0.00% |
0 / 1 |
28.06 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * WooCommerce helper utilities shared between `filter-wc-*` blocks. |
| 4 | * |
| 5 | * @package automattic/jetpack-search |
| 6 | */ |
| 7 | |
| 8 | namespace Automattic\Jetpack\Search; |
| 9 | |
| 10 | /** |
| 11 | * Render-time helpers for the WC price / price-slider blocks. Separate from |
| 12 | * `Search_Blocks` so the WC-touching code has a dedicated home. |
| 13 | */ |
| 14 | class Wc_Block_Helpers { |
| 15 | |
| 16 | const PRICE_EXTENTS_TRANSIENT = 'jetpack_search_wc_price_extents'; |
| 17 | const PRICE_EXTENTS_TTL_SEC = 5 * MINUTE_IN_SECONDS; |
| 18 | |
| 19 | /** |
| 20 | * Currency adornment (symbol + position) for a price block. Empty author |
| 21 | * values fall through to WC settings; `$`/`left` for plain WP installs. |
| 22 | * |
| 23 | * @param string $author_symbol '' to defer to WC. |
| 24 | * @param string $author_position '' to defer to WC. |
| 25 | * @return array{symbol:string,position:string} symbol clipped to 2 chars. |
| 26 | */ |
| 27 | public static function get_currency_display( string $author_symbol, string $author_position ): array { |
| 28 | $symbol = $author_symbol; |
| 29 | $position = $author_position; |
| 30 | |
| 31 | if ( '' === $symbol && function_exists( 'get_woocommerce_currency_symbol' ) ) { |
| 32 | // @phan-suppress-next-line PhanUndeclaredFunction |
| 33 | $wc_symbol = (string) get_woocommerce_currency_symbol(); |
| 34 | // WC returns HTML entities (`$`, `€`). Decode once so `mb_substr` |
| 35 | // sees a character (not half an entity) and `esc_html` round-trips cleanly. |
| 36 | $symbol = html_entity_decode( $wc_symbol, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); |
| 37 | } |
| 38 | if ( '' === $symbol ) { |
| 39 | $symbol = '$'; |
| 40 | } |
| 41 | |
| 42 | if ( '' === $position ) { |
| 43 | $wc_pos = (string) get_option( 'woocommerce_currency_pos', 'left' ); |
| 44 | $position = ( 'right' === $wc_pos || 'right_space' === $wc_pos ) ? 'right' : 'left'; |
| 45 | } |
| 46 | if ( ! in_array( $position, array( 'left', 'right' ), true ) ) { |
| 47 | $position = 'left'; |
| 48 | } |
| 49 | |
| 50 | $symbol_short = function_exists( 'mb_substr' ) ? mb_substr( $symbol, 0, 2 ) : substr( $symbol, 0, 2 ); |
| 51 | |
| 52 | return array( |
| 53 | 'symbol' => $symbol_short, |
| 54 | 'position' => $position, |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Catalog-wide price extents from WC's `wc_product_meta_lookup`. Bounds |
| 60 | * stay stable for the page (matches WC's slider — applying filters narrows |
| 61 | * products without shrinking the track). Transient-cached; null-extents |
| 62 | * are cached too so broken setups don't re-run the query. |
| 63 | * |
| 64 | * @return array{min:float|null,max:float|null} |
| 65 | */ |
| 66 | public static function get_catalog_price_extents(): array { |
| 67 | $cached = function_exists( 'get_transient' ) ? get_transient( self::PRICE_EXTENTS_TRANSIENT ) : false; |
| 68 | if ( is_array( $cached ) ) { |
| 69 | return $cached; |
| 70 | } |
| 71 | |
| 72 | $extents = array( |
| 73 | 'min' => null, |
| 74 | 'max' => null, |
| 75 | ); |
| 76 | |
| 77 | if ( function_exists( 'wc_get_product' ) ) { |
| 78 | global $wpdb; |
| 79 | if ( isset( $wpdb ) && ! empty( $wpdb->wc_product_meta_lookup ) ) { |
| 80 | // Same indexed lookup table WC's own slider/widget/Store API hit; |
| 81 | // scales linearly with product count (vs. a REGEXP `postmeta` scan). |
| 82 | // Joined to `wp_posts` so draft/pending/trashed don't inflate — WC |
| 83 | // populates the table on every save, not only on publish. |
| 84 | $row = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching |
| 85 | "SELECT MIN(l.min_price) AS min_price, MAX(l.max_price) AS max_price |
| 86 | FROM {$wpdb->wc_product_meta_lookup} l |
| 87 | INNER JOIN {$wpdb->posts} p ON p.ID = l.product_id |
| 88 | WHERE p.post_status = 'publish' |
| 89 | AND p.post_type IN ( 'product', 'product_variation' )" |
| 90 | ); |
| 91 | if ( $row && null !== $row->min_price && null !== $row->max_price ) { |
| 92 | $extents = array( |
| 93 | 'min' => (float) $row->min_price, |
| 94 | 'max' => (float) $row->max_price, |
| 95 | ); |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | if ( function_exists( 'set_transient' ) ) { |
| 101 | set_transient( self::PRICE_EXTENTS_TRANSIENT, $extents, self::PRICE_EXTENTS_TTL_SEC ); |
| 102 | } |
| 103 | |
| 104 | return $extents; |
| 105 | } |
| 106 | } |