'chinese-new-year', 'limit' => 8 ) ); * if ( empty( $r['error'] ) ) { foreach ( $r['items'] as $p ) { echo esc_html( $p['name'] ); } } * * URLs in the results already point at this site's shop routes (preview prefix while in preview mode). * Prices are numbers in THB (incl. VAT). No internal ids, no stock counts — `in_stock` is a boolean. * Preview mode: visitors get `error = 'preview'` (same rule as the shop pages and shortcodes). * * @package AooShop */ defined( 'ABSPATH' ) || exit; /** * Internal: run a data call with the preview gate + error shape. * * @param callable $fn fn(AooShop_Data_Api): array|WP_Error. * @param array $empty Empty result shape. * @return array */ function aooshop__data_call( $fn, array $empty ) { if ( ! function_exists( 'aooshop' ) || ! isset( aooshop()->data_api ) ) { return $empty + array( 'error' => 'not_loaded' ); } $api = aooshop()->data_api; if ( ! $api->can_serve() ) { return $empty + array( 'error' => 'preview' ); } $res = call_user_func( $fn, $api ); if ( is_wp_error( $res ) ) { return $empty + array( 'error' => (string) $res->get_error_code() ); } return $res; } /** * Products of the shop. * * @param array $args { * @type string $collection Collection slug. * @type string $category Category slug (or name). * @type string $brand Brand slug. * @type string $price Price bucket slug from the shop (e.g. '1500-2000') — or use price_min / price_max. * @type int $price_min 0–10,000,000 (ignored when `price` is a valid bucket). * @type int $price_max 0–10,000,000. * @type string $q Search text (≤ 40 chars). * @type string $sort best_selling | newest | name | price_asc | price_desc | manual (collection only). * @type int $limit 1–48 (default 12). * @type int $page 1–1000. * } * @return array{items: array, * total:int, page:int, error?:string} */ function aooshop_products( $args = array() ) { return aooshop__data_call( function ( $api ) use ( $args ) { return $api->products( (array) $args ); }, array( 'items' => array(), 'total' => 0, 'page' => 1 ) ); } /** * Filter counts for a set of filters (same filters as aooshop_products(), no sort/limit/page). * `error = 'facets_unavailable'` while aoo can't count yet — show no numbers then (never guess). * * @param array $args See aooshop_products(). * @return array{total:int, categories:array, brands:array, collections:array, price:array, error?:string} * categories/brands: [{name, slug, n, url}] · collections: [{name, slug, group, n, url}] · * price: [{slug, label, title, min, max|null, n, url (budget page)}] */ function aooshop_facets( $args = array() ) { return aooshop__data_call( function ( $api ) use ( $args ) { return $api->facets( (array) $args ); }, array( 'total' => 0, 'categories' => array(), 'brands' => array(), 'collections' => array(), 'price' => array() ) ); } /** * Collections (occasions, festivals, "for whom" …) in the shop's order. * * @param string $group Exact group label ('' = all). * @return array{items: array, error?:string} * days_left: days until the next festival date in Thai time (0 = today · null = no countdown). */ function aooshop_collections( $group = '' ) { return aooshop__data_call( function ( $api ) use ( $group ) { return $api->collections( (string) $group ); }, array( 'items' => array() ) ); } /** * Search suggestions (what the autocomplete shows). * * @param string $q Search text (≤ 40 chars). * @return array{categories: array, * collections: array, * products: array, error?:string} */ function aooshop_suggest( $q ) { return aooshop__data_call( function ( $api ) use ( $q ) { return $api->suggest( (string) $q ); }, array( 'categories' => array(), 'collections' => array(), 'products' => array() ) ); } /** * Approved product reviews from real buyers (newest first). * * @param array $args { * @type string $product Product slug — omit for the latest reviews of the whole shop. * @type int $min_rating 1–5: only list reviews with at least this many stars (the summary still counts all). * @type int $limit 1–24 (default 6). * @type int $page 1–1000. * } * @return array{summary: array{avg:float, count:int, distribution:int[]}, * items: array, videos:string[], reply:string|null, * product: array{name:string, slug:string, url:string, image_thumb:string|null}|null}>, * page:int, has_more:bool, error?:string} */ function aooshop_reviews( $args = array() ) { return aooshop__data_call( function ( $api ) use ( $args ) { return $api->reviews( (array) $args ); }, array( 'summary' => array( 'avg' => 0, 'count' => 0, 'distribution' => array( 0, 0, 0, 0, 0 ), ), 'items' => array(), 'page' => 1, 'has_more' => false, ) ); } /** * "อีก N วัน" / "วันนี้" / '' for a festival date — same rule as the collections countdown. * * @param string|null $next_date YYYY-MM-DD. * @param bool $repeat_yearly Roll forward whole years once the date has passed. * @return string */ function aooshop_countdown_label( $next_date, $repeat_yearly = false ) { return AooShop_Data_Api::countdown_label( AooShop_Data_Api::days_left( $next_date, (bool) $repeat_yearly ) ); }