The Loop - Professional WordPress: Design and Development, 3rd Edition (2015)

Professional WordPress: Design and Development, 3rd Edition (2015)

Chapter 5. The Loop

WHAT’S IN THIS CHAPTER?

· Understanding the flow of the Loop and where it can be used

· Determining content display using the Loop

· Customizing the Loop with different granularities of data access

· Using template tags

· Understanding global variables and their relationship to Loop processing

· Working outside of the Loop

The Loop refers to how WordPress determines what content (posts, pages, or custom content) to display on a page you are visiting. The Loop can display a single piece of content or a group of posts and pages that are selected and then displayed by looping through the content; thus, it’s called the Loop.

This is how WordPress displays posts by default. The Loop selects posts from the MySQL database based on a set of parameters, and those parameters are typically determined by the URL used to access your WordPress website. For example, the homepage might show all blog posts in reverse chronological order by default. A category page, accessed via a URL such as http://example.com/category/halloween/, shows only blog posts assigned to that category, in this case posts put into the halloween category. An archive page shows only blog posts that are dated with that particular month and year. WordPress maps nearly every parameter about your posts into a selection variable, providing the basis for an equally wide number of different ways to alter the Loop’s content selection algorithm. It is very easy to customize what content is displayed, and where, on your website with a thorough understanding of how the Loop translates a URL into what you see when you access that link.

This chapter discusses how the Loop works, where the Loop can be used, and the logical flow of the Loop. It also covers how to customize the Loop using the many different functions and data access methods available in WordPress. Global variables that maintain the current state are also discussed along with working outside of the Loop.

UNDERSTANDING THE LOOP

Understanding how the Loop functions will help you understand how you can control it. Controlling the Loop to display exactly the content you want will be one of your most used skills in developing WordPress-powered websites. Because the Loop is at the heart of every WordPress theme, being able to customize the display content opens up the doors to making WordPress look and act however you want.

To understand the Loop, it helps to break down the steps WordPress takes to generate a page’s content:

1. The URL is matched against existing files and directories in the WordPress installation. If the file is there, it is loaded by the web server. WordPress doesn’t actually get involved in this decision; it’s up to your web server and the .htaccess file created by WordPress to decide if the URL is something handled by the web server or to be turned into a WordPress content query. This was covered in the discussion of permalinks in Chapter 2.

2. If the URL doesn’t load a WordPress core file, it has to be parsed to determine what content to load. The web server starts by loading the WordPress core through index.php to begin the setup for the Loop. For example, when visiting a specific tag page such ashttp://example.com/tag/bacon, WordPress will determine that you are viewing a tag and load the appropriate template, select the posts saved with that tag, and generate the output for the tag page.

3. The translation of URL-to-content-selection magic happens inside of the parse_query() method within the WP_Query object that WordPress created early on in its processing. WordPress parses the URL first into a set of query parameters that are described in the next section. All query strings from the URL are passed into WordPress to determine what content to display, even if they look like nicely formatted pathnames. If your site is using pretty permalinks, the values between slashes in those permalinks are merely parameters for query strings. For example, http://example.com/tag/bacon is the same as http://example.com?tag=bacon, which conveys a query string of tag with a value of bacon.

4. WordPress then converts the query specification parameters into a MySQL database query to retrieve the content. The workhorse here is the get_posts() method within the WP_Query object that is described later in this chapter. The get_posts()method takes all of those query parameters and turns them into SQL statements, eventually invoking the SQL string on the MySQL database server and extracting the desired content. The content returned from the database is then saved in the WP_Query object to be used in the WordPress Loop and cached to speed up other references to the same posts made before another database query is executed.

5. Once the content is retrieved, WordPress sets all of the is_ conditional tags such as is_home() and is_page(). These are set as part of executing the default query based on the URL parsing, and you’ll consider cases where you may need to reset these tags.

6. WordPress picks a template from your theme based on the type of query and the number of posts returned—for example, a single post or a category-only query—and the output of the query is passed to this default invocation of the Loop.

The Loop can be customized for different website purposes. For example, a news site might use the Loop to display the latest news headlines. A business directory could use the Loop to display local businesses alphabetically by name, or always put posts about sponsoring businesses at the top of every displayed page. An e-commerce site might use the Loop to display products loaded into the website. The possibilities are endless when customizing the Loop in WordPress because it gives you complete control over what content is selected and the order in which it is rendered for display.

From Query Parameters to SQL

Once the query parameters have been established, either by disassembling the URL provided by the reader or by having them explicitly set in a customized loop, the WP_Query object’s get_posts() method translates those parameters into SQL for a database query. While you can exercise great control over the type, selection, and ordering of content through the query parameters, the WordPress core also exposes filters to allow you to change the generated SQL for even finer-grained control over content selection and grouping.

The basic format of a SQL query is: SELECT fields FROM table WHERE conditions. “Fields” are the columns of the database that you want returned; you usually don’t need to modify this part of the query. The “conditions” specified in the WHERE clause change the ordering, grouping, and number of posts returned. If you dump out the generated SQL query by examining the request field of the WP_Query object, you’ll see that the WHERE portion of the SQL contains 1=1 as the first conditional. If there are no other content selection parameters, the 1=1 ensures that the generated SQL isn’t syntactically malformed in the absence of other WHERE clauses; the SQL optimizer in MySQL knows enough to ignore the 1=1.

“Table” is not simply the “posts” table in the MySQL database that contains all post data; it may also refer to an SQL JOIN of two or more tables where you need to select posts based on hierarchical metadata. WordPress makes it easy to put multiple tags on a post, or to put a post in more than one category, but relational databases aren’t adept at managing these hierarchical or networked relationships. As you see in Chapter 6, the WordPress data model uses multiple tables to manage these complex relationships, but that makes queries such as “find all posts tagged bacon” more difficult to execute. For example, to select the posts tagged bacon, an SQL JOIN is needed to first find bacon in the metadata taxonomy, build an intermediate, in-memory table of posts that have been tagged with bacon, and then select posts whose IDs appear both in the intermediate table and the main WordPress content table. Database aficionados call this a “Cartesian product” or inner join of two or more tables; the multiplicative description in both query complexity and memory consumption is accurate.

In Chapter 8, you will dig into plugins and how they attach to filter and action hook insertion points in the WordPress core. Within the SQL request generation, there are a number of filters that are invoked to give plugin authors late-binding and very explicit control over the SQL that gets executed. For example, consider a plugin that changes the post selection based on custom post metadata and context that the plugin maintains in a separate database table. Your plugin would use the posts_join filter to rewrite the JOIN clause, adding another table and field match clause to further expand the selection set. If you want to explore the core for the gory details of SQL generation, most of the query-to-request parsing is done in wp-includes/query.php, and the bulk of the JOIN work is set up in wp-includes/taxonomy.php.

