array( 'publish', 'private', 'draft' ), 'type' => array_keys( wc_get_product_types() ), 'orderby' => 'title', 'order' => 'ASC', 'limit' => self::PAGE_SIZE + 1, 'offset' => $offset, 'return' => 'ids', ) ); } else { $store = \WC_Data_Store::load( 'product' ); $ids = array_slice( (array) $store->search_products( $search, '', true, false, $offset + self::PAGE_SIZE + 1 ), $offset ); } $ids = array_values( array_filter( array_map( 'intval', (array) $ids ) ) ); $more = count( $ids ) > self::PAGE_SIZE; $items = array(); foreach ( array_slice( $ids, 0, self::PAGE_SIZE ) as $id ) { $product = wc_get_product( $id ); if ( $product ) { $items[] = array( 'id' => $id, 'label' => self::product_label( $product ), 'meta' => self::product_meta( $product ), ); } } return self::page( $items, $more ); } /** * @param int[] $ids Product IDs. * @return array ID => "Name (#ID)". */ public static function product_labels( array $ids ): array { $labels = array(); foreach ( $ids as $id ) { $product = wc_get_product( $id ); if ( $product ) { $labels[ $id ] = self::product_label( $product ); } } return $labels; } /** * @param string $taxonomy product_cat or product_tag. * @param string $search Name; empty lists all by name. * @param int $page Page, from 1. * @return array{items: array, more: bool} */ public static function terms( string $taxonomy, string $search, int $page = 1 ): array { $terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'search' => $search, 'orderby' => 'name', 'number' => self::PAGE_SIZE + 1, 'offset' => ( max( 1, $page ) - 1 ) * self::PAGE_SIZE, ) ); $terms = is_array( $terms ) ? $terms : array(); $items = array(); foreach ( array_slice( $terms, 0, self::PAGE_SIZE ) as $term ) { $items[] = self::term_item( $term, false ); } return self::page( $items, count( $terms ) > self::PAGE_SIZE ); } /** * Values of every global attribute, e.g. "Colour: Red". Searching for an attribute's name lists * all its values. * * @param string $search Value or attribute name; empty lists all. * @param int $page Page, from 1. * @return array{items: array, more: bool} */ public static function attribute_terms( string $search, int $page = 1 ): array { if ( ! function_exists( 'wc_get_attribute_taxonomies' ) ) { return self::page( array(), false ); } $offset = ( max( 1, $page ) - 1 ) * self::PAGE_SIZE; $wanted = $offset + self::PAGE_SIZE + 1; $found = array(); foreach ( wc_get_attribute_taxonomies() as $attribute ) { $by_name = '' !== $search && false !== stripos( (string) $attribute->attribute_label, $search ); $terms = get_terms( array( 'taxonomy' => wc_attribute_taxonomy_name( $attribute->attribute_name ), 'hide_empty' => false, 'search' => $by_name ? '' : $search, 'orderby' => 'name', 'number' => $wanted - count( $found ), ) ); foreach ( is_array( $terms ) ? $terms : array() as $term ) { $found[] = $term; } if ( count( $found ) >= $wanted ) { break; } } $items = array(); foreach ( array_slice( $found, $offset, self::PAGE_SIZE ) as $term ) { $items[] = self::term_item( $term, true ); } return self::page( $items, count( $found ) > $offset + self::PAGE_SIZE ); } /** * @param int[] $ids Term IDs. * @param bool $attribute Prefix the attribute name, e.g. "Colour: Red". * @return array */ public static function term_labels( array $ids, bool $attribute = false ): array { if ( ! $ids ) { return array(); } $terms = get_terms( array( 'include' => $ids, 'hide_empty' => false, ) ); $labels = array(); foreach ( is_array( $terms ) ? $terms : array() as $term ) { $labels[ (int) $term->term_id ] = self::term_label( $term, $attribute ); } return $labels; } /** * @param \WP_Term $term Term. * @param bool $attribute Prefix the attribute name. */ private static function term_item( \WP_Term $term, bool $attribute ): array { return array( 'id' => (int) $term->term_id, 'label' => self::term_label( $term, $attribute ), /* translators: %d: number of products. */ 'meta' => sprintf( _n( '%d product', '%d products', (int) $term->count, 'webstyle-coupons' ), (int) $term->count ), ); } /** * @param \WP_Term $term Term. * @param bool $attribute Prefix the attribute name. */ private static function term_label( \WP_Term $term, bool $attribute ): string { $name = html_entity_decode( $term->name, ENT_QUOTES ); if ( $attribute && function_exists( 'wc_attribute_label' ) ) { return wc_attribute_label( $term->taxonomy ) . ': ' . $name; } // Two categories can share a name in different branches; the parent tells them apart. if ( $term->parent ) { $parent = get_term( $term->parent, $term->taxonomy ); if ( $parent instanceof \WP_Term ) { return html_entity_decode( $parent->name, ENT_QUOTES ) . ' › ' . $name; } } return $name; } /** * @param \WC_Product $product Product. */ private static function product_label( $product ): string { return sprintf( '%s (#%d)', wp_strip_all_tags( html_entity_decode( $product->get_name(), ENT_QUOTES ) ), $product->get_id() ); } /** * SKU and price, so products with similar names can be told apart. * * @param \WC_Product $product Product. */ private static function product_meta( $product ): string { $parts = array(); if ( $product->get_sku() ) { /* translators: %s: product SKU. */ $parts[] = sprintf( __( 'SKU %s', 'webstyle-coupons' ), $product->get_sku() ); } $price = $product->get_price_html(); if ( '' !== $price ) { $parts[] = trim( html_entity_decode( wp_strip_all_tags( $price ), ENT_QUOTES ) ); } return implode( ' · ', $parts ); } /** * @param array $items Items. * @param bool $more More pages exist. * @return array{items: array, more: bool} */ private static function page( array $items, bool $more ): array { return array( 'items' => $items, 'more' => $more, ); } }