Forums

how to page custom database query (to sort by custom field)? (22 posts)

  1. ericr23
    Member
    Posted 4 years ago #

    I have a Page template with the following query to list all posts ordered by the "Author" custom field (i.e., author of the document posted, not the author of the post).

    <?php
    $pageposts = $wpdb->get_results("SELECT wposts.* FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
    WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = 'Author' AND
    wposts.post_status = 'publish' ORDER BY wpostmeta.meta_value ASC", OBJECT);
    if ($pageposts):
    foreach ($pageposts as $post):
    setup_postdata($post); ?>
    
    ... etc.

    How can I paginate this instead of listing all 360+ posts at once?

  2. Otto
    Tech Ninja
    Posted 4 years ago #

    You need to add a LIMIT to the SQL and increment that with an offset that you pass between pages.

    Say $page=1,2,3, etc...
    $posts_per_page = 10;
    $offset = ($page-1) * $posts_per_page
    Now do your sql but add "LIMIT $offset, $posts_per_page" to the query.

  3. ericr23
    Member
    Posted 4 years ago #

    Thanks for the guidance, Otto.

    It's what I thought might be required. I'll tackle it when I have more time and report back with any success.

    One question right away, though: How to pass the page information? I.e., how to tell each query which page number it's on?

  4. ericr23
    Member
    Posted 4 years ago #

    Another challenge I see is that I'd want to get the number of posts so I know when I'm on the last page, but that means a query has to be done once to get the number of posts and then again with the offset and posts_per_page. So it would seem a better choice to offset and limit the While loop perhaps (which I'm not sure how to do: can each $post be called by $pageposts[n]?)?

  5. ericr23
    Member
    Posted 4 years ago #

    This is working:

    <?php
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    $postsperpage = 30;
    $pageposts = $wpdb->get_results("SELECT wposts.*
    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
    WHERE wposts.ID = wpostmeta.post_id
    AND wpostmeta.meta_key = 'Author'
    AND wposts.post_status = 'publish'
    ORDER BY wpostmeta.meta_value ASC", OBJECT);
    
    $numposts = count($pageposts);
    $numpages = $numposts/$postsperpage;
    if ($numposts%$postsperpage) $numpages=$numpages+1; ?>
    
        (navigation stuff)
    
    <?php
    if ($pageposts):
    $i=$postsperpage*($paged-1);
    while ($i<$postsperpage*$paged):
    $post=$pageposts[$i];
    setup_postdata($post); ?>
    
        (loop stuff)
    
    <?php
    $i++;
    endwhile; endif; ?>

    http://www.wind-watch.org/documents/authors/

  6. ericr23
    Member
    Posted 4 years ago #

    I've run into a problem farther down the page, now.

    The following is an attempt to create prev/next navigation right after the end of the loop above.

    <?php
    $precpage=$paged-1;
    $nextpage=$paged+1;
    if ($paged>1) {
    if ($precpage==1)
    	{ echo ('<a href="../../">« Home</a>'); }
    	else
    	{ echo ('<a href="../' . $precpage . '/">« Preceding Page</a> • <a href="../../">Home</a>'); }
    }
    if ($paged<$numpages)
    	{ if ($paged==1)
    		{ echo (' • <a href="./page/'); }
    		else
    		{ echo (' • <a href="../'); }
    	echo ($nextpage . '/">Next Page »</a>'); }
    ?>

    The "home" link is provided only if it's not page 1, and the "preceding" link only if it's page 3 or more. The "next" link is provided as long as the current page is less than the total number of pages (as determined earlier).

    That "next" link isn't working, though, because the value for $numpages has apparently changed to 1. (If I remove the "if($paged<$numpages)" condition, the link is created -- but the last page would then point to a nonexisting page.)

    What happened to $numpages?

  7. Otto
    Tech Ninja
    Posted 4 years ago #

    What happened to $numpages?

    Are you doing this in the theme itself, or are you using some kind of plugin like ExecPHP or something? Different files in the theme?

    Different bits of code can have different scope, I need to know where you are placing this code to explain the scope to you properly.

    Also, your main code is an incredibly bad idea. You're basically pulling every single post from the database, then only displaying a small fraction of them. You really, really should use a LIMIT in your selection instead. It will be massively faster, and your code will be much simpler.

    Example:

    <?php
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    $postsperpage = 30;
    $offset = ($paged-1) * $postsperpage
    $pageposts = $wpdb->get_results("SELECT wposts.*
    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
    WHERE ... blah ...
    LIMIT $offset, $posts_per_page ", OBJECT);
    ...
    foreach ($posts as $post) :
    setup_postdata($post); ?>
        (loop stuff)
    <?php endforeach; ?>
  8. ericr23
    Member
    Posted 4 years ago #

    Thanks for the feedback.

    All of the code I've shown is in the Page template. Other variables, but not $numpages, seem to be usable throughout the template script (in fact, for now, I'm just recalculating $numpages at the bottom of the page from those other variables).

    The question I had about using LIMIT in the query is knowing when I'm on the last page, which would usually be when the number of posts is less than the $postsperpage -- but what if it's equal to that number but it's the last page? So I'd still want to do a query to find out how many posts there are in total. And I'd want to be able to know how many pages there are in total so I can have a navigation link for each page on all of the pages.

    I'll re-do this with an initial query to count the number of posts with "Author" metadata, which I guess would be much faster each time than getting all of the posts.

    (By the way, this is not a blog with tens of thousands of posts, but more a CMS-type reference library -- after more than 2 years, there are only just over 360 posts.)

  9. ericr23
    Member
    Posted 4 years ago #

    I'm having trouble getting the result of this query via the $wpdb query:

    SELECT COUNT(*)
    FROM wp_postmeta, wp_posts
    WHERE wp_postmeta.meta_key = 'Author'
    AND wp_posts.ID = wp_postmeta.post_id
    AND wp_posts.post_status = 'publish'

    That works as expected in MySQL (returning the count of published posts with the "Author" custom field), but the following returns the string "Array" in the Page template:

    $numpostss = $wpdb->get_results("SELECT COUNT(*)
    FROM $wpdb->postmeta wpostmeta, $wpdb->posts wposts
    WHERE wpostmeta.meta_key = 'Author'
    AND wposts.ID = wpostmeta.post_id
    AND wposts.post_status = 'publish'", ARRAY_N);
    echo $numposts = $numpostss[0];
  10. ericr23
    Member
    Posted 4 years ago #

    Never mind: It needed to go one down one more level:

    $numposts = $numpostss[0][0];

  11. ericr23
    Member
    Posted 4 years ago #

    OK -- here's the new code in the custom Page template:

    <?php
    $numpostss = $wpdb->get_results("SELECT COUNT(*)
    FROM $wpdb->postmeta wpostmeta, $wpdb->posts wposts
    WHERE wpostmeta.meta_key = 'Author'
    AND wposts.ID = wpostmeta.post_id
    AND wposts.post_status = 'publish'", ARRAY_N);
    
    $numposts = $numpostss[0][0];
    $postsperpage = 30;
    $numpages = intval($numposts/$postsperpage);
    if ($numposts%$postsperpage) { $numpages=$numpages+1; };
    $lastpageposts = $numposts-(($numpages-1)*$postsperpage);
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    
       ... navigation stuff ...
    
    $offset = ($paged-1) * $postsperpage;
    if ($paged==$numpages) { $postsperpage=$lastpageposts; }
    
    $posts = $wpdb->get_results("SELECT wposts.*
    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
    WHERE wposts.ID = wpostmeta.post_id
    AND wpostmeta.meta_key = 'Author' AND
    wposts.post_status = 'publish'
    ORDER BY wpostmeta.meta_value ASC
    LIMIT $offset, $postsperpage", OBJECT);
    
    foreach ($posts as $post) :
    setup_postdata($post); ?>
    
       ... loop stuff ...
    
    endforeach;
    ?>
    
       ... navigation stuff ...

    I still have to recreate $numpages (but not any of the other variables) for that second navigation section.

    Thanks again for the help.

  12. ericr23
    Member
    Posted 4 years ago #

    It turns out that $numpages is a WordPress-defined variable. I renamed my variable to $numofpages and it all works as expected.

  13. Otto
    Tech Ninja
    Posted 4 years ago #

    Hah. Yes, I didn't notice that one.

    WordPress uses a lot of global variables like that. I highly recommend always prefixing your own variables. Instead of $numpages, something like $otto_numpages and $otto_posts and things like that.

    Plugins should do this too, in general.

  14. ericr23
    Member
    Posted 4 years ago #

    Good advice!

  15. ubilla
    Member
    Posted 4 years ago #

    Please Ericr23, copy all code for view the example.

    Tks! :)

  16. ericr23
    Member
    Posted 4 years ago #

    What code other than what is posted above (remembering to change $numposts to $numofposts or, better, to $xx_numposts) do you want to see?

  17. Ellision
    Member
    Posted 4 years ago #

    <?php
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    $postsperpage = 30;
    $offset = ($paged-1) * $postsperpage
    $pageposts = $wpdb->get_results("SELECT wposts.*
    FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
    WHERE ... blah ...
    LIMIT $offset, $posts_per_page ", OBJECT);
    ...
    foreach ($posts as $post) :
    setup_postdata($post); ?>
        (loop stuff)
    <?php endforeach; ?>

    Yeah, this works very well!

  18. hotkee
    Member
    Posted 3 years ago #

    I have been trying to get the code above to display the navigation links but I cant get it to work.

    I have done exactly as described in the code listed by ericr23 and my "navigation stuff" contains the code

    previous_post('&laquo; %', ''); next_post('% &raquo;', '');

    But the links are not showing. I have wordpress 2.5.1, earlier versions exhibited the same issue, custom query works but navigation doesnt appear.

    Would appreciate some input from people, particularly like to see the navigation code being used by ericr23.

  19. Georgi
    Member
    Posted 3 years ago #

    Hi,

    The initial question was good. I had the same problem - have page with custom query
    $pageposts = $wpdb->get_results("select ...", OBJECT);
    if ($pageposts):
    foreach ($pageposts as $post):
    setup_postdata($post); ?>)
    ...

    with many post as a result.

    The problem is that navigation functions next_posts_link and previous_posts_link don't work here. Of course, it works if one writes his own navigation functions, but I prefer to use WP navigation way. So, the solution is to 'put' this query in the loop ant to use wordpress functionality.

    Just change the search form(searchform.php) and add the following line <input type="hidden" name="key" value="<what you want>" /> - just before <input type="submit" id="searchsubmit" value="Search" />
    Let say you have
    <input type="hidden" name="key" value="pretty" />
    Thus the url by searching something(f.i. word 'dog') would be
    http://...../?s=dog&key=pretty

    Then get the plugin Search Custom Fields and modify it a bit. The functions in the plugin get
    the search word (dog) and the value of the key(pretty).
    Add one if() to szub_search_custom_join and szub_search_custom_where functions - if the value of the key is 'pretty', change the functions szub_search_custom_join and szub_search_custom_where in such way, like normal search is performed - only the word 'dog' will be searched without any additional JOIN and WHERE
    f.i.
    function szub_search_custom_where($where) {
    ...
    ...
    if ($_GET['key'] == 'pretty') {
    $where = " (1=1) AND $status ";
    }
    }
    return $where;

    But if key has other value - f.i. 'myname', then set variable $join in szub_search_custom_join and $where in szub_search_custom_where so, like your custom query will be executed (you can use also search word here - dog in this example).

    So, if you use http://...../?s=dog&key=myname - then the output will be your custom query. And this will come from the loop - so navigation is ok and so on.

    Hope this help.

    Regards.

  20. hotkee
    Member
    Posted 3 years ago #

    Hi Georgi
    Thanks for the reply.
    Interesting approach but not convenient for my needs.

    I have just managed to find a work around using the pagebar navigation (which I was already using) http://www.elektroelch.de/hacks/wp/pagebar/

    I amended this plugin slightly and now with custom query page, I can navigate by using the above plugin as per all my non custom pages.

    So problem sorted for me :-)

  21. h4x3d
    Member
    Posted 3 years ago #

    great post, thanks for contributing everyone!

  22. ericr23
    Member
    Posted 3 years ago #

    Sorry, I haven't checked in for a while.

    The bottom-of-page navigation I used is posted above, but one might miss the later note that $numpages needs to be renamed. So here is the final:

    <?php
    $precpage=$paged-1;
    $nextpage=$paged+1;
    if ($paged>1) {
    if ($precpage==1)
    	{ echo ('<a href="../../">« Home</a>'); }
    	else
    	{ echo ('<a href="../' . $precpage . '/">« Preceding Page</a> • <a href="../../">Home</a>'); }
    }
    if ($nextpage<=$numofpages) {
    	if ($paged==1)
    		{ echo ('<a href="./page/'); }
    		else
    		{ echo (' • <a href="../'); }
    	echo ($nextpage . '/">Next Page »</a>');
    	}
    ?>

    And here is a different navigation scheme for the top of the page:

    <?php
    echo ('<b>[</b> &nbsp;' . $numposts . ' documents &nbsp;•&nbsp; <i>pages:&nbsp;</i> ');
    $i=1; while ($i<=$numofpages) {
    if ($i==$paged) {
    echo (' <b>' . $i . '</b>'); }
    else {
    	if ($i==1)
    	{ echo (' <a href="../../">' . $i . '</a>'); }
    	else
    	{ if ($paged==1)
    	{ echo (' <a href="./page/' . $i . '/">' . $i . '</a>'); }
    	else
    	{ echo (' <a href="../' . $i . '/">' . $i . '</a>'); } } }
    $i++;
    }
    echo ('&nbsp; <b>]</b>'); ?>

    Again, see them at work at http://www.wind-watch.org/documents/authors/

Topic Closed

This topic has been closed to new replies.

About this Topic

Tags

No tags yet.