How To List All Posts Of An Archive, A Category Or A Search Result

Archive pages are usually paged according to your settings in options/reading. Sometimes you may want to offer a page with all posts for an archive (time, category, search result).

You need:

  • a separate address for the unpaged archive,
  • a filter for the internal WordPress query and
  • a link to your ‘all posts’ page.

We put everything into a class to avoid name collisions and to keep the global namespace clean.
We name the file class.View_All_Posts.php.

Let’s start with the class, the parameter and a checker, this is easy:

class View_All_Posts
{
    /**
     * GET parameter to trigger a complete, not paged archive.
     * @var string
     */
    protected $all_param = 'all';

    /**
     * Are we there already?
     *
     * @return bool
     */
    public function is_all_posts()
    {
        return isset ( $_GET[$this->all_param] );
    }
}

For the address I use a very simple approach: a GET parameter named all. You may change the name here; just stay with ASCII chars from a—z. все_сообщения will get you in trouble!

Next we need a constructor that manages our work:

    public function __construct()
    {
        /* Register the query argument. */
        add_filter('query_vars', array ( $this, 'add_query_arg') );

        /* Hook into the query. */
        add_action('pre_get_posts', array ( $this, 'view_all_posts') );
    }

The constructor references two internal functions – add_query_arg() and view_all_posts(), which we build next:

    /**
     * Registers the query arg in WordPress.
     * Otherwise it will be unset.
     *
     * @param  array $vars Already registered query args.
     * @return array
     */
    public function add_query_arg( array $vars )
    {
        return array_merge( $vars, array ( $this->query_arg ) );
    }

    /**
     * Alters the query to remove the paging.
     * @return void
     */
    public function view_all_posts()
    {
        if ( ! $this->is_all_posts() )
        {
            return;
        }

        $GLOBALS['wp_query']->query_vars['nopaging'] = TRUE;

        return;
    }

The first function just registers our GET parameter in WordPress. The second alters the query to the database and removes the paging.

We are almost done. A template tag for the link would be nice, wouldn’t it?

    /**
     * Creates the markup for the link.
     *
     * Usage in archive.php, category.php or search.php:
     * $GLOBALS['view_all_posts']->get_allposts_link();
     *
     * @param  string $text Linktext
     * @param  bool   $print echo or return
     * @return string|void
     */
    public function get_allposts_link(
        $text   = 'View all posts'
    ,   $before = '<p class="allpostslink">'
    ,   $after  = '</p>;'
    ,   $print  = TRUE
    )
    {
        if ( $this->is_all_posts()
         or $GLOBALS['wp_query']->found_posts <= get_option('posts_per_page')
        )
        {   // No link needed.
            return;
        }

        if ( isset ( $_SERVER['QUERY_STRING'] )
            && ! empty ( $_SERVER['QUERY_STRING'] )
        )
        {
            /* We have already visible GET parameters: /?hello=world. */
            $new_url = $_SERVER['REQUEST_URI'] . '&';
        }
        else
        {
            /* Note the difference: REQUEST_URL doesn't include
             * the query string while REQUEST_URI does. */
            $new_url = $_SERVER['REQUEST_URL'] . '?';
        }

        $link = "$before<a href='$new_url$this->all_param'>$text</a>$after";

        if ( $print )
        {
            print $link;
            return;
        }
        return $link;
    }

Note: $GLOBALS['wp_query']->found_posts holds the sum of all posts for a given query, not just for the current page. Useful if you want to print out the total number on a paged archive.

If you want to avoid duplicate content, hide the full archives from search engines in your header:

    /**
     * Prevents indexing from search engines.
     *
     * Add this as an action to 'wp_head'.
     *
     * @return void
     */
    public function meta_noindex()
    {
        if ( $this->is_all_posts() )
        {
             print '<meta name="robots" content="noindex">';
        }
    }

Our class is complete. Now we put an object of the class into the global namespace …

$GLOBALS['view_all_posts'] = new View_All_Posts;

… add an action to wp_head

add_action(
    'wp_head'
,   array ( $GLOBALS['view_all_posts'], 'meta_noindex' )
);

… and include the file into the functions.php of our theme:

require_once dirname(__FILE__) . DIRECTORY_SEPARATOR
    . 'class.View_All_Posts.php';

In our archive templates (archive.php, category.php, search.php) we print the link:

$GLOBALS['view_all_posts']->get_allposts_link();

Done.

Oh, wait … maybe you want to see the full code? And a download link?

Here’s the link: Download class.View_All_Posts.php

The complete code:

/**
 * Adds a view all posts page to any query.
 * @author Thomas Scholz http://toscho.de
 * @version 1.1
 */
class View_All_Posts
{
    /**
     * GET parameter to trigger a complete, not paged archive.
     * @var string
     */
    protected $all_param = 'all';