One final note on SQL generation: WordPress does a very good job of building canonical URLs, that is, one and only one way to reference a particular post. Search engines notoriously consider http://example.com/bacon and http://example.com/2012/bacon as distinct pages, even if they refer to the same piece of content (this is largely done to discourage more notorious practice of link farming where many distinct URLs are generated to feign the popularity of a single target). Part of the URL parsing function within the WordPress core attempts to clean up and redirect URLs to their canonical form; the same functions also make every effort to return some relevant content rather than a 404 page. As a result, if an attempt to load a page by name fails to return any content, WordPress will insert a LIKE modifier into the WHERE clause that contains the post name. For example, if a user supplies the URL http://example.com/2015/lecter, but you have no posts with the title “Lecter,” the LIKE clause will match any posts that start with “Lecter,” such as “Lecter Fish IPA Review.” Canonical URLs and “like name” matching are part of the complex maze of URL rewriting and intent parsing that try to generate a pleasant user experience, rather than an annoying 404 error.

Understanding Content in WordPress

Before diving into the Loop in detail, it’s important to understand the different types of content in WordPress. By default, WordPress defines two types of content: posts and pages. What you’ll see in Chapter 6 is that all content types are stored in the same MySQL table, and are differentiated by their “post type.” Since the release of WordPress 2.9, it’s possible to define your own custom post types, which is basically custom content in WordPress. For example, you could have an Events custom post type to register events in WordPress.

Throughout this chapter, content is referred to as “posts,” but it’s important to remember that posts could really be any type of content in WordPress.

NOTE Custom post types are covered in Chapter 7.

Putting the Loop in Context

The Loop is the heart of a theme, which is what controls how your content is displayed. It is the functional connection between the MySQL database data and the HTML that is rendered in the visitor’s browser. Basically, anywhere a post or page is displayed, WordPress is going to use the Loop. This can be a single post or page, a loop of posts, or a sequence of loops with different display options.

Most WordPress themes feature a header, footer, and sidebar element. Figure 5.1 shows how the Loop is placed directly in the middle of these elements, creating your website content area. This section of your website is usually dynamic and will change as you navigate through it.

images

Figure 5.1 The WordPress Loop

The Loop, by default, is used in your WordPress theme template files. Custom Loops can be created anywhere in your theme template files, as Figure 5.2 shows. Custom Loops are also used in plugins and widgets. Loops can be used anywhere inside of WordPress, but different methods exist for creating custom Loops depending on where they are used, and the potential side effects of each construction will differ.

images

Figure 5.2 Using multiple Loops

Multiple Loops can be used throughout your theme template files. Custom Loops can be created in your header, sidebars, footer, and main content areas of your website. There is no limit to the number of Loops that can be displayed on your website. Keep in mind that a Loop is effectively a database query to select content and then an iteration over the selection to display it. The default Loop uses context from the visited URL to make that selection, but you can fine-tune and craft a query against the WordPress content database to implement any number of content management processes.

The following section looks at the basic flow control of the Loop and the WordPress template functions provided to customize the way content is displayed while being handled inside of a loop. Having armed you with the basics, you will now explore building custom Loops based on hand-tailoring those query parameters.

Flow of the Loop

The Loop uses some standard programming conditional statements to determine what and how to display. The first statement in the Loop is an if statement that checks whether any posts exist, because you might not have any posts with the specified category or tag. If content exists, the while statement is used to initiate the Loop and cycle through all posts or pages that need to be displayed. Finally, the_post() function is called to build the post data, making it accessible to other WordPress functions. Once the post data has been built, Loop content can be displayed in whatever format you like.

Following is a minimal Loop example. This example features the only required elements for the Loop to function properly:

<?php

if ( have_posts() ) :

while ( have_posts() ) :

the_post();

//loop content (template tags, html, etc)

endwhile;

endif;

?>

Remember that this is PHP code, so it needs to be surrounded in <?php and ?> tags. This is the Loop in its simplest form. If you’re wondering how the output from the database query got handed to this simple Loop when there are no variables passed as parameters, the answer lies in the global variable $wp_query, which is an instance of WP_Query that is referenced by the functions in the simple Loop. It is, in effect, the default query for the Loop. Note that by the time this default Loop is called, WordPress has already called theget_posts() method within the default query object to build the list of appropriate content for the URL being viewed, and the Loop in this case is charged with displaying that list of posts. Later on, you look at how to hand-structure queries to exercise fine-grain control over post selection, but for now it’s safe to assume that the database heavy lifting has been done, and the results are stored in $wp_query, when the Loop is invoked.

Some very minimal requirements exist for the Loop to work in WordPress. Let’s break down this example to look at the different parts of the Loop:

if ( have_posts() ) :

This line checks if any posts or pages are going to be displayed on the current page you are viewing. If posts or pages exist, the next line will execute:

while ( have_posts() ) :

The preceding while statement starts the Loop, essentially looping through all posts and pages to be displayed on the page until there are no more. The Loop will continue while content exists to be displayed. Once all content has been displayed, the while loop will end. The have_posts() function simply checks to see if the list of posts being processed is exhausted, or had no entries to begin with.

the_post();

Next, the the_post() function is called to load all of the post data. This function must be called inside your loop for the post data to be set correctly. Calling the_post() in turn calls the setup_postdata() function to set up the per-post metadata such as the author and tags of the content you are displaying in the Loop, as well as the content of the post itself. This data is assigned to a global variable each time through the Loop iteration. Specifically calling the_post() has the side effect of setting up the global $post variable used by most of the template tags described later on, and then advances to the next post in the list.

Setting up the post data also applies the appropriate filters to the raw content that comes out of the WordPress database. WordPress stores user-edited content exactly as entered, so if a user adds a shortcode, for example, to add a Google AdSense item at the end of a post, the shortcode is stored in the database content. When the post setup is done, the plugin that converts that shortcode to a chunk of JavaScript is called, along with other registered plugins that modify the raw post content. You’ll look at the plugin mechanics in Chapter 8, but for now, it’s important to note the distinction between the raw post data in the WordPress query object and the filtered content that is eventually rendered.

//loop content

This is where all Loop template tags are placed and any additional code you want displayed inside the Loop. This is covered in more detail later in this chapter.

endwhile;

endif;

The endwhile and endif calls end the Loop. Any code placed after these two lines will show at the bottom of your page, after all posts have been displayed. You could also place an else clause to display a message if there is no content to display in the Loop.

The Loop is usually surrounded by HTML tags in your theme template files. The following code shows how the Loop is structured in the core Twenty Fourteen theme that comes with WordPress:

<div id="main-content" class="main-content">

<?php

if ( is_front_page() && twentyfourteen_has_featured_posts() ) {

// Include the featured content template.

get_template_part( 'featured-content' );

}

?>

<div id="primary" class="content-area">

<div id="content" class="site-content" role="main">

<?php

if ( have_posts() ) :

// Start the Loop.

while ( have_posts() ) : the_post();

/*

* Include the post format-specific template for the content.

If you want to

* use this in a child theme, then include a file

called called content-___.php

* (where ___ is the post format) and that will be used

instead.

*/

get_template_part( 'content', get_post_format() );

endwhile;

// Previous/next post navigation.

twentyfourteen_paging_nav();

else :

// If no content, include the "No posts found" template.

