问题
I am using AJAX to load more products on a WooCommerce archive. I have used AJAX to "Load More" once before on this page. I have used the same code for the most part, just altered the WP_Query
arguments to suit my needs. I can't understand why my code doesn't work.
JS
/**
* AJAX Load (Lazy Load) events
*/
$('#load-more').click( function(e){
e.preventDefault();
ajax_next_posts()
$('body').addClass('ajaxLoading');
});
var ajaxLock = false; // ajaxLock is just a flag to prevent double clicks and spamming
if( !ajaxLock ) {
function ajax_next_posts() {
ajaxLock = true;
// How many posts there's total
var totalPosts = parseInt( jQuery( '#found-posts' ).text() );
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length
// How many do you want to load in single patch
var postsPerPage = 1;
// Ajax call itself
$.ajax({
method: 'POST',
url: leafshop.ajax_url,
data: {
action: 'ajax_next_posts',
post_offset: postOffset,
posts_per_page: postsPerPage,
product_cat: cat_id
},
dataType: 'json'
})
.done( function( response ) { // Ajax call is successful
// console.log( response );
// Add new posts
jQuery( '.product-grid' ).append( response[0] );
// Log SQL query
jQuery( '#query > pre' ).text( response[2] );
// Update the count of total posts
// jQuery( '#found-posts' ).text( response[1] );
ajaxLock = false;
console.log( 'Success' );
$('body').removeClass('ajaxLoading');
// How many posts there's total
var totalPosts = parseInt( jQuery( '#found-posts' ).text() );
console.log( "Total Posts: " + totalPosts );
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length
console.log( "Posts currently showing: " + postOffset );
// Hide button if all posts are loaded
if( totalPosts < postOffset + ( 1 * postsPerPage ) ) {
jQuery( '#load-more' ).fadeOut();
}
})
// .fail( function() {
.fail( function(jqXHR, textStatus, errorThrown) { // Ajax call is not successful, still remove lock in order to try again
ajaxLock = false;
console.log(XMLHttpRequest);
console.log(textStatus);
console.log(errorThrown);
console.log( 'Failed' );
});
}
}
PHP
<?php
/**
* Load next 12 products using AJAX
*/
function ajax_next_posts() {
global $wpdb;
// Build query
$args = array(
'post_type' => 'product'
);
// Get offset
if( !empty( $_POST['post_offset'] ) ) {
$offset = $_POST['post_offset'];
$args['offset'] = $offset;
// Also have to set posts_per_page, otherwise offset is ignored
$args['posts_per_page'] = 12;
}
// Get posts per page
if( !empty( $_POST['posts_per_page'] ) ) {
// Also have to set posts_per_page, otherwise offset is ignored
$args['posts_per_page'] = $_POST['posts_per_page'];
}
// Set tax query if on cat page
if( !empty( $_POST['product_cat'] ) ){
$args['tax_query'] = array(
'taxonomy' => 'product_cat',
'terms' => array( (int)$_POST['product_cat'] ),
'field' => 'id',
'operator' => 'IN',
'include_children' => 1
);
}
$count_results = '0';
$ajax_query = new WP_Query( $args );
// Results found
if ( $ajax_query->have_posts() ) {
$count_results = $ajax_query->found_posts;
// Start "saving" results' HTML
$results_html = '';
ob_start();
while( $ajax_query->have_posts() ) {
$ajax_query->the_post();
echo wc_get_template_part( 'content', 'product' );
}
// "Save" results' HTML as variable
$results_html = ob_get_clean();
}
// Build ajax response
$response = array();
// 1. value is HTML of new posts and 2. is total count of posts
global $wpdb;
array_push ( $response, $results_html, $count_results, $wpdb->last_query );
echo json_encode( $response );
// Always use die() in the end of ajax functions
die();
}
add_action('wp_ajax_ajax_next_posts', 'ajax_next_posts');
add_action('wp_ajax_nopriv_ajax_next_posts', 'ajax_next_posts');
The AJAX call runs successfully but doesn't return what I'd expect it to. I'd expect it to return the next product from the current category, instead it returns a product from a different category. The development site is accessible on http://leaf.kalodigital.co.uk and I have been using the "Black Tea" page in my testing. Any help would be greatly appreciated.
回答1:
Method
After some further research, I found that WooCommerce contains a class names WC_Poduct_Query
. Following their documentation, found here, I rebuilt my query. This was working in the sense that it was querying for the correct products and returns the ones I'd expect it to as an array of WC_Product_Variable
Objects. I proceeded to, within a foreach
loop, use setup_postdata( $post );
to set the product Objects up so that I could use the wc_get_template_parts();
function to call the content-product.php
template to format the output of the data. I found that this didn't work, although I couldn't figure to why this was the case, using setup-postdata();
was causing the object to turn into [{id:0,filter:raw},{id:0,filter:raw}]
. I imagine this would be related to the WC_Product_Variable
Objects not matching the expect format that setup_postdata();
usually expects from a WP_Post
Object.
Nonetheless, I reverted to using WP_Query
for my query, rebuilt the query script from scratch and, would you believe it, everything worked as expected to. Below is my working code to AJAX load the next "bunch" of products on any WooCommerce Archive page, at the click of a button.
Code
JS
/**
* AJAX Load (Lazy Load) events
*/
//-- Settings
// How many do you want to load each button click?
var postsPerPage = 12;
//-- /Settings
// How many posts there's total
var totalPosts = parseInt( jQuery( '#found-posts' ).text() );
// if( totalPosts == postOffset ) {
// jQuery( '#load-more' ).fadeOut();
// }
$('#load-more').click( function(e){
e.preventDefault();
// Get current category
var cat_id = $(this).data('product-category');
ajax_next_posts( cat_id );
$('body').addClass('ajaxLoading');
});
var ajaxLock = false; // ajaxLock is just a flag to prevent double clicks and spamming
if( !ajaxLock ) {
function ajax_next_posts( cat_id ) {
ajaxLock = true;
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length;
// Ajax call itself
$.ajax({
method: 'POST',
url: leafshop.ajax_url,
data: {
action: 'ajax_next_posts',
post_offset: postOffset,
posts_per_page: postsPerPage,
product_cat: cat_id
},
dataType: 'json'
})
.done( function( response ) { // Ajax call is successful
// Add new posts
jQuery( '.product-grid' ).append( response[0] );
// Update Post Offset
var postOffset = jQuery( 'li.product' ).length;
ajaxLock = false;
console.log( 'Success' );
$('body').removeClass('ajaxLoading');
// How many posts there's total
console.log( "Total Posts: " + totalPosts );
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length
console.log( "Posts on Page: " + postOffset );
// Hide button if all posts are loaded
if( ( totalPosts - postOffset ) <= 0 ) {
jQuery( '#load-more' ).fadeOut();
}
})
// .fail( function() {
.fail( function(jqXHR, textStatus, errorThrown) { // Ajax call is not successful, still remove lock in order to try again
ajaxLock = false;
console.log(XMLHttpRequest);
console.log(textStatus);
console.log(errorThrown);
console.log( 'Failed' );
});
}
}
PHP
<?php
/**
* Load next 12 products using AJAX
*/
function ajax_next_posts() {
global $product;
// Build Query
$args = array(
'post_type' => 'product',
'posts_per_page' => (int)$_POST['posts_per_page'],
'orderby' => 'title',
'order' => 'ASC',
'offset' => (int)$_POST['post_offset'],
);
if( !empty( $_POST['product_cat'] ) ) {
$args['tax_query'] = array(
'relation' => 'AND',
array (
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $_POST['product_cat'],
'operator' => 'IN'
),
);
}
$count_results = '0';
$ajax_query = new WP_Query( $args );
// Results found
if( $ajax_query->have_posts() ){
// Start "saving" results' HTML
$results_html = '';
ob_start();
while( $ajax_query->have_posts() ) {
$ajax_query->the_post();
echo wc_get_template_part( 'content', 'product' );
}
wp_reset_postdata();
// "Save" results' HTML as variable
$results_html = ob_get_clean();
} else {
// Start "saving" results' HTML
$results_html = '';
ob_start();
echo "none found!";
// "Save" results' HTML as variable
$results_html = ob_get_clean();
}
// Build ajax response
$response = array();
// 1. value is HTML of new posts and 2. is total count of posts
array_push ( $response, $results_html );
echo json_encode( $response );
// Always use die() in the end of ajax functions
die();
}
add_action('wp_ajax_ajax_next_posts', 'ajax_next_posts');
add_action('wp_ajax_nopriv_ajax_next_posts', 'ajax_next_posts');
Use Cases
By refactoring the supplied AJAX code, it would be possible to turn this solution into an "infinite scrolling" system as opposed to a "Load More" button solution. Please feel free to reuse this code where it may see fit!
来源:https://stackoverflow.com/questions/52189258/using-ajax-to-load-more-products-woocommerce