Ask your WordPress questions! Pay money and get answers fast! (more info)

Reusable ajax script/tutorial to load more posts with get_posts() WordPress

  • SOLVED

I have a theme with several custom post types. And I use many custom queries as well (using new WP_Queries, with pre_get_posts method or with get_posts - I don't use query_posts).

In some parts of the theme - in certain pages, archives - I want to display posts from different post types, therefore separate queries are made (most of the time I use get_posts for this purpose so we can assume get_posts will be always used if this makes things simpler).

Some of these queries consist of a list of posts to display featured content filed under a <strong>custom taxonomy or custom post type (or combination of both, eventually using sorting parameters or filtering taxonomy terms)</strong>.

I would like to have such queries <em>ajaxed</em>, meaning that I can set an initial number of posts to show, then the user can query more by performing an action (click a "load more" element or when a certain event is met - for example some custom queries load posts into carousels or sliders, for these queries I could want more posts to automatically load when the carousel has met one end - the carousel and sliders I use are predisposed to log this event to hook into).

What I'm looking for is a reusable, optimized code to trigger the ajax query - also considering I might have <strong>more than one get_posts query</strong> in the same page, for different posts/post types with different parameters.

I have tried to look around for a template, but haven't found any. The best I was able to get were tutorials on how to ajax the standard wordpress main query, which I was not interested in.

I would love to have an ajax/php snippet that can be used more than once in the same page - I need to be able to specify how many more posts to load time and basically access to all parameters used by get_posts() to load specific posts (filter post types, taxonomies, terms) in a specific order.

I've set 5 USD for this answer if you can point me to a good tutorial which can solve my problem using get_posts (please don't link generic tutorials on ajax or tutorial only explaining how to achieve an ajax query on the main query).

<em>However, if you're able to write for me a template which I can reuse for the purpose described, I'd naturally pay you more since it's code work.</em>

Thank you

ps - I've increased the prize to 30 USD after being told that someone would be able to produce a solution, it's fine to me and I don't mind sharing the answer with others in fact

Answers (2)

2013-01-11

jazbek answers:

Hi Gasllight, please try the following code:

First, place the following php code in your functions.php:


add_action('wp_ajax_ajax_custom_load_posts', 'ajax_custom_load_posts');
add_action('wp_ajax_nopriv_ajax_custom_load_posts', 'ajax_custom_load_posts');
function ajax_custom_load_posts()
{
if (check_ajax_referer(__FUNCTION__,'nonce', false))
{
$query = $_POST['query'] ? $_POST['query'] : array();
$query['post_status'] = 'publish'; // otherwise wp thinks we're in the admin and shows all post statuses
$posts = get_posts($query);
foreach ($posts as $post) : setup_postdata($post);
get_template_part('/partials/loop', $post->post_type);
endforeach;
echo '<div id="nonce" style="display: none">'.wp_create_nonce(__FUNCTION__).'</div>';
}
else
{
die('Invalid request.');
}

die();
}




Then, here is some code to use in your page or post templates:

<?php

/*
How to use this script:
1) Create your query arguments array in php, and make sure you assign the 'paged' arg and a 'nonce' (see my example).
2) Run get_posts() and output them as you see fit using a loop.
FOR BEST RESULTS: use a template part in your loop (in the example below, I assume the template part filename is loop-[posttype].php,
where [posttype] should be your custom post type).
This assures that your posts are always output the same way.
2) Output the php query arguments to a GLOBAL (i.e. don't place "var" before it) javascript variable, using json_encode($args)
3) Attach an event handler to your click event, or other type of event.
In your event handler, run the load_posts_ajax() function. Parameters are described below.
I have provided examples for both a click event on a link, as well as a custom event.
*/

?><!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title>Ajax load custom queries example</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';

// This is the function that sends the query to php. It accepts 2 parameters:
// query: a string that contains the name of the javascript variable that contains the query you want to send.
// note, please pass the name of the variable, not the variable itself
// target: the selector for the element to receive the new posts
function load_posts_ajax(query, target){
query = window[query];
$.post(ajaxurl, {
action: 'ajax_custom_load_posts',
nonce: query.nonce,
query: $.extend({}, query, {paged: query.paged+1})
}, function(result){
// I assume you want to append the new posts. If you want to replace the old posts with the new posts,
// use $(target).html(result); instead
$(target).html(result);

// increase the page number for the next time the query is sent
query.paged++;

// replace the nonce with the new one, since it's already been used
query.nonce = $(result).filter('#nonce').text();
})
}