    public function __construct()
    {
        /* Register the query argument. */
        add_filter('query_vars', array ( $this, 'add_query_arg') );

        /* Hook into the query. */
        add_action('pre_get_posts', array ( $this, 'view_all_posts') );
    }

    /**
     * Registers the query arg in WordPress.
     * Otherwise it will be unset.
     *
     * @param  array $vars Already registered query args.
     * @return array
     */
    public function add_query_arg( array $vars )
    {
        return array_merge( $vars, array ( $this->query_arg ) );
    }

    /**
     * Alters the query to remove the paging.
     * @return void
     */
    public function view_all_posts()
    {
        if ( ! $this->is_all_posts() )
        {
            return;
        }

        $GLOBALS['wp_query']->query_vars['nopaging'] = TRUE;

        return;
    }

    /**
     * Are we there already?
     *
     * @return bool
     */
    public function is_all_posts()
    {
        return isset ( $_GET[$this->all_param] );
    }

    /**
     * Creates the markup for the link.
     *
     * Usage in archive.php, category.php or search.php:
     * $GLOBALS['view_all_posts']->get_allposts_link();
     *
     * @param  string $text Linktext
     * @param  bool   $print echo or return
     * @return string|void
     */
    public function get_allposts_link(
        $text   = 'View all posts'
    ,   $before = '<p class="allpostslink">'
    ,   $after  = '</p>'
    ,   $print  = TRUE
    )
    {
        if ( $this->is_all_posts()
        or $GLOBALS['wp_query']->found_posts <= get_option('posts_per_page')
        )
        {   // No link needed.
            return;
        }

        if ( isset ( $_SERVER['QUERY_STRING'] )
            && ! empty ( $_SERVER['QUERY_STRING'] )
        )
        {
            /* We have already visible GET parameters: /?hello=world. */
            $new_url = $_SERVER['REQUEST_URI'] . '&';
        }
        else
        {
            /* Note the difference: REQUEST_URL doesn't include
             * the query string while REQUEST_URI does. */
            $new_url = $_SERVER['REQUEST_URL'] . '?';
        }

         $link = "$before<a href='$new_url$this->all_param'>$text</a>$after";

        if ( $print )
        {
            print $link;
            return;
        }
        return $link;
    }
}

$GLOBALS['view_all_posts'] = new View_All_Posts;

Mission completed. Any suggestions?

Guest Post

Thomas ScholzThis post is written by Thomas Scholz toscho.de, a good friend of us and a web designer from Halle, Germany.

Thank you very much from our part to Thomas.

Comments are closed.

7 comments

  1. Milemann

    Hi Thomas!

    looks realy good but ... it dosen't show anything on my test blogs...
    i've try it on wp3.0 and on wp2.9.2

    btw ...
    wp3.0 does not show posts with post_status=future even when i try to show it with:
    ...
    $ main_query = new WP_Query ($ query_string. 'Category_name = Concerts & post_status = publish, future & order = ASC');
    ...

  2. Thomas Scholz

    @Milemann Where don’t you see anything? On the result page “/?all”?

    Do you get useful errors, when you turn error reporting on your wp-config.php?

    define('WP_DEBUG', true);

  3. Milemann

    i've put the class file into the template folder,
    into funtions.php -
    require_once dirname(__FILE__) . DIRECTORY_SEPARATOR
    . 'class.View_All_Posts.php';
    into archive.php -
    "$GLOBALS['view_all_posts']->get_allposts_link();"

    does it mean that in this case all postlinks should appear on each archiv site?

    i try to invoke the site with
    http://192.168.178.25/bbgwp/?m=201006&paged=all
    even with
    http://192.168.178.25/bbgwp/?m=201006

    i dont see the any postlinks either any errors??

  4. Thomas Scholz

    @Milemann The link will be displayed only if you have more posts in the archive than the normal page shows. This is checked by:

    $GLOBALS['wp_query']->found_posts <= get_option('posts_per_page')

    To force the link set the post per page option to a very low value.

  5. Milemann

    ok!
    i've got it.
    I misunderstood the approach of your class ...

    my other problem after update to 3.0
    i have the query in the archive.php like:
    $main_query = new WP_Query($query_string.'&post_status=publish,future&order=ASC');
    to show planned events.

    i have a lot of future posts, when i want to get month archives with future date:
    http://192.168.178.25/bbgwp/?m=201009
    wo3.0 redirected to 404
    (in 2.9.2 it works pretty well)

    do you have an idea how to fix it??

  6. Obaid Mahmood

    Hi,
    I need help to make sidebar widget (header-images) customized. Like already at this website in right side column, each widget has its own header image. I want the same on my blog can you please help me with this? If required i can provide you my coding also.

    I appreciate your kind help in this regard.

  7. Michael

    Sorry Obaid, but we can't offer such a service ;)