admin管理员组文章数量:1390822
I am trying to produce an API that basically delivers the same results as the "archive" search page, with their widgets, to say, show terms that matches the queried products. So for instance, if you query products with a specific category, only the terms associated with products in that category will be shown.
All this logic is based in the class WC_Widget_Layered_Nav
, more specifically in the get_filtered_term_product_counts
method.
However, it's a private method. So I might need to rewrite everything myself. Or would there be a way to do this smarter?
Question: How can I retrieve specific product attributes & categories ONLY if they exist within the queried resultset?
I guess I can't be the first to develop an API for WooCommerce where this is needed.
I am trying to produce an API that basically delivers the same results as the "archive" search page, with their widgets, to say, show terms that matches the queried products. So for instance, if you query products with a specific category, only the terms associated with products in that category will be shown.
All this logic is based in the class WC_Widget_Layered_Nav
, more specifically in the get_filtered_term_product_counts
method.
However, it's a private method. So I might need to rewrite everything myself. Or would there be a way to do this smarter?
Question: How can I retrieve specific product attributes & categories ONLY if they exist within the queried resultset?
I guess I can't be the first to develop an API for WooCommerce where this is needed.
Share Improve this question asked Feb 12, 2020 at 14:20 FooBarFooBar 4441 gold badge8 silver badges23 bronze badges1 Answer
Reset to default 0So I realized there's no open API for WooCommerce that can handle this, and my research declared that there were nobody else doing this. At least not to my knowledge.
So I created the code, and it works. I'll share my code in case anybody else would like to use WordPress as a headless CMS where WooCommerce is installed.
The code is not that pretty, but it does the job, and I'm happy with the result:
https://gist.github/simplenotezy/b0eb1552c12fd13a46c8683fb79c3d6c
<?php
/**
* This allows you to get the archive page / shop page data through the WordPress REST APi.
* It's not beautiful, but it works.
*
* The REST URL: https://yourwebsite/wp-json/custom-woocommerce/v1/shop/slug
*
* Parameters:
* "filter_{attribute_name}=some_value", e.g. "/shop/?filter_color=red". You can filter multiple values: ?filter_color=red,blue&filter_size=1,2
* "categories", e.g. "/shop/?categories=shoes,shirts&filter_color=blue"
*
* The response will be your products, as well as a key called "filters". This contains all relevant filters related to the _specific query_ you just made.
*/
add_action( 'rest_api_init', function () {
register_rest_route( 'custom-woocommerce/v1', '/shop/(?P<slug>\S+)', array(
'methods' => 'GET',
'callback' => function($data) {
$page = (isset($_GET['page']) && $_GET['page']) ? $_GET['page'] : 1;
$seed = (isset($_GET['seed']) && $_GET['seed']) ? $_GET['seed'] : rand(11111,99999);
$args = [
'post_type' => 'product',
'posts_per_page' => 12,
'orderby' => 'RAND(' . $seed . ')',
'tax_query' => ['relation' => 'AND'],
'page' => $page
];
$response = [
'seed' => $seed
];
/**
* Search product attributes
*/
$public_wc_taxonomies = array_values(array_map(function($taxonomy) {
return 'pa_' . $taxonomy->attribute_name;
}, array_filter(wc_get_attribute_taxonomies(), function($taxonomy) {
return $taxonomy->attribute_public;
})));
$taxonomies = [];
foreach($_GET as $getParameter => $attribute_slugs) {
if(substr( $getParameter, 0, 7 ) === "filter_") {
if(!isset($attribute_tax_query))
$attribute_tax_query = ['relation' => 'OR'];
$attribute_slugs = explode(',', $attribute_slugs);
$taxonomy = str_replace('filter_', 'pa_', $getParameter);
foreach($attribute_slugs as $attribute_slug) {
$taxonomies[$taxonomy][] = get_term_by('slug', $attribute_slug, $taxonomy);
$attribute_tax_query[] = [
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => $attribute_slug
];
}
}
}
if(isset($attribute_tax_query))
$args['tax_query'][] = $attribute_tax_query;
/**
* Search categories
*/
if(isset($_GET['categories'])) {
$cats = explode(',', $_GET['categories']);
$categories = [];
$category_tax_query = ['relation' => 'OR'];
foreach($cats as $category_slug) {
$category = get_term_by('slug', $category_slug, 'product_cat');
if(!$category)
continue; // we couldn't find a category with that slug
$categories[] = [
'name' => $category->name,
'slug' => $category->slug,
'count' => $category->count,
];
$category_tax_query[] = [
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $category_slug
];
}
$args['tax_query'][] = $category_tax_query;
$response['categories'] = $categories;
}
$query = new WP_Query($args);
$response['products'] = $query->posts;
/**
* If this is the first page, output attributes related to this search
*/
if($args['page'] == 1) {
global $wp_the_query;
$wp_the_query = $query; // little hack to get interals of WooCommerce to function properly
(wc())->query->pre_get_posts($wp_the_query);
foreach($public_wc_taxonomies as $taxonomy) {
$terms = get_terms( $taxonomy, array( 'hide_empty' => '1' ) );
$response['filters'][$taxonomy] = get_terms_and_count_for_current_query($terms, $taxonomy, (isset($args['tax_query'][0]['relation'])) ? $args['tax_query'][0]['relation'] : 'OR');
if(!$response['filters'][$taxonomy]) {
unset($response['filters'][$taxonomy]);
}
}
}
/**
* Outut categories if they are not yet requested
*/
if(!isset($_GET['categories'])) {
$response['filters']['product_cat'] = get_terms_and_count_for_current_query(get_terms( 'product_cat', array( 'hide_empty' => '1' ) ), 'product_cat', 'or');
}
return $response;
}));
});
/**
* Code mostly copied from WooCommerce internals, then adjusted.
* The reason for this is, that their API is private, so you can't access it outside without even nastier hacks
* So. I did a bit of workarounds to get the right response..
*/
function wc_get_filtered_term_product_counts($term_ids, $taxonomy, $query_type = 'or') {
global $wpdb;
$tax_query = WC_Query::get_main_tax_query();
$meta_query = WC_Query::get_main_meta_query();
if ( 'or' === $query_type ) {
foreach ( $tax_query as $key => $query ) {
if ( isset($query['taxonomy']) && is_array( $query ) && $taxonomy === $query['taxonomy'] ) {
unset( $tax_query[ $key ] );
}
}
}
$meta_query = new WP_Meta_Query( $meta_query );
$tax_query = new WP_Tax_Query( $tax_query );
$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' );
// Generate query.
$query = array();
$query['select'] = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) as term_count, terms.term_id as term_count_id";
$query['from'] = "FROM {$wpdb->posts}";
$query['join'] = "
INNER JOIN {$wpdb->term_relationships} AS term_relationships ON {$wpdb->posts}.ID = term_relationships.object_id
INNER JOIN {$wpdb->term_taxonomy} AS term_taxonomy USING( term_taxonomy_id )
INNER JOIN {$wpdb->terms} AS terms USING( term_id )
" . $tax_query_sql['join'] . $meta_query_sql['join'];
$query['where'] = "
WHERE {$wpdb->posts}.post_type IN ( 'product' )
AND {$wpdb->posts}.post_status = 'publish'"
. $tax_query_sql['where'] . $meta_query_sql['where'] .
'AND terms.term_id IN (' . implode( ',', array_map( 'absint', $term_ids ) ) . ')';
$search = WC_Query::get_main_search_query_sql();
if ( $search ) {
$query['where'] .= ' AND ' . $search;
}
$query['group_by'] = 'GROUP BY terms.term_id';
$query = apply_filters( 'woocommerce_get_filtered_term_product_counts_query', $query );
$query = implode( ' ', $query );
// We have a query - let's see if cached results of this query already exist.
$query_hash = md5( $query );
// Maybe store a transient of the count values.
$cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true );
if ( true === $cache ) {
$cached_counts = (array) get_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ) );
} else {
$cached_counts = array();
}
if ( ! isset( $cached_counts[ $query_hash ] ) ) {
$results = $wpdb->get_results( $query, ARRAY_A ); // @codingStandardsIgnoreLine
$counts = array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
$cached_counts[ $query_hash ] = $counts;
if ( true === $cache ) {
set_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ), $cached_counts, DAY_IN_SECONDS );
}
}
return array_map( 'absint', (array) $cached_counts[ $query_hash ] );
}
function get_terms_and_count_for_current_query($terms, $taxonomy, $query_type = 'or') {
$term_counts = wc_get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type );
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
$found = false;
$return_terms = [];
foreach ( $terms as $index => $term ) {
$current_values = isset( $_chosen_attributes[ $taxonomy ]['terms'] ) ? $_chosen_attributes[ $taxonomy ]['terms'] : array();
$option_is_set = in_array( $term->slug, $current_values, true );
$count = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
if ( 0 < $count ) {
$found = true;
} elseif ( 0 === $count && ! $option_is_set ) {
continue;
}
$return_terms[] = [
'name' => html_entity_decode ($term->name),
'slug' => $term->slug,
'taxonomy' => $term->taxonomy,
'count' => $count,
'is_set' => $option_is_set
];
}
return $return_terms;
}
本文标签: phpgetfilteredtermproductcountsGet product terms if any products
版权声明:本文标题:php - get_filtered_term_product_counts - Get product terms if any products 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744753634a2623329.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论