Think in WP-CLI: The Loop ![rw-book-cover](https://readwise-assets.s3.amazonaws.com/static/images/article0.00998d930354.png) ## Metadata - Author: [[Djeva]] - Full Title: Think in WP-CLI: The Loop - Category: #articles - Document Note: # Title: Understanding Loops in WordPress PHP and WP-CLI - - - tags: WordPress, PHP, WP-CLI, Loops Author: Courtney Robertson Source: Courtney's WordPress Insights Reference: N/A date created: 2024-01-21T15:30 - - - ## Table of Contents: 1. Introduction 2. The Importance of Understanding Loops 3. Examples - List all the posts - List all posts in category - List all posts in the category but exclude the current post - Return only post IDs 4. Conclusion 5. Helpful Commands ## Index Creation: - Loop - PHP - WP-CLI - WordPress ## 📝 Summary: This note explores the significance of understanding loops in WordPress, both in PHP templates and with WP-CLI. It provides examples and tips for effectively working with loops to manipulate and display WordPress content. ## 📖 Main Content: ### 1. Introduction Loops are fundamental in WordPress development, whether you're working with PHP templates or WP-CLI. They serve as the backbone for querying and displaying posts and are crucial for various tasks. ### 2. The Importance of Understanding Loops - In PHP templates, the Loop is essential for displaying posts based on the template file, making it a fundamental concept for theme development. - In WP-CLI, loops are equally crucial for performing various actions with ease, such as querying posts, listing them, or excluding specific posts. ### 3. Examples #### List all the posts - PHP Example: ```php if ( have_posts() ) : while ( have_posts() ) : the_post(); // Display post content endwhile; endif; ``` - WP-CLI Example: ``` wp post list ``` #### List all posts in category - PHP Example: ```php $category = get_the_category( get_the_ID() ); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, 'cat' => $category[0]->term_id, ); $in_this_category = new WP_Query( $args ); if ( $in_this_category->have_posts() ) : while ( $in_this_category->have_posts() ) : $in_this_category->the_post(); // Display post content endwhile; endif; wp_reset_postdata(); ``` - WP-CLI Example: ``` wp post list --cat=2 ``` #### List all posts in the category but exclude the current post - PHP Example: ```php $current_post = get_the_ID(); $category = get_the_category( $current_post ); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, 'cat' => $category[0]->term_id, ); $in_this_category = new WP_Query( $args ); if ( $in_this_category->have_posts() ) : while ( $in_this_category->have_posts() ) : $in_this_category->the_post(); if ( get_the_ID() === $current_post ) { continue; } // Display post content endwhile; endif; wp_reset_postdata(); ``` - WP-CLI Example: ``` wp post list --cat=2 --post__not_in=82 ``` #### Return only post IDs - PHP Example: ```php $current_post = get_the_ID(); $category = get_the_category( $current_post ); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, 'cat' => $category[0]->term_id, 'fields' => 'ids', ); $in_this_category = new WP_Query( $args ); foreach ( $in_this_category->posts as $id ) { if ( $id === $current_post ) { continue; } $permalink = get_the_permalink( $id ); $title = get_the_title( $id ); echo '<h3><a href="' , esc_url( $permalink ) , '">' , esc_html( $title ) , '</a></h3>'; } wp_reset_postdata(); ``` - WP-CLI Example: ``` wp post list --cat=2 --post__not_in=82 --fields=ID ``` ### 4. Conclusion Understanding loops in WordPress, whether in PHP templates or WP-CLI, is essential for efficient development. Loops are versatile tools for working with WordPress content, and mastering them can significantly improve your development workflow. ### 5. Helpful Commands - Download and activate Twenty Twenty-One theme: ``` wp theme install --activate twentytwentyone ``` - Generate 100 posts - Create a new category: ``` wp term create category "Terry Pratchett" --description="The best reporter on the Discworld." ``` - Find category terms IDs: ``` wp term list category --fields=term_id,slug ``` - Move all posts from Uncategorized category to Terry Pratchett: ``` for x in $(wp post list --cat=1 --field=ID) ; do wp post term add $x category terry-pratchett && wp post term remove $x category uncategorized ; done ``` ## 📖 References: - N/A - Summary: To effectively use WP-CLI, it is important to understand and utilize loops. The Loop is a crucial concept in WordPress development, and it is also applicable to WP-CLI. The Loop is the foundation of template files and allows you to perform various actions with WP-CLI. Examples provided in the document demonstrate how to list all posts, list posts in a specific category, exclude the current post from a category listing, and return only post IDs. The document also highlights helpful commands for downloading themes, generating posts, creating categories, and more. Understanding and utilizing loops in WP-CLI can greatly enhance your efficiency in performing actions with WordPress. - URL: https://developerka.org/2024/01/14/think-in-wp-cli-the-loop/ #### Prerequisites If you want to follow these examples in your local environment, you’ll need a theme with PHP template files, [a hybrid theme](https://developer.wordpress.org/themes/getting-started/what-is-a-theme/#theme-types), and, of course, WP-CLI installed. I’m using [Twenty Twenty-One](https://wordpress.org/themes/twentytwentyone/) as the last default hybrid theme. HINT: quickly download and activate Twenty Twenty-One theme ``` wp theme install --activate twentytwentyone ``` #### A problem/use case When you start writing PHP code in a WordPress theme or plugin, learning [the Loop](https://developer.wordpress.org/themes/basics/the-loop/) is one of the most important things. It is the base of every template file. Similarly, to perform many highly useful actions with WP-CLI, you need to learn and understand loops in CLI tools. #### Examples ##### List all the posts This snippet will give different results depending on [the template](https://developer.wordpress.org/themes/basics/template-hierarchy/). You’ll get a list of posts in all categories on archive templates, as many as you set in [reading options](https://wordpress.org/documentation/article/administration-screens/#reading); you’ll get only one on singular templates. Different archives will have different lists. The most simple loop, in the most generic template, the `index.php`, will just list all posts (limited by the number set in options) in descending order, ordered by published date. HINT: quickly generate 100 posts PHP ``` if ( have_posts() ) : while ( have_posts() ) : the_post(); // Display post content endwhile; endif; ``` WP-CLI: If we want to go beyond these default templates and queries, which we often do, we must create a custom query. That means modifying arguments for the `WP_Query` object. ##### List all posts in category For example, we want to see all the posts from the `Terry Pratchett` category. On the `category.php` template, we would use the first PHP example. But maybe we want to list all these posts on a `single.php` template as a part of the “Also in this category” section. HINT: quickly create a new category ``` wp term create category "Terry Pratchett" --description="The best reporter on the Discworld." ``` The loop from the first example will give the current post in this template, so we need to create another loop with a custom query to get all the other posts from the same category. HINT: quickly move all posts from Uncategorized category to Terry Pratchett ``` for x in $(wp post list --cat=1 --field=ID) ; do wp post term add $x category terry-pratchett && wp post term remove $x category uncategorized ; done ``` PHP ``` // We want to get category ID dynamically. $category = get_the_category( get_the_ID() ); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, // Never use -1, it's very bad for performance. 'cat' => $category[0]->term_id, // Assuming the post has only one category. ); $in_this_category = new WP_Query( $args ); if ( $in_this_category->have_posts() ) : while ( $in_this_category->have_posts() ) : $in_this_category->the_post(); // Display post content endwhile; endif; // Always reset post data after the custom loop to avoid unexpected results on the rest of the page. wp_reset_postdata(); ``` HINT: quickly find category term ID ``` wp term list category --fields=term_id,slug ``` WP-CLI: ``` wp post list --cat=2 ``` ##### List all posts in the category but exclude the current post With the previous example, we’re listing all the posts in the category, including the current one. That’s usually not desired, and, more often than not, you’ll want to exclude it. You might be tempted to use `post__not_in` argument, like this: PHP ``` // Get current post ID. $current_post = get_the_ID(); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, // Never use -1, it's very bad for performance. 'cat' => $category[0]->term_id, // Assuming the post has only one category. 'post__not_in' => $current_post, // Also very bad for performance. ); ``` Even though this example fits very well with my point, which we will come to later, I can not, in my good conscience, leave it here as an example of how to exclude the current post. What you really want to do in this situation is to get all posts with the query and skip the current post later on inside the loop. PHP ``` // Get current post ID. $current_post = get_the_ID(); // We want to get category ID dynamically. $category = get_the_category( $current_post ); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, // Never use -1, it's very bad for performance. 'cat' => $category[0]->term_id, // Assuming the post has only one category. ); $in_this_category = new WP_Query( $args ); if ( $in_this_category->have_posts() ) : while ( $in_this_category->have_posts() ) : $in_this_category->the_post(); if ( get_the_ID() === $current_post ) { continue; } // Display post content endwhile; endif; // Always reset post data after the custom loop to avoid unexpected results on the rest of the page. wp_reset_postdata(); ``` For the sake of the example, let’s say the current post ID is 82. WP-CLI: ``` wp post list --cat=2 --post__not_in=82 ``` The thing to note here are parameter names. WP-CLI uses the same parameter names as WP\_Query: `cat` and `post__not_in`. The only difference is that WP\_Query needs them in an array, while WP-CLI prefixes them with `--`. ##### Return only post IDs Let’s say you want to list only post titles as links. You don’t need the whole post object being returned by the query. You’ll only need post IDs and can get them with the fields parameter. You can switch to foreach a loop as well. PHP ``` // Get current post ID. $current_post = get_the_ID(); // We want to get category ID dynamically. $category = get_the_category( $current_post ); $args = array( 'post_type' => 'post', 'posts_per_page' => 1000, // Never use -1, it's very bad for performance. 'cat' => $category[0]->term_id, // Assuming the post has only one category. 'fields' => 'ids', ); $in_this_category = new WP_Query( $args ); foreach ( $in_this_category->posts as $id ) { if ( $id === $current_post ) { continue; } $permalink = get_the_permalink( $id ); $title = get_the_title( $id ); echo '<h3><a href="' , esc_url( $permalink ) , '">' , esc_html( $title ) , '</a></h3>'; } // Always reset post data after the custom loop to avoid unexpected results on the rest of the page. wp_reset_postdata(); ``` WP-CLI: ``` wp post list --cat=2 --post__not_in=82 --fields=ID ``` This is the only place the WP-CLI parameter has a different name than the WP\_Query one. You may have never thought about what the query returned to you because it was never on your way – you would use only what you needed. Might as well get the whole post object because you never know what the future brings. However, by default, you’ll get post ID, title, post name, published data, and status fields in WP-CLI. If you’re using the loop to perform some action other than listing all posts, chances are you don’t need all those parameters. ``` +-----+--------------+-------------+---------------------+-------------+ | ID | post_title | post_name | post_date | post_status | +-----+--------------+-------------+---------------------+-------------+ | 105 | Post 101 | post-101 | 2024-01-13 12:02:38 | publish | | 85 | Post 81 | post-81 | 2024-01-13 12:02:38 | publish | +-----+--------------+-------------+---------------------+-------------+ ``` Because of this, you can really fine-tune what fields you’ll get back from the WP-CLI query. WP\_Query has [only a few options](https://developer.wordpress.org/reference/classes/wp_query/#return-fields-parameter) for the `fields` argument: `all`, `ids`, and `id=>parent`; while WP-CLI can use *ALL* query arguments. **I’ll repeat this: WP-CLI can use any query argument for returning values.** For example, return post title, published and modified date for all posts and order descending by modified date: ``` wp post list --fields=post_title,post_date,post_modified --order=DESC --orderby=post_modified ``` Go ahead, try it. #### Conclusion WP-CLI queries are template-agnostic and treat every query as a custom one. Listing posts in WP-CLI has a different purpose than loops in PHP files. Other than informational value, just listing posts is not very useful in the terminal. But performing and understanding loops in WP-CLI is crucial for doing other actions that would take much time in PHP (or dashboard) and only one command in WP-CLI (e.g. deleting tens of thousands of posts). #### Helpful commands Download and activate Twenty Twenty-One theme ``` wp theme install --activate twentytwentyone ``` Generate 100 posts Create a new category ``` wp term create category "Terry Pratchett" --description="The best reporter on the Discworld." ``` Find category terms IDs ``` wp term list category --fields=term_id,slug ``` Move all posts from Uncategorized category to Terry Pratchett ``` for x in $(wp post list --cat=1 --field=ID) ; do wp post term add $x category terry-pratchett && wp post term remove $x category uncategorized ; done ``` #### Resources, Think in WP-CLI: The Loop, Djeva, 01hmhbse688g27f6xdak28877w.