get_template_part( 'content', 'none' );

endif;

?>

</div><!---- #content ---->

</div><!---- #primary ---->

<?php get_sidebar( 'content' ); ?>

</div><!---- #main-content ---->

Notice how the minimal Loop elements exist but are surrounded by HTML tags. This is how a normal theme template file will be structured to utilize the Loop. The HTML elements can certainly change, but the Loop elements stay the same. Customizing the style in which content is displayed and choosing post metadata to include in the page composition is done through template tags.

TEMPLATE TAGS

PHP functions used in your WordPress theme templates to display Loop content are called template tags. These tags are used to display specific pieces of data about your website and content. This allows you to customize how and where content is displayed on your website.

For example, the the_title() template tag displays the title of your post or page inside the Loop. The major benefit of using template tags is that you don’t need to know PHP code to use them.

Many different template tags are available in WordPress. Some template tags must be inside the Loop, whereas other tags can be used anywhere in your theme template files. Note that in this context, template tags refer to the WordPress functions used to extract post data for display; template files are the theme elements that control how content for a particular content type is displayed. Put another way, template files contain Loops comprising template tags. For an updated list of template tags available in WordPress, visithttp://codex.wordpress.org/Template_Tags.

Commonly Used Template Tags

There is no shortage of template tags, but typically you will use only a handful of tags in your Loops. Following are the most commonly used template tags available in the Loop. These template tags will return and display the post data listed.

· the_permalink()—Displays the URL of your post.

· the_title()—Displays the title of the post.

· the_ID()—Displays the unique ID of your post.

· the_content()—Displays the full content of your post.

· the_excerpt()—Displays the excerpt of your post. If the Excerpt field is filled out on the Post edit screen, that will be used. If not, WordPress will auto-generate a short excerpt from your post content.

· the_time()—Displays the date/time your post was published.

· the_author()—Displays the author of the post.

· the_tags()—Displays the tags attached to the post.

· the_category()—Displays the categories assigned to the post.

· edit_post_link()—Displays an edit link that is shown only if you are logged in and allowed to edit the post.

· comment_form()—Displays a complete commenting form for your post.

To learn how template tags work, just place any template tag inside the Loop and view the results. The following example views the values of a couple different template tags:

<?php

if ( have_posts() ) :

while ( have_posts() ) :

the_post();

?>

<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>

<br />

<?php

the_content();

endwhile;

endif;

?>

As you can see, your post titles are displayed with links to the permalink for each post. The content of the post is displayed directly below the post title.

Tag Parameters

Most template tags have parameters that can be added to modify the value returned. For example, the the_content() template tag has two parameters. The first parameter allows you to set the more link text like so:

<?php the_content( 'Read more', false ); ?>

Your post content will be displayed as normal, but when the <!––more––> tag is found in your post, WordPress will automatically add the text Read more, which would link to the entire blog post. The second parameter determines whether to display the teaser paragraph again when viewing the full post. The default value is false so the teaser will be displayed in both places.

NOTE The more tag in WordPress allows you to display a defined teaser from the full post on your website. For example, you could display the first paragraph of a post on your homepage, and only show the full blog post when a visitor clicks the link to view the full post. To accomplish this, you can place <!––more––> in your content in HTML view where you want this break to happen. In the visual editor, there is a button to insert a More tag.

You can also send multiple parameters to any template tag that supports it. For example, the template tag the_title() accepts three parameters: $before, $after, and $echo. The following code sets the the_title() tags $before and $after parameters to wrap the post title with h1 tags:

<?php the_title( '<h1>', '</h1>' ); ?>

You can also view the actual function in the WordPress source code. The post template functions are located in wp-includes/post-template.php. Doing a quick search for function the_title() will lead you to the exact function for the the_title() tag. You can also use the Codex for a detailed description of the template tag you are working with, in this case http://codex.wordpress.org/Template_Tags/the_title.

CUSTOMIZING THE LOOP

The opening discussion of Loop flow of control mentioned that the main workhorse for data selection is the get_posts() method of the WP_Query object. In most cases, if you want to build a custom Loop, you’ll build your own WP_Query object and reference it explicitly. Alternatively, you can use the lower-level query_posts() and get_posts() functions (not to be confused with the methods within the WP_Query object of the same name) to manipulate the output of the default query that was passed into your Loop. Both query_posts()and get_posts() use the WP_Query class to retrieve content. The final method you’ll examine is the pre_get_posts hook. This hook is called after the query variable object is created but before the actual query is run. You’ll look at the various approaches and discuss how and where you should—and shouldn’t—use them, but let’s start with a discussion of how you build a custom query object.

Using the WP_Query Object

Once WordPress is handed a URL to parse by the web server, it goes to work disassembling the tokens in that URL and converting them into parameters for a database query. Here’s a bit more detail on what happens when manipulating your own WP_Query.

WP_Query is a class defined in WordPress that makes it easy to create your own custom Loops. Both query_posts() and get_posts() use the WP_Query class to retrieve the WordPress content. When you’re using query_posts(), the global variable $wp_query is used as an instance of WP_Query, making $wp_query the default data store for several operations. Custom Loops can be used anywhere in your theme template files to display different types of content; they must build on separate instances of a WP_Query variable.

When you create a new WP_Query object, it’s instantiated with some default functions for building queries, executing the query to get posts, and parsing parameters out of a URL. However, you can use these built-in object methods to construct your own parameter strings, creating custom loops that extract whatever particular content you need for that point in your Loop.

The following is an example of a custom Loop displaying the five most recent posts on your website:

<?php

$myPosts = new WP_Query( 'posts_per_page=5' );while ( $myPosts->have_posts() )

: $myPosts->the_post();

?>

<!---- do something ---->

<?php endwhile; ?>

Rather than using the simpler have_posts() and the_post() calls that you saw in the basic Loop, this custom loop calls the methods of the newly created WP_Query object $myPosts. The explicit invocation shown here and the default have_posts() call are functionally equivalent; have_posts(), for example, is merely calling $wp_query->have_posts() using the global query variable for the default query—that is, the one generated from parsing the URL handed to WordPress by the web server.

Going into your default Loop from the URL used to invoke WordPress; there’s an additional step that takes the URL and parses it into an appropriate query string using the parse_query() method of the query object. When you build your own custom Loop, you explicitly set the parameters you want to control the query. Here’s a bit more detail on what happens inside the query function:

· Calling $myPosts->query() converts the parameters into an SQL statement via the function $myPosts->get_posts(), which then executes the query against the MySQL database and extracts the content you’ve requested.

· Equally important, the query call sets up the conditional tags such as is_home() and is_single() that are dependent upon the type of page displayed and the quantity of content for that page.

· The array of posts returned by the query is cached by WordPress so that future references to the same query won’t generate additional database traffic.

The key to building a powerful custom Loop is to map your content selection criteria into the right set of query parameters.

Building a Custom Query

Parameters are used to define what content will be returned in your Loop, whether a custom Loop or altering the primary Loop. When creating Loops, it’s essential to understand what parameters are available to help define what content will be displayed. You can use many different, sometimes confusing, parameters in creating your custom Loop to alter the output of your content.

Multiple parameters can also be set per query by separating the parameter name and values with an ampersand. For a detailed list of available parameters, visit http://codex.wordpress.org/Class_Reference/WP_Query#Parameters.

The following sections cover some of the more commonly used parameters.

Post Parameters

The most obvious, and sometimes most used, parameters select the number and types of posts to be displayed:

· p=2—Loads an individual post by ID.

· name=my-slug—Loads posts based on post slug (permalink tail).

· post_status=pending—Loads posts by post status. For example, if you choose to see only drafts, use post_status=draft.

· ignore_sticky_posts—Excludes sticky posts from being returned first. A sticky post is one that always sorts to the top of the list of posts, independent of the other parameters set for the query. You can have multiple sticky posts, making them useful for calling attention to news announcements, highlighting changes, or otherwise grabbing the reader’s attention, and this parameter lets you drop them from their priority slot at the top of the list.

· post_type=post—Loads posts based on type. If you only want to look at pages, not posts, post_type=page will retrieve them. This parameter enables special-purpose loops to select content based on custom post types, as you’ll see in Chapter 7.

· posts_per_page=5—Number of posts to load per page. This is the default. To show all posts, set this parameter to –1.

· offset=1—Number of posts to skip before loading.

Page Parameters

Pages have parameters similar to those for posts to control their selection:

· page_id=5—Loads an individual page by ID. Like post IDs and user IDs, page IDs can be found in the dashboard by hovering over a page and looking at the URL displayed at the bottom on your browser.

· pagename=Contact—Loads a page by name, in this case the Contact page.

· pagename=parent/child—Loads a child page by slug, or hierarchy of slugs (that is, its path).

Category, Tag, and Author Parameters

Posts can also be sorted by the category into which they were placed, by tags applied to the post, or by author information:

· cat=3,4,5—Loads posts based on category ID.

· category_name=About Us—Loads posts based on category name. Note that if a post belongs to more than one category, it will show up in selections for each of those categories.

· tag=writing—Loads posts based on tag name.

· tag_id=34—Loads posts based on tag ID.

· author=1—Loads posts based on user ID.

· author_name=brad—Loads posts based on author’s name.

· author__in & author__not_in—Loads posts based on user ID.

Date and Time Parameters

Parameters to select content based on their chronology are a key part of building an archive of posts, or providing a view into content through a calendar on your website’s homepage.

· monthnum=6—Loads posts created in June.

· day=9—Loads posts created on the ninth day of the month.

· year=2015—Loads posts created in 2015.

Ordering and Custom Field Parameters

You can also change the sort parameter and the sort order. If you’re building an online index, and want to show an alphabetical post listing, you’ll set the parameters for querying posts by month and author, but order the results by title. Custom field parameters allow you to query posts based on post metadata.

· orderby=title—Field to order posts by.

· order=ASC—Defines ascending or descending order of orderby.

· meta_key=color—Loads posts by custom field name.

NOTE Refer to the custom taxonomy and data discussion in Chapter 7 to see how custom fields are added to posts.

· meta_value=blue—Loads posts by custom field value. Must be used in conjunction with the meta_key parameter.

· meta_query—Used for more advanced custom field (metadata) queries.

Putting It Together

Now look at some examples using parameters. The following examples use the $myPosts->query() function from the $myPosts custom query object created in the example to select the content displayed in your custom Loop.

Display post based on post ID:

$myPosts = new WP_Query( 'p=1' );

Display the five latest posts, skipping the first post:

$myPosts = new WP_Query( 'posts_per_page=5&offset=1' );

Display all posts from today:

// display all posts from the current date

$today = getdate(); // get todays date

$myPosts = new WP_Query('year=' .$today["year"]

.'&monthnum=' .$today["mon"] .'&day=' .$today["mday"] );

Display all posts from October 31, 2015:

$myPosts = new WP_Query( 'monthnum=10&day=31&year=2015' );

Display all posts from category ID 5 with the bacon tag:

$myPosts = new WP_Query( 'cat=5&tag=bacon' );

Display all posts with the bacon tag, excluding posts in category ID 5:

$myPosts = new WP_Query( 'cat=-5&tag=bacon' );

Display all posts with the tag writing or reading:

$myPosts = new WP_Query( 'tag=writing,reading' );

Display all posts with the tags writing and reading and tv:

$myPosts = new WP_Query( 'tag=writing+reading+tv' );

Display all posts with a custom field named color with a value of blue:

$myPosts = new WP_Query( 'meta_key=color&meta_value=blue' );

Adding Paging to a Loop

If your custom Loop requires paging (navigation links), you will need to take a few extra steps. Paging is currently designed to work only with the $wp_query global variable; that is, it works within the default Loop and requires some sleight of hand to make it work in custom Loops. You need to trick WordPress into thinking your custom query is actually $wp_query in order for paging to work.

<?php

$temp = $wp_query;

$wp_query= null;

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

$wp_query = new WP_Query( 'posts_per_page=5&paged='.$paged );

while ( $wp_query->have_posts() ) : $wp_query->the_post();

?>

<h2>

<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>

</h2>

<?php the_excerpt(); ?>

<?php endwhile; ?>

First, you have to store the original $wp_query variable into the temporary variable $temp. Next, you set $wp_query to null to completely flush it clean. This is one of the few times it’s acceptable to overwrite a global variable value in WordPress. Now set your new WP_Queryobject into the $wp_query variable and execute it by calling the object’s query() function to select posts for your custom Loop. Notice the $paged variable added to the end of the query. This stores the current page, using the get_query_var() function, so WordPress knows how to display the navigation links. Now display your navigation links for paging:

<div class="navigation">

<div class="alignleft"><?php previous_posts_link( '« Previous' ); ?></div>

<div class="alignright"><?php next_posts_link( 'More »' ); ?></div>

</div>

Finally, you need to reset $wp_query back to its original value:

<?php

$wp_query = null;

$wp_query = $temp;

?>

Now your custom Loop will contain proper pagination based on the content returned.

Using the pre_get_posts Hook

The pre_get_posts hook allows you to modify any Loop query on your WordPress website. Generally, this hook is the preferred method for modifying the main WordPress Loop. The pre_get_posts hook accepts the global WordPress query by reference, which enables you to modify the query variables prior to having the query run. In short, this hook makes it very easy to modify a WordPress Loop prior to making a call to the database to retrieve the content.

When using the pre_get_posts hook, you’ll generally place the code in your theme’s functions.php file. Let’s look at an example of pre_get_posts in action:

function prowp_exclude_category( $query ) {

if ( $query->is_home() && $query->is_main_query() && ! is_admin() ) {

$query->set( 'category_name', 'halloween' );

}

}

add_action( 'pre_get_posts', 'prowp_exclude_category' );

NOTE Chapter 9 covers themes in detail.

In the preceding example, you are modifying the main WordPress Loop to only show posts in the halloween category on the home page. As you can see, the example uses conditionals to verify that the query is only modified on the home page, is the main query, and is not the admin dashboard.

