Frontend AJAX call with WordPress

Hi there,

This is driving me a little crazy.

I’m developing a plugin to retrieve the next couple of posts using AJAX. Here’s the basic idea of what I’m trying to do:

Count the number of posts currently displayed;
Send an AJAX request to a plugin function get_more_posts;
get_more_posts() instantiates a WP_Query object using the AJAX data as its arguments;
get_more_posts() uses a function format_category_posts to convert the WP_Query object into a string of formated posts;
The string is echoed in the function;
The AJAX return is appended to the DOM.

Wordpress has its own (somewhat convoluted) method for handing AJAX calls (http://codex.wordpress.org/AJAX_in_Plugins):

Since Ajax is already built into the core WordPress administration screens, adding more administration-side Ajax functionality to your plugin is fairly straightforward…

Note: Unlike on the admin side, the ajaxurl javascript global does not get automatically defined for you, unless you have BuddyPress or another Ajax-reliant plugin installed. So instead of relying on a global javascript variable, use the tip suggested in the article 5 tips for using Ajax in WordPress and declare a javascript namespace object with its own property, ajaxurl. As the article suggests, use wp_localize_script() to make the URL available to your script, and generate it using this expression: admin_url(‘admin-ajax.php’)

Anyway, after a lot of searching and trying things out, I got a basic AJAX call working on the front end of my site using a plugin:

Plugin php code:


<?php
/*
Plugin Name: More Posts
Description: Get more posts using ajax
Version: 1.0
*/

add_action('wp_enqueue_scripts', 'add_more_posts_script'); //add plugin script;

function add_more_posts_script(){
	if(!is_admin() && is_category()){
		wp_enqueue_script('more-posts', plugins_url( '/js/more-posts.js' , __FILE__ ), array('jquery'), 1.0, true);
		wp_localize_script('more-posts', 'more_posts', array('ajaxurl' => admin_url('admin-ajax.php'))); //create ajaxurl global for front-end AJAX call;
	}
}

add_action('wp_ajax_more_posts', 'get_more_posts'); //fire get_more_posts on AJAX call for logged-in users;
add_action('wp_ajax_nopriv_more_posts', 'get_more_posts'); //fire get_more_posts on AJAX call for all other users;

function get_more_posts(){

	echo "Hello World";
	exit();

}

Plugin JS code:


jQuery(document).ready(function($){

	$.ajax({
		url: more_posts.ajaxurl,
		type: "GET",
		data: {
			action: 'more_posts'
		},
		dataType: "html",
		success: function(response){
			alert(response);
		}
	});
});

At this point I have a working plugin, which makes an AJAX call, and alerts the response (Hello World);

Ok, next step:
I have a function in my functions.php file, which wraps posts in the required mark up. Here it is:


function format_category_posts($query, $i = 1){
	$output = '';
	if( $query->have_posts() ) while( $query->have_posts() ) : the_post();

	$image = get_field('image');
	switch($i % 7){
		case 1:
			$output .= '<div class="row">';
			$class = 'third dark-box';
			break;

		case 2:
			$class = 'third light-box';
			break;

		case 3:
			$class = 'third dark-box';
			break;

		case 4:
			$output .= '</div><div class="row">';
			$class = 'two-thirds light-box';
			break;

		case 5:
			$class = 'third dark-box';
			break;

		case 6:
			$output .= '</div><div class="row">';
			$class = 'half light-box';
			break;

		case 0:
			$class = 'half dark-box';
			break;
	}

	$output .= '<div class="col ' . $class .'">' . "\\r\
";
	$output .= "\	<article>\\r\
";
	if($image){
		$output .= "\	\	" . '<div class="images">' ."\\r\
\	\	\	" . '<img src="' . $image['sizes']['large'] . '" alt="' . $image['alt'] . '" />' . "\\r\
\	\	</div>";
	}
	$output .= "\	\	" . '<div class="copy">' . "\\r\
";
	$output .= "\	\	\	<header>\\r\
\	\	\	\	" . '<h2>' . get_the_title() . '</h2>' . "\\r\
\	\	\	</header>\\r\
";
	$output .= "\	\	\	" . wpautop(get_the_excerpt()) . "\\r\
";
	$output .= "\	\	</div>\\r\
";
	$output .= "\	</article>\\r\
";
	$output .= "</div>";

	if($i == (int)$query->found_posts || $i % 7 == 0) $output .= '</div>';
	$i++; endwhile;

	return $output;
}

So I amend my get_more_posts function:


function get_more_posts(){

	$args = $_GET;
	unset($args['action']);

	$more_posts_query = new WP_Query($args);
	echo format_category_posts($more_posts_query);

	exit();

}

and the javascript:


jQuery(document).ready(function($){

	var offset = $('article').length;

	$.ajax({
		url: more_posts.ajaxurl,
		type: "GET",
		data: {
			action: 'more_posts',
			posts_per_page: 3,
			offset: offset
		},
		dataType: "html",
		success: function(response){
			alert(response);
		}
	});
});

So I’m expecting to get an alert box containing 3 posts wrapped in HTML tags, ready for insertion into the DOM. And I get… nothing.

If I check the Network tab on console, the AJAX call takes about a minute to respond, and sends around 70MB of data.
If I var_dump the WP_Object in get_more_posts, the AJAX will happily return an object with 3 posts in it.
If I var_dump(function_exists(‘format_category_posts’)), I get bool(true) as the response.

I use the format_category_posts on my category.php script, so I know the function works as expected.

I’m totally at a loss. Does anyone have any ideas?

Thanks in advance,
Mike

OK so I’ve found a solution, which I think is worth putting here incase anyone else finds themselves in the same situation.

So the problem was the loop. When the loop is called within a page it works fine because I guess there are other modules loaded at that stage. (Possibly in wp-head.php, or similar). So my function format_category_posts was running fine in category.php, but not in more-posts.php, since all the wp jazz wasn’t loaded into the script.

Instead of the standard while loop I used a foreach loop instead. Methods like get_the_title() do not work here since I’m not calling the the_post() method to set them up. Instead I just retrieve the data from the object:


function format_category_posts($query, $i = 1){
    $output = '';
    if( $query->have_posts() ) foreach( $query->posts as $post ) :

    $image = get_field('image');
    switch($i % 7){
        case 1:
            $output .= '<div class="row">';
            $class = 'third dark-box';
            break;

        case 2:
            $class = 'third light-box';
            break;

        case 3:
            $class = 'third dark-box';
            break;

        case 4:
            $output .= '</div><div class="row">';
            $class = 'two-thirds light-box';
            break;

        case 5:
            $class = 'third dark-box';
            break;

        case 6:
            $output .= '</div><div class="row">';
            $class = 'half light-box';
            break;

        case 0:
            $class = 'half dark-box';
            break;
    }

    $output .= '<div class="col ' . $class .'">' . "\\r\
";
    $output .= "\	<article>\\r\
";
    if($image){
        $output .= "\	\	" . '<div class="images">' ."\\r\
\	\	\	" . '<img src="' . $image['sizes']['large'] . '" alt="' . $image['alt'] . '" />' . "\\r\
\	\	</div>";
    }
    $output .= "\	\	" . '<div class="copy">' . "\\r\
";
    $output .= "\	\	\	<header>\\r\
\	\	\	\	" . '<h2>' . $post->post_title . '</h2>' . "\\r\
\	\	\	</header>\\r\
";
    $output .= "\	\	\	" . wpautop( $post->post_excerpt ) . "\\r\
";
    $output .= "\	\	</div>\\r\
";
    $output .= "\	</article>\\r\
";
    $output .= "</div>";

    if($i == (int)$query->count_posts || $i % 7 == 0) $output .= '</div>';
    $i++; endforeach;

    return $output;
}

Job done :slight_smile: