How the Loop Works

The Word “Loop” throws many beginner developers of WordPress into the confusion. Even I was one of them when I was getting started. 

So, it is important that you understand how “The Loop” works. But to understand how “the Loop” works, one must understand how WordPress decides what posts or post to display on a page.

I learned this from the following page of the WordPress Codex. It is an eye-opener for me.

https://codex.wordpress.org/Query_Overview

What follows now is a highly simplified version of the process mentioned in the above article.

Note:  If you failed to understand the below flow, it is totally Ok. Just remember that, with the help of the Loop, WordPress displays correct content on a page based on the URL of the Page.

Anyway, We have already look at the flow of WordPress a couple of times. But, let’s take a look at it one last time. This time, we look at it by keeping the Loop in mind.

It all starts with a visitor typing a website URL into the web browser and hitting enter. 

1) As soon as the visitor hits enter, the browser sends the request to the Web Server for the web page.

2) The Web Server receives the request and checks if there is a file name or directory name in the URL. If there is a file name and if that file exists, the Web Server processes that particular file and sends it as a response to the browser. 

If it is a directory and if that directory exists, the Web Server sends the content of the directory to the browser.

4) If the URL is not about a particular file or directory, Server starts executing the WordPress core starting with the index.php file. From here onwards, WordPress takes the control.

5) WordPress loads and executes the plugins that are currently active.

6) WordPress then loads the functions.php file of the active theme.

7) WordPress runs the wp() function and this is where it all begins. This function calls $wp->main() function internally and this tells WordPress to Parse the URL and figure out what content should be displayed on a page.

The URL parsing is done by  $wp->parse_request() function call. And the end goal of URL parsing is to determine query variables from the URL.

“What is a query variable and why do we need to determine it?”

Simply put, a query variable is fed to the MYSQL query to retrieve a particular piece of content from the Database.

If Ugly Permalinks are enabled on our WordPress site, the query variable can be directly seen in the URL, like this:

https://usablewp.com/?page_id=85 (If the visitor is requesting a page)

In the above URL, page_id is a query variable set to a value of 85. So, WordPress puts this query variable inside the MYSQL query, retrieves the information of the page with ID 85 from the database.

https://usablewp.com/?cat=13 (If the visitor is requesting a category of blog posts)

In the above URL,cat is a query variable set to a value of 13. This time, WordPress retrieves all the blog posts that are assigned to the category with ID of 13.

Here is the list of Query Variables that WordPress ships with:

https://codex.wordpress.org/WordPress_Query_Vars

But, WordPress by default enables Pretty Permalinks for our site. So, URLs don’t include query variables, for example:

https://usablewp.com/category/tips (this is same as https://usablewp.com/?cat=85)

For this reason, WordPress has to determine the query variables by parsing the URL and it does so by using the Rewrite API

The WordPress Rewrite API makes the pretty permalinks features possible because It is the workhorse that transforms the URL into query variables, for example:

a) If the user visits https://usablewp.com/category/tips, Rewrite API determines the following query variable

cat=13

b) If the user visits https://usablewp.com/get-help-now, Rewrite API determines the following query variable:

page_id=85

8) Once the Query Variables are determined, these are put inside an array called “Query Specification”.

9) This Query Specification is then converted into an MYSQL query and this MYSQL query is executed to get a list of posts using the WP_Query->get_posts() function, for example:

a)  If the user visits https://usablewp.com/get-help-now, the WP_Query->get_posts() function only retrieves a single page from the database. 

b) But, If the user visits https://usablewp.com/category/tips, the WP_Query->get_posts() function retrieves a bunch of posts from the database.

We also refer to this MySQL query as the “Main Query”. From now, I could be referring to this Main Query quite a bit.

10) Next, All these retrieved posts are saved to the $posts variable of the global $wp_query object. To be precise, $wp_query->posts. The $wp_query object is just an instance of the WP_Query class. If future lessons, We will be using this class to craft our Custom Queries.

(Spoiler Alert ) The Loop uses this global $wp_query object behind the scenes.

At this point, because WordPress has already figured out what content should be displayed on the page, So, It sets all the is_ variables that are used by conditional tags such as is_page(), is_single(), is_category etc. Trust me, these conditional tags are really helpful and saves us a lot of time. So, We will be using these Conditional Tags quite extensively throughout our WordPress Theme Development career.

Important Realization: WordPress has pulled the posts from the Database long before the Loop is executed inside our template files.

11) Next, based on the type of content retrieved from the Database, WordPress picks an appropriate template from our theme using the Template Hierarchy logic and starts processing it, for example:

a) If the user visits https://usablewp.com/, at this point of the flow, WordPress already knew that the user is trying to access the Homepage, and picks the front-page.php template file from the active theme. 

b) If the user visits https://usablewp.com/get-help-now, WordPress already knew that the user is trying to access a page and picks looks for the page.php inside the active theme, if it is not present, it looks for another file. Refer to Template Hierarchy Image.

c) If the user visits https://usablewp.com/category/tips, WordPress looks for a template file in the active theme’s directory that matches the category’s slug. In the above URL, the category slug is “Tips”, So WordPress looks for a template file named category-tips.php. If it is not present, it looks for another file. Refer to Template Hierarchy Image.

12) No matter what Template file WordPress picks, it comes across the Loop inside that template file.

Important Realization: We must put/include the Loop inside every template file we use to display a single post or bunch of posts.

Imagine that the user has visited https://usablewp.com/category/tips, and there are five posts that are assigned to “tips” category. 

Now, all these five posts are store inside the $wp_query->posts array.

WordPress picked the category.php template file to display these posts and this file contains the following:


<?php while( have_posts() ): ?>
    <?php the_post(); ?>
    <div class="actual-content">
        <?php the_title(); ?>
        <?php the_date(); ?>
        <?php the_content(); ?>
    </div>
<?php endwhile; ?>

The First line of the Loop is:


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

The WHILE statement is used to loop through all posts that are inside the $wp_query->posts array and display them neatly to the frontend.

Let me elaborate it.

The have_posts() function inside this WHILE Statement checks to see if there any posts inside $wp_query->posts that needs to be processed, that is, if the current query( Main/Custom Query ) has any results to loop over. Based on this it returns either TRUE or FALSE.

This function starts the Loop, steps through it, and finally ends the Loop when all the posts inside $wp_query->posts array are exhausted.

In our case, the Main Query returned five posts and we are still at the first iteration, so the have_post() function returns true and we step into the Loop for the first time.

During the First Iteration of the Loop

Once the WordPress is inside the Loop, it comes across the the_post() function. This function internally calls the setup_postdata() function to fill the global $post variable with the data of the first post inside $wp_query->posts array. 

Technically, now the global $post variable contains all the information that belongs to the first post.

Every template tag that we use inside the Loop relies on the global $post variable to output specific piece of information about a particular post.

So, the_title() function ouputs the title of the first post by accessing global $post variable internally. 

Then, the_content() function outputs the content of the first post by accessing the same $post variable.

Finally, the_date() function outputs the date of the first post by accessing the same $post variable. 

That’s all.

By the end of the First iteration of the Loop, WordPress echoes the content of the first post.

Next, We are back again to the top of Loop for the Second Iteration because there are still four more posts that we need to output.

During the Second Iteration of the Loop

Once the WordPress is inside the Loop for the second time, it comes across the the_post() function again. 

This time, WordPress replaces the old data( first post data ) inside the global $post variable with the data of the second post inside $wp_query->posts array by using the setup_postdata() function call.

Important Realization: The data inside the global $post variable is set freshly for every post in the Loop. For example, if the Main query returns five posts from the database, the global $post would be filled with the data of the current post of the Loop. So, when the loop ends, the global $post variable still possesses the data of the last post inside the $wp_query->posts array. This is very important for you to remember.

Next, the_title(), the_content and the_date() functions outputs the data of the second post using the global $post variable. 

Now, all the remaining posts inside the $wp_query->posts array is displayed in the same way. 

Once we looped through all the posts (After the Fifth Iteration)

Great! So, the have_posts() function will finally return false indicating “Hey! We needed to display 5 items and currently, we displayed all of them. so, don’t go inside the Loop.”

Hence, We are finally out of the Loop. 

And, This is exactly how the Loop Works.

How this Applies to a single Page like Homepage

If we apply this same flow to the Homepage, the $wp_query->posts would only contain a single post with the data of the Homepage. So the Loop will run only once. And, the global $post variable will contain the data of the Homepage even after we exit out of the Loop.

Now, it’s time to answer the three question were raised in the last lesson.

Q and A

1) We did not provide any parameters to any of the functions in the Loop, But still, how does the Loop output the Homepage content?

It is because the $wp_query object is the backbone of the Loop.

For example, the have_posts() function inside the WHILE Loop is just a wrapper function for the $wp_query->have_posts()

This applies to the the_post() function inside the Loop too. It is just wrapper function for the $wp_query->the_post() method.

Technically, everything that is related to the Loop uses the $wp_query object behind the scenes. 

2) is it because we put the Loop inside the front-page.php file?

The answer to the above question answers this question too.

WordPress is picking the template file based on the query variables. When it comes to Homepage, it looks from the front-page.php file inside the active theme. If the file is found, but there is no Loop inside it, the content will still not get displayed.

3) why we need to use a WHILE Loop to display the content of a single page like Homepage? We generally use a WHILE Loop when we are displaying a bunch of pages, right?

As you have seen the above flow, WordPress uses the same approach( same code ) to display any kind of page on the site. Be it a single post or a bunch of blog posts. It takes the term “Code Reusability” to the next level.

Get it?

In the next Lesson, We will style our Homepage’s above the fold content. Right now, it doesn’t look good.