Using conditional functions allows you to modify the Loop only in specific areas of WordPress. A common example is modifying WordPress search results. By default, WordPress search includes posts and pages. Let’s assume you want only posts returned in your search results:

function prowp_search_filter( $query ) {

if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {

$query->set( 'post_type', 'post' );

}

}

add_action( 'pre_get_posts', 'prowp_search_filter' );

The pre_get_posts hook filters a WP_Query object, which means anything you can do with WP_Query you can also do with pre_get_posts using the set() function. This includes all of the Loop parameters you reviewed earlier in this chapter.

For more information on the pre_get_posts hook, visit the Codex page http://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts.

Using query_posts( )

A tremendous amount of customization can be done by specifying the appropriate set of parameters for your Loop. While the WP_Query object is the most general-purpose mechanism for extracting content from the WordPress database, there are other lower-level methods that you’ll encounter.

The query_posts() function is used to easily modify the content returned for the default WordPress Loop. Specifically, you can modify the content returned in $wp_query after the default database query has executed, fine-tune the query parameters, and re-execute the query using query_posts(). The downside to calling query_posts() in this fashion is that the previously cached results from the default query are discarded, so you’re incurring a database performance hit to use this shortcut. The query_posts() function should be placed directly above the start of the Loop:

query_posts( 'posts_per_page=5&paged='.$paged );

if ( have_posts() ) :

while ( have_posts() ) : the_post();

//loop content (template tags, html, etc)

endwhile;

endif;

This example tells WordPress to display only five posts.

Explicitly calling query_posts() overwrites the original post content extracted for the Loop. This means any content you were expecting to be returned before using query_posts() will not be returned. For example, if the URL passed to WordPress is for a category page at http://example.com/category/zombie/, none of the zombie category posts will be in the post list after query_posts() has been called unless one is in the five most recent posts. You explicitly overwrite the query parameters established by the URL parsing and default processing when you pass the query string to query_posts().

To avoid losing your original Loop content, you can save the parsed query parameters by using the $query_string global variable:

// initialize the global query_string variable

global $query_string;

// keep original Loop content and change the sort order

query_posts( $query_string . "&orderby=title&order=ASC" );

In the preceding example, you would still see all of your zombie category posts, but they would be ordered alphabetically by ascending title. This technique is used to modify the original Loop content without losing that original content.

You can also pack all of your query_posts() parameters in an array, making it easier to manage. Following is an example of how to retrieve only the sticky post set in WordPress using an array called $args to store the parameter values:

$args = array(

'posts_per_page' => 1,

'post__in' => get_option( 'sticky_posts' )

);

query_posts( $args );

If no sticky post is found, the latest post will be returned instead. The query_posts() function is used to modify the main page Loop only. It is not intended to create additional custom Loops. If you want to make a slight change to the default query—for example, adding posts of a specific category or tag to every displayed page—then the query_posts() approach is a shortcut. However, it’s not without side effects or cautions:

· query_posts() modifies the global variable $wp_query and has other side effects. It should not be called more than once and shouldn’t be used inside the Loop. The example shows the call to query_posts() before post processing has started, when the extra parameters are added to the query string but before the Loop has begun to step through the returned post list. Calling query_posts() more than once, or inside the Loop itself, can result in your main Loop being incorrect and displaying unintended content.

· query_posts() unsets the global $wp_query object, and in doing so, may invalidate the values of conditional tags such as is_page() or is_home(). Going through the entire WP_Query object instantiation sets all of the conditional tags appropriately. For example, you may find with the shortcut that you have added content to a selection that the default query found contained only one post, and therefore is_single() is no longer valid.

· Calling query_posts() executes another database query, invalidating all of the cached results from the first, default query. You at least double the number of database queries executed and are incurring a performance hit for each trip back to MySQL; on the other hand the default query has already been run by the time you get to the default Loop, so there’s little chance to work around it if you’re building an entirely custom main Loop.

Using get_posts( )

Like query_posts(), there’s an alternative, simpler access function called get_posts() that retrieves raw post data. You’ll see get_posts() used in administration pages to generate a list of pages of a particular type, or it may be used within a plugin to grab all raw data for a set of posts and examine it for patterns such as common terms, tags, or external links, with the intent of discarding the content after a quick digestion. It’s not intended for user-facing content display because it turns off much of query processing and filtering that is done within the more general WP_Query approach.

What get_posts() lacks, specifically, is the ability to set up all of the global data needed to make template tags reflect the current post data. One main issue is that not all template tags are available to get_posts() by default. To fix this deficiency, you need to call thesetup_postdata() function to populate the template tags for use in your Loop. The following example shows how to retrieve a single random post using get_posts():

<?php

$randompost = get_posts( 'numberposts=1&orderby=rand' );

foreach( $randompost as $post ) :

setup_postdata( $post );

?>

<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>

<?php the_content(); ?>

<?php endforeach; ?>

You’ll notice another major difference using get_posts()—the value returned is an array. The foreach loop code is used to cycle through the array values. This example returns only one post, but if more than one were returned, this would cycle through each. Then thesetup_postdata() function is called to populate the data for your template tags.

Remember that you can also set up your get_posts() parameter using an array:

<?php

$args = array(

'numberposts' => 1,

'orderby' => rand

);

$randompost = get_posts( $args );

Although you may see older code using get_posts() or query_posts() constructions, WP_Query is the preferred approach and should be the heart of custom loop syntax. However, there are times when you’ll want the quick-and-dirty access provided by get_posts() to generate additional context or data for further customization of your Loop or in a plugin.

When working with Loops in WordPress, it’s important to understand what Loop method to use and when. The pre_get_posts hook should be used when altering the main query on the page. The WP_Query object should be used for all secondary Loops in your theme templates and plugins.

Resetting a Query

When customizing the main Loop, or creating custom Loops, it’s a good idea to reset the Loop data after you are done. WordPress features two different functions to handle this: wp_reset_postdata() and wp_reset_query().

The first method for resetting post data is wp_reset_data(). This function actually restores the global $post variable to the current post in the main query. This is the preferred method when using WP_Query to create custom Loops.

For example, assume you have the following custom Loop in your theme’s header.php file:

<?php

$myPosts = new WP_Query( 'posts_per_page=1&orderby=rand' );

// The Loop

while ( $myPosts->have_posts() ) : $myPosts->the_post();

?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br /><?php

endwhile;

?>

This will display a random post in the header of your theme. This code will also change the main query object for other Loops on the page. The original query data will not be available, which could produce unexpected results on the main posts’ Loop for your theme.

To fix the problem, place a call to wp_reset_postdata() directly after your custom Loop like so:

$myPosts = new WP_Query( 'posts_per_page=1&orderby=rand' );

// The Loop

while ( $myPosts->have_posts() ) : $myPosts->the_post();

?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br /><?php

endwhile;

// Reset Post Data

wp_reset_postdata();

Calling this function will restore the $post variable to the current post in the query. This will eliminate any strangeness in the main query for the page you are viewing.