// this function handles all the links with the "load-more" class,
// so you could create multiple .load-more links for different queries.
// each link should have the data-query and data-target elements set (see example #1 below)
$(document).ready(function(){
$('a.load-more').on({
click: function(){
var $this = $(this);

// send the request to load_posts_ajax(), with the name of the query variable and the name of the target container
load_posts_ajax($this.data('query'), $this.data('target'));
}
})
})
</script>
</head>
<body>

<!--
Example #1 -- you might need to change the markup as you see fit
-->

<?php
// here is the query for your scroller.
// note I have added the paged variable and a nonce argument
$scroller_query = array(
'numberposts' => $numberposts,
'post_type' => $post_type,
'orderby' => $order,
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => $term->slug
)
),
'paged' => 1,
'nonce' => wp_create_nonce('ajax_custom_load_posts'),
);
$scroller_posts = get_posts( $scroller_query );

?>

<ul id="scroller">
<?php foreach ($scroller_posts as $post) : setup_postdata($post); ?>
<?php get_template_part('/partials/loop', $post->post_type) ?>
<?php endforeach; ?>
</ul>
<!--
below is how you would create the load-more link.
note I have assigned the name of the javascript query variable to the data-query attribute (scrollerQuery, assigned above)
and the selector of the ul target to the data-target attribute (#scroller):
-->
<a class="load-more" href="javascript:void(0)" data-query="scrollerQuery" data-target="#scroller">Load more &raquo;</a>


<script type="text/javascript">
// assign the query to a global javascript variable from php, using json_encode():
scrollerQuery = <?php echo json_encode($scroller_query); ?>;
</script>


<!--
Example #2 -- you might need to change the markup as you see fit
-->

<?php

// here is the query for your places carousel
// note i have added the paged variable and a nonce argument
$places_query = array(
'post_type' => array( 'heritage', 'nature' ),
'posts_per_page' => 16,
'status' => 'published',
'orderby' => 'rand',
'paged' => 1,
'nonce' => wp_create_nonce('ajax_custom_load_posts'),
);
$places_posts = get_posts( $places_query );

?>

<ul id="places">
<?php foreach ($places_posts as $post) : setup_postdata($post); ?>
<?php get_template_part('/partials/loop', $post->post_type) ?>
<?php endforeach; ?>
</ul>
<script type="text/javascript">
// output the $places_query into a javascript variable
placesQuery = <?php echo json_encode($places_query); ?>;

// I assumed the custom event would be an event that's triggered on the <ul> that encloses the carousel,
// but I didn't look up the documentation for your carousel script. Please edit as you see fit:

$('#places').on({
someCustomEvent: function(){
// send the request to load_posts_ajax(), with the name of the query variable and the name of the target container
load_posts_ajax('placesQuery', '#places');
}
})
</script>

</body>
</html>


Gaslight comments:

ok jazbek, many thanks

I'm trying to make the code work, seems very clean

I have currently a problem with your ajax function

`query = $_POST['query'] ?: array();`

it returns

`Parse error: syntax error, unexpected ':' `



jazbek comments:

Ah, you must be using an older version of php... it's an easy fix. Please try:

$query = $_POST['query'] ? $_POST['query'] : array();

I have updated my answer.


jazbek comments:

Edit #2 -- I just removed some css files from the <head> that I had been using locally so things looked nice while I was testing. Please put your own css files in the <head>. :)
Jessica


Gaslight comments:

ok thanks
I have another error at the console this time:

Uncaught TypeError: Cannot call method 'extend' of undefined
load_posts_ajax
$.on.click
v.event.dispatch
o.handle.u

I guess the console means this:

query: $.extend({}, query, {paged: query.paged+1})

I'm trying to work out with the link method first since for the scrollers I need to look up in the documentation


jazbek comments:

Do you have jquery included in your page? I have the <script> tag for it in my code, it should be in the head of your page:


<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>


Gaslight comments:

i enqueue scripts with wp_enqueue_script - yes it's enqueued in the head, before your ajax js - which I inserted manually just before </head> closing tag

2013-01-11

Arnav Joy answers:

i want to look your site where you have no. of custom post types calls