Elegant wp-query markup for repetitive post-type category loops For WordPress sites, I always end up building custom post types. They usually have categories, but I don't use much of the 'archive' abilities. This site, for example, is for a film director and so it has "project" resources and then have the standard 'category' taxonomy. I find myself copying and pasting this code in blocks to list each category.

It seems like there are 2 ways to go with PHP. You can keep the markup clear and work the PHP into it, or stay mostly in PHP land and echo markup. I think PHP is a fine language - but simply the syntax has lead me to JS frameworks in its place:

{{#each things as |thing|}}
  &lt;li&gt;{{thing.name}}&lt;/li&gt;
{{/each}}


Regarding custom fields, I am far more accustomed to ACF vs CMB2, but recently worked on a project with CMB2 and I kinda saw some light in creating the template with more PHP... but both ways still seem less than awesome.

Here's the block of code I find myself using the most. I do it so much that I have a version with xxxxx where project is - that I use to just find and replace with the resource name.

The markup isn't always exactly the same - so I paste it rather than build a function that just takes 1 parameter of resource - but I could have conditionals

What say you - brave code reviewers? How can I up my game with this? Simplify? Change my view-point? Just relax? build a function? Best practices?

Should I be using some archive template for repeating post-type categories instead?

&lt;section class='container projects one'&gt;
&lt;div class='inner-w'&gt;

  &lt;?php
  // =========================================
  // Define the rules/arguments
  $project_args = array(
    'post_type' =&gt; 'project',
    'showposts' =&gt; '-1',
    'category_name' =&gt; 'documentary',
  );

  // The Query
  $project_query = new WP_Query($project_args);

  // The Loop
  if ( $project_query-&gt;have_posts() ) { ?&gt;

    &lt;h2 class='category-title'&gt;Documentary&lt;/h2&gt;

    &lt;ul class='project-list'&gt;

    &lt;?php
      while ( $project_query-&gt;have_posts() ) {
        $project_query-&gt;the_post();

        $image = get_field('project_poster');
    ?&gt;

          &lt;li class='project'&gt;
            &lt;a class='link' href='&lt;?php the_permalink(); ?&gt;'&gt;
              &lt;figure class='poster'&gt;
                &lt;?php
                  if ( $image ) {
                    $poster = $image['sizes']['medium'];
                  } else {
                    $poster = 'https://placehold.it/1600x900';
                  }
                ?&gt;
                &lt;img src='&lt;?php echo $poster; ?&gt;' alt='&lt;?php echo get_the_title(); ?&gt; poster image' /&gt;
              &lt;/figure&gt;
              &lt;h1 class='title'&gt;&lt;?php the_field("project_title"); ?&gt;&lt;/h1&gt;
            &lt;/a&gt;
          &lt;/li&gt;

        &lt;?php } ?&gt;

    &lt;/ul&gt;

  &lt;?php } else { ?&gt;

    &lt;?php // what happens if there are no posts? ?&gt;

  &lt;? } ?&gt;

  &lt;?php wp_reset_postdata(); /* restore original post data */ ?&gt;

&lt;/div&gt;
&lt;/section&gt;


&lt;?php // repeat many blocks like the 'section' above ?&gt;
First off, Wordpress is an awful platform when it comes to separation of business logic from display logic.  If you expect to continue working in it, you can expect to continue to feel the pain that you are feeling.

The thing that concerns me the most about your comments, is that you say you cut/paste this code repeatedly.  That immediately screamw out for putting this code into a widget or function so that it is reusable.  Think if you had a function like:

display_category_list($post_type, $category_name, $show_posts = -1)
{
    // your code
}


This would simplify your main pages to have calls like:

display_category_list('project', 'documentary');
display_category_list('foo', 'bar');


So that could solve the copy/paste problem.



Now let's get into the problem of display templating.  Even working with basic PHP output mechanisms, you can greatly clean up your code, simply by separating out the logic from the HTML in a more reasonable fashion.  You could structure your code like this:

display_category_list($post_type, $category_name, $show_posts = -1)
{
    // Define main section template.
    // You could just as easily include this to keep template
    // in a separate file
    $section_template = &lt;&lt;&lt;'EOT'

&lt;section class='container projects one'&gt;
&lt;div class='inner-w'&gt;
{{list}}
&lt;/div&gt;
&lt;/section&gt;
EOT;

    // list template, again could be included
    $list_template = &lt;&lt;&lt;'EOT'

    &lt;h2 class='category-title'&gt;{{category_name}}&lt;/h2&gt;
    &lt;ul class='project-list'&gt;
    {{items}}
    &lt;/ul&gt;
    &lt;/h2&gt;
EOT;

    // item template
    $item_template = &lt;&lt;&lt;'EOT'

        &lt;li class='project'&gt;
            &lt;a class='link' href='{{link}}'&gt;
                &lt;figure class='poster'&gt;
                    &lt;img src='{{poster}}' alt='{{poster_alt_text}} poster image' /&gt;
                &lt;/figure&gt;
                &lt;h1 class='title'&gt;{{title}}&lt;/h1&gt;
            &lt;/a&gt;
        &lt;/li&gt;
EOT;

    // Actual function logic
    $args = array(
        'post_type' =&gt; $post_type,
        'showposts' =&gt; $show_posts,
        'category_name' =&gt; $category_name,
    );
    $query = new WP_Query($args);

    // put default "no post" content here (this could come from template as well)
    $list_content = '';
    if($query-&gt;have_posts()) {
        $list_content = str_replace('{{category_name}}', $category_name, $list_template);   
        $items = '';
        while ($project_query-&gt;have_posts()) {
            $post-&gt;the_post();
            $link = the_permalink();
            $poster = (get_field('project_poster')) ?
                $image['sizes']['medium'] : 'https://placehold.it/1600x900';
            $poster_alt_text = get_the_title();
            $title = the_field('project_title');
            $template_data = array(
                '{{link}}' =&gt; $link,
                '{{poster}}' =&gt; $poster,
                '{{poster_alt_text}}' =&gt; $poster_alt_text,
                '{{title}}' =&gt; $title
            );
            $items .= str_replace(
                array_keys($template_data),
                array_values($template_data),
                $item_template
            );
            $items .= PHP_EOL;    
        }
        $list_content = str_replace('{{items}}', $items, $list_content);
    }        
    echo str_replace('{{list}}', $list_content, $section_template);            
    wp_reset_postdata();
}


Note how I have moved the HTML into templates defined within nowdoc blocks.  This approach can allow you to easily pull this content out of the function itself into separate template files that are easier to manage.  For example, the function could be rewritten with template-to-variable assignments like this:

$section_template = include('/path/to/section_template.php');


With the template file looking like:

return &lt;&lt;&lt;'EOT'

&lt;section class='container projects one'&gt;
&lt;div class='inner-w'&gt;
{{list}}
&lt;/div&gt;
&lt;/section&gt;
EOT;


This means you could even go so far as to pass template file paths into the function on the fly to switch out templates to be used (or you could have some config settings for template paths that the function uses).  This could totally get you out of having any HTML within the function itself.