The second method available for resetting post data is the wp_reset_query() function. From time to time, you may run into problems with page-level conditional tags being used after a custom Loop has been created. Conditional tags allow you to run different code on different pages in WordPress—for example, using the conditional tag is_home() to determine if you are viewing the main blog page. This problem is caused, as indicated in the “Using query_posts( )” section, by potentially changing the output of a database query after setting the conditional tags based on its original set of values. To fix this issue, you need to call wp_reset_query(). This function will properly restore the original query, including the conditional tags set up early in the URL parsing process.

Consider the following example:

<?php query_posts( 'posts_per_page=5' ); ?>

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br />

<?php endwhile; endif; ?>

<?php

if( is_home() && !is_paged() ):

wp_list_bookmarks( 'title_li=&categorize=0' );

endif;

?>

Executing this code will return the latest five posts followed by the links saved in your WordPress link manager. The problem you will run into is that the is_home() conditional tag will not be interpreted correctly, meaning your links will show on every page, not just the homepage. To fix this issue, you need to include wp_reset_query() directly below your Loop:

<?php query_posts( 'posts_per_page=5' ); ?>

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br />

<?php endwhile; endif; ?>

<?php wp_reset_query(); ?>

<?php

if( is_home() && !is_paged() ):

wp_list_bookmarks( 'title_li=&categorize=0' );

endif;

?>

Now that you have properly restored your Loop’s instance of the WP_Query object, the conditional tag is_home() will be followed and your links will now display only on the homepage of your website. It’s a good practice to add wp_reset_query() after using query_posts()in your Loop to ensure you do not run into problems down the road. The wp_reset_query() function actually calls wp_reset_postdata(), but it does one additional step. The function actually destroys the previous query before resetting it. In short, wp_reset_query()should always be used after a query_posts() Loop and wp_reset_postdata() should be used after a WP_Query or get_posts() custom Loop.

More Than One Loop

The Loop can be used multiple times throughout your theme and plugins. This makes it easy to display different types of content in multiple places throughout your WordPress website. Maybe you want to display your most recent blog posts below each page on your website. You can achieve this by creating more complex Loops that make multiple passes through the list of posts, or by generating multiple post arrays over which to loop.

Nested Loops

Nested Loops can be created inside your theme templates using a combination of the main Loop and separate WP_Query instances. For example, you can create a nested Loop to display related posts based on post tags. The following is an example of creating a nested Loop inside the main Loop to display related posts based on tags:

<?php

if ( have_posts() ) :

while ( have_posts() ) :

the_post();

//loop content (template tags, html, etc)

?>

<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>

<?php

the_content();

$tags = wp_get_post_terms( get_the_ID() );

if ( $tags ) {

echo 'Related Posts';

$tagcount = count( $tags );

for ( $i = 0; $i < $tagcount; $i++ ) {

$tagIDs[$i] = $tags[$i]->term_id;

}

$args=array(

'tag__in' => $tagIDs,

'post__not_in' => array( $post->ID ),

'posts_per_page' => 5,

'ignore_sticky_posts' => 1

);

$relatedPosts = new WP_Query( $args );

if( $relatedPosts->have_posts() ) {

//loop through related posts based on the tag

while ( $relatedPosts->have_posts() ) :

$relatedPosts->the_post(); ?>

<p><a href="<?php the_permalink(); ?>">

<?php the_title(); ?></a></p>

<?php

endwhile;

}

}

endwhile;

endif;

?>

This code will display all of your posts as normal. Inside the main Loop, you check if any other posts contain any of the same tags as your main post. If so, you display the latest five posts that match as related posts. If no posts match, the related posts section will not be displayed.

Multi-Pass Loops

The rewind_posts() function is used to reset the post query and loop counter, allowing you to do another Loop using the same content as the first Loop. Place this function call directly after you finish your first Loop. Here’s an example that processes the main Loop content twice:

<?php while ( have_posts() ) : the_post(); ?>

<!---- content. ---->

<?php endwhile; ?>

<?php rewind_posts(); ?>

<?php while ( have_posts() ) : the_post(); ?>

<!---- content ---->

<?php endwhile; ?>

Advanced Queries

You can also perform more advanced queries in your Loops. Let’s construct a Loop that will compare a custom field value using the meta_compare parameter:

$args = array(

'posts_per_page' => '-1',

'post_type' => 'product',

'meta_key' => 'price',

'meta_value' => '13',

'meta_compare' => '<='

);

$myProducts = new WP_Query( $args );

// The Loop

while ( $myProducts->have_posts() ) : $myProducts->the_post();

?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br /><?php

endwhile;

// Reset Post Data

wp_reset_postdata();

As you can see, the meta_compare parameter is used to display all products with a meta value for price that is less than or equal to (<=) 13. The meta_compare parameter can accept all sorts of comparison operators such as !=,>,>=,<,<=, and the default, which is =.

For more complex meta data queries, you’ll use the meta_query parameter. Now you can expand upon the preceding example. Instead of just returning product entries that are less than or equal to a price of 13, you can also only return products that are the color blue:

$args = array(

'post_type' => 'product',

'meta_query' => array(

array(

'key' => 'color',

'value' => 'blue',

'compare' => '='

),

array(

'key' => 'price',

'value' => '13',

'type' => 'numeric',

'compare' => '<='

)

)

);

$myProducts = new WP_Query( $args );

// The Loop

while ( $myProducts->have_posts() ) : $myProducts->the_post();

?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br /><?php

endwhile;

// Reset Post Data

wp_reset_postdata();

Notice the meta_query parameter accepts an array of parameters. In this example, the first item in the array is an array to verify the products are blue. The second parameter is an array to verify the product price is less than or equal to 13.

Creating Loops using meta query parameters can be extremely powerful. This is an important tool for creating complex websites with various metadata options.

Complex date-based queries can be created using the date_query parameter. Let’s look at an example:

$args = array(

'date_query' => array(

array(

'after' => array(

'year' => '2015',

'month' => '6',

'day' => '1'

),

'before' => array(

'year' => '2015',

'month' => '8',

'day' => '31'

),

'inclusive' => true

),

)

);

$my_posts = new WP_Query( $args );

// The Loop

while ( $my_posts->have_posts() ) : $my_posts->the_post();

?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br /><?php

endwhile;

// Reset Post Data

wp_reset_postdata();

The preceding code will display all posts published in the summer of 2015, between June 1 and August 31. Using the date_query parameter, you set theafter and before values based on the post published dates you want to display. The inclusive parameter is used when the after and before parameters are used, and sets whether the exact value should be matched or not. In this example, you want posts published on June 1 and August 31 to be included, so you set the value to true.

The before and after parameters also accept strtotime compatible strings, so you can simplify this query like so:

$args = array(

'date_query' => array(

array(

'after' => 'June 1st, 2015',

'before' => 'August 31st, 2015',

'inclusive' => true

)

)

);

$my_posts = new WP_Query( $args );

The preceding code will return the exact same results as the first example, but it is much easier to read and understand.

