Create a list of posts with topic headdings

OK I got it working. Not sure if it is the best way but I thought I would add a reply anyway.

Set up the information needed for the custom loop using $args = array As described in http://codex.wordpress.org/Class_Reference/WP_Query

$args = array(
'post_type'      => 'product',
'meta_key'       => 'wpcf-topic',
'orderby'        => 'meta_value',
'order'          => 'ASC',
'posts_per_page' => -1,
'date_query'     => array(
    array(
        'column' => 'post_date_gmt',
        'after'  => 'Monday 1 week ago',
        ),      
    ),
);

I used this to select the post type as product, order them by topic in ascending order (alphabetical order), set the posts per page to all of them (I think that is what -1 does), and finally limit it to only get the products added after “monday 1 week ago” using the new date_query introduced in WordPress 3.7

declared a new variable with nothing in it

$last_topic="" 

started the custom loop with

$loop = new WP_Query( $args );
if ( $loop->have_posts() ) {
    while ( $loop->have_posts() ) : $loop->the_post();

set a new variable $topic to the value of my custom field using

$topic=(get_post_meta( get_the_ID(), 'wpcf-topic', true ));

( I used types plugin to create a new custom field and it adds wpcf to the start of the field meta_key so I need to use ‘wpcf-MyFieldName’ for the meta_key when referring to the custom field)

used an if to show the title only if $topic is not the same as $last_topic

    if ($last_topic !== $topic){ echo "<h1>"  .  $topic   .  "</h1>";
}  

Then, after the rest of my loop template I added

    $last_topic=$topic;

So now topic and last topic are the same and the topic tile will not be echoed until it changes to something different to the last one. This only makes sense because they are ordered by topic already so all products in a topic will be in the same place.

I closed the loop with

    endwhile;
wp_reset_postdata(); // resets the loop back to the standard WordPress loop

} else {
    echo __( 'No products found' );
}

All the code I used is below

$args = array(
'post_type'      => 'product',  
'meta_key'       => 'wpcf-topic',
'orderby'        => 'meta_value',
'order'          => 'ASC',
'posts_per_page' => -1,
'date_query'     => array(
    array(
        'column' => 'post_date_gmt',
        'after'  => 'Monday 1 week ago',
        ),      
    ),
);

$last_topic="";

$loop = new WP_Query( $args );
if ( $loop->have_posts() ) {
    while ( $loop->have_posts() ) : $loop->the_post();

    $topic=(get_post_meta( get_the_ID(), 'wpcf-topic', true ));

    if ($last_topic !== $topic){

    echo "<h1 class="red grid__item">"  . $topic .  "</h1>";
    }
         woocommerce_get_template_part( 'content', 'product' ); // gets templet for product 

    $last_topic=$topic;
    endwhile;
wp_reset_postdata();
} else {
echo __( 'No products found' );
}

The reasoning behind doing it this way rather than looping through each topic one after another is that I don’t want to limit it to a set number per topic, rather get all new products after monday one week ago. I also wanted to limit the number of loops to reduce the number of calls to the database. I am still learning so cannot say how much (if at all) doing it this way speeds up things, or reduces strain on the database. But in as I am expecting about 50 products per week to be added, in about 10-15 categories, it seemed like that would be a lot of loops per page load if I had a new loop for each topic found.

One final thing. The reasoning behind setting a custom field for topic, when it sort of looks like it should be a taxonomy (in fact in my case it will almost always make sense to be the same as the parent taxonomy for the product) is that you cannot order posts by taxonomy, for reasons explained very clearly here.

http://ottopress.com/2011/when-to-not-use-a-custom-taxonomy/
without that post I would have never got this far in understanding what to do.