Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

Help paginating a custom query WordPress

  • SOLVED

I have a custom post display page like this:

http://www.activejunky.com/category/clothing-footwear/mens-clothing/mens-jackets

where posts (products) are displayed for a given category, in which case my custom pagination function works, but then when the brand name is set as a restriction, like here:

http://www.activejunky.com/category/clothing-footwear/mens-clothing/mens-jackets?brandName=The+North+Face&order=DESC

...then the pagination fails (though the product count shown above the grid is correct), and we see my set WP default of 39 posts.

My queries for the products are as follows:


<?php
$brandName= $_GET['brandName'];
$order= $_GET['order'];

if (isset($brandName)) {
} else {
$brandName = "ALL";
}

if (isset($order)) {
} else {
$order = "DESC";
}

global $wp_query;

if ($brandName=="ALL") {

$meta_key= "Retail_Price";
$orderby= "meta_value_num";
query_posts('numberposts=-1&paged='.$paged.'&meta_key='.$meta_key.'&orderby='.$orderby.'&order='.$order.'');
$total_results = $wp_query->found_posts;


} else {

$post_ids = array();
$meta_key = 'product_brand';
$unordered_posts = get_posts('paged='.$paged.'&numberposts=-1&meta_key='.$meta_key.'&meta_value='.$brandName.'');

foreach( $unordered_posts as $unordered_post ) {
$post_ids[]= $unordered_post->ID;
};

query_posts( array( 'post__in' => $post_ids , 'orderby' => 'meta_value_num' , 'order' => $order , 'meta_key' => 'Retail_Price' ) );

$total_results = $wp_query->found_posts;

}
?>


The function for the pagination Im using is:


<?php
function wp_pagenavi($before = '', $after = '', $prelabel = '', $nxtlabel = '', $pages_to_show = 20, $always_show = false)
{
global $request, $posts_per_page, $wpdb, $paged;
if (empty($prelabel)) {
$prelabel = '&laquo; previous page';
}
if (empty($nxtlabel)) {
$nxtlabel = 'next page &raquo;';
}
$half_pages_to_show = round($pages_to_show / 2);
if (!is_single()) {

if (!is_category()) { // brand search page matches this cond
preg_match('#FROM\s(.*)\sORDER BY#siU', $request, $matches);
} else {
preg_match('#FROM\s(.*)\sGROUP BY#siU', $request, $matches);
}
$fromwhere = $matches[1];
$numposts = $wpdb->get_var("SELECT COUNT(DISTINCT ID) FROM $fromwhere");
$max_page = ceil($numposts / $posts_per_page);
if (empty($paged)) { // brand search page matches this cond
$paged = 1;
}
if ($max_page > 1 || $always_show) {

//echo "$before <span id='howManyPages'>Pages ($max_page): </span>";
echo "$before ";
if ($paged >= ($pages_to_show - 1)) {
echo '<a href="' . get_pagenum_link() . '" class="firstPageLink">&laquo; first</a> ';
}

previous_posts_link($prelabel);

echo "<span id='howManyPages'>Pages ($max_page): </span>";

for ($i = $paged - $half_pages_to_show; $i <= $paged + $half_pages_to_show; $i++) {
if ($i >= 1 && $i <= $max_page) {
if ($i == $paged) {
echo "<strong class='on'>$i</strong>";
} else {
echo ' <a href="' . get_pagenum_link($i) . '">' . $i . '</a> ';
}
}
}
next_posts_link($nxtlabel, $max_page);
if (($paged + $half_pages_to_show) < ($max_page)) {
echo ' <a href="' . get_pagenum_link($max_page) . '" class="lastPageLink">last &raquo;</a>';
}
echo " $after";
}
}
}
?>


Im wondering if this function could be adapted to work in both cases on this type of page- I did some testing and it seems that once the brand restriction is applied, the var $numposts returns 1 instead of the true value of the total number of products.

Thanks in advance for any help!


Answers (4)

2010-07-30

wjm answers:

try setting at the beggining of the script

global $paged;


Adam Bundy comments:

The beginning of the pagination function?


wjm comments:

nope. right before.

$brandName= $_GET['brandName'];


Adam Bundy comments:

No dice- thanks though WJM.


wjm comments:

please, do at the end of the first script.
print_r($wp_query); and post the results to pastebin.com
that will give me a better idea of what is happening.


Adam Bundy comments:

http://pastebin.com/nj0DTi1E


wjm comments:

sorry, that is impossible to read in one line.
can you view the source code and copy it from there. (not directly from the web browser)


Adam Bundy comments:

Sorry, here is a revised paste:

http://pastebin.com/L22DgKxd


wjm comments:

you are missing the paged parameter in your second call.


change
query_posts( array( 'post__in' => $post_ids , 'orderby' => 'meta_value_num' , 'order' => $order , 'meta_key' => 'Retail_Price' ) )

for
query_posts( array( 'post__in' => $post_ids , 'orderby' => 'meta_value_num' , 'order' => $order , 'meta_key' => 'Retail_Price', 'paged'=> $paged) )


wjm comments:

^^^keeping the changes i have mentioned before.
that is,
global $paged


Adam Bundy comments:

WJM, when I make that change, I get a blank page: http://www.activejunky.com/category/clothing-footwear/mens-clothing/mens-jackets?brandName=The+North+Face&order=DESC


wjm comments:

I think i will need access to the server to test the changes, it's hard to spot what would be the problem over a forum.


Adam Bundy comments:

WJM re-added that paged declaration, and still blank page.


wjm comments:

that shoudlnt break things. you probably have a syntax error.
if i only had access to the server...


wjm comments:

just in case,
there needs to be a ";" after
global $paged

so the right code is
global $paged


wjm comments:

right code is
global $paged;


Adam Bundy comments:

Yes, global $paged; is what I have there. Here is my current query:


<?php
global $paged;

$brandName= $_GET['brandName'];
$order= $_GET['order'];

if (isset($brandName)) {
} else { // brand not set- set to ALL
$brandName = "ALL";
}

if (isset($order)) {
} else { // sort order not set- sort DESC
$order = "DESC";
}

global $wp_query;

if ($brandName=="ALL") {

//echo ('simple case');
$meta_key= "Retail_Price";
$orderby= "meta_value_num";
query_posts('paged='.$paged.'&cat='.$thiscategory.'&meta_key='.$meta_key.'&orderby='.$orderby.'&order='.$order.'');
$total_results = $wp_query->found_posts;


} else {

$post_ids = array();
$meta_key = 'product_brand';
$unordered_posts = get_posts('numberposts=-1&paged='.$paged.'&cat='.$thiscategory.'&meta_key='.$meta_key.'&meta_value='.$brandName);

foreach( $unordered_posts as $unordered_post ) {
$post_ids[]= $unordered_post->ID;
};

query_posts( array( 'post__in' => $post_ids , 'orderby' => 'meta_value_num' , 'order' => $order , 'paged'=> $paged , 'meta_key' => 'Retail_Price' ) )
$total_results = $wp_query->found_posts;

}
?>


wjm comments:

as i said, it was a syntax error
you are missing a ";"
at the end of

query_posts( array( 'post__in' => $post_ids , 'orderby' => 'meta_value_num' , 'order' => $order , 'paged'=> $paged , 'meta_key' => 'Retail_Price' ) )


your code should be
query_posts( array( 'post__in' => $post_ids , 'orderby' => 'meta_value_num' , 'order' => $order , 'paged'=> $paged , 'meta_key' => 'Retail_Price' ) );


wjm comments:

let me give you an advice:
you shouldnt be developing this with no error reporting.
you will spend endless hours trying to figure out whay may be wrong.


Adam Bundy comments:

Ahh- fixed that, which now displays the page, but the result is the same- no pagination appearing, 39 posts shown out of 128. Doesnt that mean that it IS paginating, but the pagination links function just isnt getting whatever it needs to detect that it should show nav?


wjm comments:

if you are passing the "paged" parameter to your query post,
that's all wp_navi needs to display the pagination bar.

Sorry, but that's all the support i can give you so far with no access to the source files.


Adam Bundy comments:

WJM, sorry- Im a designer by trade, a PHP n00b, and just trying to get a few bugs ironed out. I dont actually know how to turn on error reporting.


wjm comments:

simply remove (or comment out) any
error_reporting(0);
or

@ini_set(‘display_errors’, 0);


wjm comments:

Fixed!
let me know if it works as expected.