The date_query parameter can also be combined with regular Loop parameters. As an example, let’s return the most commented posts in the last year:

$args = array(

'date_query' => array(

'after' => '1 year ago',

'before' => 'today',

'inclusive' => true,

),

'orderby' => 'comment_count',

'order' => 'DESC',

'posts_per_page' => '5'

);

$my_posts = new WP_Query( $args );

In this example, you’re using the date_query parameter to return all posts published between 1 year ago and today. You are also setting the orderby value to comment_count and order to DESC. This will return the posts with the highest comment counts first. The final parameter is posts_per_page, which tells the query to return only five posts.

It’s easy to see how powerful the date_query parameter can be in your Loops.

GLOBAL VARIABLES

A global variable is a variable that has a defined value that can be accessed anywhere within the WordPress execution environment. These variables store all types of information about the Loop content, author, and users, and specific information about the WordPress installation, such as how to connect to the MySQL database. Global variables should only be used to retrieve data, meaning you should never write data to these variables directly. Overwriting the global variable values could cause unexpected results in WordPress because significant parts of core and extended functionality depend on these values being set within one context and remaining consistent for the duration of a query, page load, or single-post handling. Assigning values to global variables almost always has unintended side effects, and they’re almost always not what the user or blog author wanted. However, global variables are discussed here to shed more light on how post data can be manipulated, and you may see code snippets that utilize these functions for post processing outside of the Loop.

Post Data

You saw how the key first step in the Loop is calling the_post(). Once invoked, you will have access to all of the data in WordPress specific to the post being displayed. This data is stored in the global $post variable. The $post variable stores the post data of the last post displayed on the page. So if your Loop displays ten posts, the $post variable will store post data for the tenth post displayed.

The following example shows how you can reference the $post global variable and display all values in the array using the print_r() PHP function.

<?php

global $post;

print_r( $post ); //view all data stored in the $post array

?>

The preceding code will print the array values for the $post global variable. The default WordPress blog post would look like this:

WP_Post Object

(

[ID] => 1

[post_author] => 1

[post_date] => 2015-06-09 19:05:19

[post_date_gmt] => 2015-06-09 17:23:50

[post_content] => Welcome to WordPress. This is your first post.

Edit or delete it, then start blogging!

[post_title] => Hello world!

[post_excerpt] =>

[post_status] => publish

[comment_status] => open

[ping_status] => open

[post_password] =>

[post_name] => hello-world

[to_ping] =>

[pinged] =>

[post_modified] => 2015-06-09 19:04:12

[post_modified_gmt] => 2015-06-09 19:04:12

[post_content_filtered] =>

[post_parent] => 0

[guid] => http://localhost/Brad/?p=1

[menu_order] => 0

[post_type] => post

[post_mime_type] =>

[comment_count] => 1

[filter] => raw

)

As you can see, the $post global variable contains all sorts of data for the post. You can also display specific pieces of data from the array, such as the post title and content, like so:

<?php

global $post;

echo $post->post_title; //display the post title

echo $post->post_content; //display the post content

?>

Accessing the content through the global $post variable means that you are accessing the unfiltered content. This means any plugins that would normally alter the output of the content will not affect the global content value. For example, if you had the built-in[gallery] shortcode in your post to display all images uploaded on the post, retrieving the post content as shown would return [gallery] instead of the actual image gallery.

Remember that WordPress provides template tags that can be called anywhere to retrieve these values as well, and in most cases, template tags are going to be the preferred mechanism for getting at these bits. For example, if you need to get the permalink of your post, you can use the following method:

<?php

global $post;

echo get_permalink( $post->ID ); //displays the posts permalink

?>

This is covered in more detail in the section “Working Outside the Loop,” later in this chapter.

Author Data

$authordata is a global variable that stores information about the author of the post being displayed. You can use this global variable to display the author’s name:

<?php

global $authordata;

echo 'Author: ' .$authordata->display_name;

?>

The $authordata variable is created when setup_postdata() is called during the_post() function call in the Loop. This means the $authordata global variable will not be created until the Loop has run for the first time. Another problem with this method is that the global values do not get passed through hook filters, meaning that any plugin you install to override this functionality would not be run.

The preferred method for accessing the author metadata, like that for getting post data, is to use the available WordPress template tags. For example, to display the author’s display name, you would use this code:

<?php

echo 'Author: ' .get_the_author_meta( 'display_name' );

?>

The get_the_author_meta() and the_author_meta() functions are available for retrieving all metadata related to the author of the content. If this template tag is used inside the Loop, there is no need to pass the user ID parameter. If used outside of the Loop, the user ID is required to determine what author metadata to retrieve.

User Data

The $current_user global variable stores information on the currently logged-in user. This is the account that you are currently logged in to WordPress with. Following is an example showing how to display the logged-in user’s display name:

<?php

global $current_user;

echo $current_user->display_name;

?>

This is a useful technique if you want to display a welcome message to your users. Remember that the display name will default to the user’s username. To display a welcome message to any user that is logged in, you could use this code:

<?php

global $current_user;

if ( $current_user->display_name ) {

echo 'Welcome ' .$current_user->display_name;

}

?>

Environmental Data

WordPress also has global variables created for browser detection. The following is an example showing how you can detect the user’s browser version in WordPress using global variables:

<?php

global $is_lynx, $is_gecko, $is_IE, $is_opera, $is_NS4,

$is_safari, $is_chrome, $is_iphone;

if ( $is_lynx ) {

echo "You are using Lynx";

}elseif ( $is_gecko ) {

echo "You are using Firefox";

}elseif ( $is_IE ) {

echo "You are using Internet Explorer";

}elseif ( $is_opera ) {

echo "You are using Opera";

}elseif ( $is_NS4 ) {

echo "You are using Netscape";

}elseif ( $is_safari ) {

echo "You are using Safari";

}elseif ( $is_chrome ) {

echo "You are using Chrome";

}elseif ( $is_iphone ) {

echo "You are using an iPhone";

}

?>

This is extremely useful when designing a website that needs to include browser-specific tasks or functionality. As always, it’s best to stick with web standards and degrade gracefully for lesser browsers, but in some circumstances this can be very beneficial. For example, you can use the $is_iphone variable to load a custom style sheet for iPhone web users.

WordPress features another global variable to detect if the user is on a mobile device, which could be a smartphone or tablet. This global variable is called $is_mobile. Rather than calling this global variable directly, there’s a handy function available calledwp_is_mobile(). This function detects if the user is on a mobile device. If you are browsing using a mobile device, the function returns true; if not, the function returns false, as shown here:

if ( wp_is_mobile() ) {

echo "You are viewing this website on a mobile device";

}else{

echo "You are not on a mobile device";

}

WordPress also stores what type of web server the website is hosted on using the $is_IIS and $is_apache global variables. Here’s an example:

<?php

global $is_apache, $is_IIS;

if ( $is_apache ) {

echo "web server is running Apache";

}elseif ( $is_IIS ) {

echo "web server is running IIS";

}

?>

Depending on what web server a website is using, code can produce different results than expected. As a developer, you need to consider that your plugins and themes may be running on WordPress installations on different web servers; you might also need to check what the user is running in order to accomplish specific tasks.

Global Variables or Template Tags?

Generally speaking, template tags should be used whenever they can be. There will be certain instances where a template tag will not be available. In this case, global variables can be substituted to access the information you need. Also, global variables are great for retrieving unfiltered data, meaning the values will bypass any plugin, altering what would normally be used against the content and giving you the original value to work with. Once your code has accessed or processed the original value, you can still force the plugin filters to run using the following code:

<?php apply_filters( 'the_content', $post->post_content );?>

While this is included in a discussion of working outside of the Loop, you can access these global variables inside the loop, but again remember to treat globals as read-only, as changing their values will have possibly negative side effects.

WORKING OUTSIDE THE LOOP

There are times when you’ll want to access generic post information, or to manipulate some information about the currently displayed post outside of the Loop. WordPress provides some functions to operate on sets of posts for even finer-grain control over post display.

Along with access to global variables, there is a set of WordPress functions to return generic information that’s not specific to a single post, or the post currently displayed. Following is a list of frequently used functions when working outside the Loop:

· wp_list_pages()—Displays a list of pages as links

· wp_list_categories()—Displays a list of categories as links

· wp_tag_cloud()—Displays a tag cloud from all tags

· get_permalink()—Returns the permalink of a post

· next_posts_link()—Link to display previous posts

· previous_posts_link()—Link to display next posts

You already saw how you could create navigational links using next_posts_link() and previous_posts_link() in the custom Loop example. Now explore some of these functions to get a real feel for how they work.

To display a list of pages in WordPress, you can use the wp_list_pages() function. This function will return your pages in a list format, so it’s important to wrap the function call with <ul> tags, as shown here:

<ul>

<?php wp_list_pages( 'title_li=' ); ?>

</ul>

The preceding code would generate a list of pages from WordPress with links. Notice that you set the parameter title_li to nothing, which eliminates the default title displayed for your pages. The function would generate your menu list like so:

<ul>

<li class="page_item page-item-1">

<a href="http://example.com/about/" title="About">About</a>

</li>

<li class="page_item page-item-2">

<a href="http://example.com/order/" title="Order">Order</a>

</li>

<li class="page_item page-item-3">

<a href="http://example.com/contact/" title="Contact">Contact</a>

</li>

</ul>

You can also use the wp_page_menu() function to generate a page menu. There are several advantages to this page listing function. The first is a new show_home parameter allowing a Home link to automatically be added to the list of pages. You also don’t have to remove the title using title_li, as in the preceding code. This function also wraps a custom <div> around your menu, the class of which you can set. The following is an example of this function:

<?php wp_page_menu( 'show_home=1&menu_class=my-menu&sort_column=menu_order' ); ?>

Another common function for generating links is wp_list_categories(). This function lists your categories, and subcategories, in a list as well. Consider the following example:

<ul>

<?php wp_list_categories( 'title_li=&depth=4&orderby=name&exclude=8,16,34' ); ?>

</ul>

This code will generate a list of categories with links. As before, you are setting your title to nothing, rather than the default Categories title. You are also setting the depth to 4. The depth parameter controls how many levels in the hierarchy of categories to be included in the list. The categories will be ordered by their name. You are also excluding three categories (8, 16, and 34) based on their IDs.

The functions next_posts_link() andprevious_posts_link() are typically used directly after your Loop has completed. These two functions will generate the previous and next links for viewing more posts on your website. Notice that the next_posts_link() function actually returns your previous posts. The reason for this is that WordPress assumes your posts are displaying in reverse chronological order, meaning the next page of posts would actually be posts from earlier in the timeline.

Now imagine you’d like to load a single post outside of the Loop. To do this, you use the get_post() function to load your post data. The following example loads the post data for post ID 1031:

<?php

$my_id = 1031;

$myPost = get_post( $my_id );

echo 'Post Title: ' .$myPost->post_title .'<br />';

echo 'Post Content: ' .$myPost->post_content .'<br />';

?>

The get_post() function has only one required parameter: the post ID you want to load. You must pass a variable containing an integer for the ID. Passing a literal integer (for example, 5) will cause a fatal error. The second optional parameter is how you would like the results returned: as an object, an associative array, or a numeric array. By default, an object is returned. To return an associative array you can run this code:

<?php

$my_id = 1031;

$myPost = get_post( $my_id, ARRAY_A );

echo 'Post Title: ' .$myPost['post_title'] .'<br />';

echo 'Post Content: ' .$myPost['post_content'] .'<br />';

?>

No matter how you return the results, however, this invocation of get_post() returns the raw content from the WordPress database. Filters and processing normally done within the loop won’t be applied to the returned content. The solution is to use thesetup_postdata() function in conjunction with get_post() to set up your global post data and template tags for use with your post:

<?php

$my_id = 1031;

$myPost=get_post( $my_id );

setup_postdata( $myPost );

the_title();

the_content();

?>

The get_post() function uses the internal WordPress object cache. This means that if the post you are loading is already in the cache, you will avoid running an unneeded database query. It’s easy to see how useful this function can be to quickly and efficiently load a single post outside of the Loop.

Some functions that can be used inside the Loop can also be used outside of the Loop. For example, you can use the the_author_meta() function to retrieve specific author metadata:

The email address for user id 1 is <?php the_author_meta( 'user_email', 1 ); ?>

Remember that when calling the the_author_meta() function outside of the Loop, you have to specify the author’s ID that you want to load metadata for. If you call this function inside the Loop, you do not need to specify this ID because it will load the author data for the current post.

WordPress also features specific functions for retrieving individual data about a post outside of the Loop. For example, you can use the get_the_title() function to retrieve a post’s title based on post ID like so:

<?php

echo 'Title: ' .get_the_title( 1031 );

?>

You can also use a function to retrieve post metadata (custom fields) from an individual post. To do this, you use the get_post_meta() function, as shown here:

<?php

echo 'Color: ' .get_post_meta( 1031, 'color', true );

?>

The get_post_meta() function accepts three parameters: post ID, key, and single. The post ID is the ID of the post you want to load metadata for. The key is the name of the meta value you want to load. The third optional value determines whether the results are returned as an array or whether the function will return a single result. By default, this is set to false so an array would be returned. As you can see, you can set this value to true so only a single color is returned.

SUMMARY

This chapter covered the basic mechanics of WordPress content selection and display and provided a guide to the WordPress core to help you locate the code used to implement these functions. The real power of WordPress is in its extensibility through plugins and themes. You are first going to look at the WordPress data model in more detail in Chapter 6, which shows you how the various data items saved for all content, users, and metadata relate to each other. Chapter 7 will cover custom post types, custom taxonomies, and metadata, and will show you the various types of content you can define and use in WordPress. You will then use that as the basis for a full-fledged plugin construction discussion in Chapter 8. Along with plugins, themes are the other primary avenue for extending and customizing WordPress, and you reapply some of the Loop constructs with a deeper look at templates and content presentation in Chapter 9.