I installed the wp-pagenavi plugin, removed your pagenavi function from your functions.php file
fixed your functions.php file (you had a whole bunch of blank lines).

disabled caching for testing.
let me know if all works as expected

2010-07-30

Pradipta Sinha answers:

Try to use 2 lines of code in front of your product page.

global $query_string;
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

and try to use showposts instead of numberposts .

query_posts('showposts=-1&paged='.$paged.'&meta_key='.$meta_key.'&orderby='.$orderby.'&order='.$order.'');

I hope it will work.


Adam Bundy comments:

Thanks Pradipta, but that doesnt seem to have done anything- still get 128 results, but only 39 shown and no pagination.

2010-07-30

Lew Ayotte answers:

Your paging is actually working (as it stand right now at 2:26PM EST)

See this URL:
http://www.activejunky.com/category/clothing-footwear/mens-clothing/mens-jackets/page/2?brandName=The+North+Face&order=DESC

notice the /page/2 in the URL

It appears that your missing the actual <previous next> links

How are you calling them in your theme template file?


Adam Bundy comments:

Yes, Lew- you're right- what I need I think is an adjusted version of the pagenav script from the functions file (posted above) that will correctly display the page nav, like it is seen here:

http://www.activejunky.com/category/clothing-footwear/mens-clothing/mens-jackets/page/2


Lew Ayotte comments:

Are you calling the wp_pagenavi function in this template?

Try adding this to your template file (the default wordpress page navigation)

<div class="navigation">
<div class="alignleft"><?php previous_posts_link('&laquo; Previous') ?></div>
<div class="alignright"><?php next_posts_link('More &raquo;') ?></div>
</div>


Lew

2010-07-30

Mike Schinkel answers:

Hi Adam:

Unfortunately I don't have appropriate test data for this so I can't give you tested code but I think I can get you to approach the problem differently with the result being that your problem will disappear, at least I assume it will.

If you look at your code for the brand-specific branch of the if..else you'll see you are calling two queries: first get_posts() and then query_posts(). However you use the paged parameter on your first query and never use it on the second query. I believe that is the crux of your problem.

Obviously the reason you've used two queries is because there is no support of multiple meta keys in what I'll call the "WordPress Query Language." So you ran one query to collect up all the IDs and passed the paging variable to it and then called the second query without the "paged" parameter and that is the query that runs your page results. You could potentially move the "paged" parameter from get_posts() to query_posts() and it may solve your problem. However, for a large data set using two queries is a really inefficient way to do things anyway (but understandable it was the only obvious way to do it, and kudos to you for knowing how to make that work, most people wouldn't have a clue.)

Let's tackle this with another approach? Let's add a "posts_where" hook that lets you use the following single query without needing an if..else; just change the query:

query_posts(array(
'post_type' => 'post',
'meta_key' => 'Retail_Price',
'orderby' => 'meta_value_num',
'meta[product_brand]' => (isset($_GET['brandName']) ? $_GET['brandName'] : ''),
'posts_per_page' => -1,
'order' => (!isset($_GET['order']) ? 'ASC' : strtolower($_GET['order'])=='asc' ? 'asc' : 'desc')),
));


Here's the filter hook:

add_filter('posts_where','multiple_meta_criteria_posts_where',10,2);
function multiple_meta_criteria_posts_where($where,$wp_query) {
if (isset($wp_query->query)) {
$query = $wp_query->query;
if (is_string($query))
parse_str($query,$query);
if (is_array($query)) {
global $wpdb;
foreach($query as $param_key => $param_value) {
if ($param_key=='meta') {
foreach($param_value as $key => $value) {
if (!empty($value)) {
$sql = " AND {$wpdb->posts}.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key=%s AND meta_value=%s) ";
$where .= $wpdb->prepare($sql,$key,$value);
}
}
}
}
}
}
return $where;
}


I've created a standalone example you can drop into the root of your website to play with this. You can find it here: [[LINK href="http://gist.github.com/501033"]]http://gist.github.com/501033[[/LINK]]

Let me know if this helps.

<strong>UPDATE:</strong> I see someone else answered and you had a different problem. I'd still recommend you look at using the filter hook instead of multiple queries. FWIW, I'm not concerned about the $20; if you would have given me some just give it to others.