It’s pretty common to need to change the number of columns on WooCommerce product archive pages. You can code it yourself by adding a simple function into your theme’s functions.php file. From the WooThemes docs:

// Change number or products per row to 3
add_filter( 'loop_shop_columns', 'loop_columns' );
if ( ! function_exists( 'loop_columns' ) ) {
    function loop_columns() {
        return 3; // 3 products per row
    }
}

Or, if you don’t want to mess with code, just install the WooCommerce Product Archive Customizer plugin and, voila!

But what if you need a 3-up grid for archive pages that have sub-categories, and a 5-up grid for archive pages that don’t?

Turns out, a little creative variation on the above will get you where you want to go. But since it’s not immediately obvious, I thought the following might be helpful.

As always, your mileage may vary, and if you’re trying this without being familiar with WordPress coding, keep in mind you’re doing this at your own risk.

Part 0 — Figure out what kind of product archive page this is.

What we need to know is whether we have an archive page that’s in a category that has subcategories. So, we consult the global query object:

function do_we_have_product_subcategories() {
    global $wp_query;

    $current_page = get_queried_object();
    $children = get_term_children( $current_page->term_id, 'product_cat' );

    return ( empty( $children ) ? false : true;
}

To flesh that out a little, we ask WordPress to tell us what kind of page came back from the database when this page was loaded. What WordPress gives us is all kinds of information about the current page that we’ve stored in the $current_page variable. What we want to know is whether the product category of the page has any sub- or child-terms. We ask WordPress for that information and store it in our $children variable.

So either we have children or we don’t (we don’t really care what they are in this case, just whether there are any) and simply return true or false: yes we have children or no we don’t.

Part 1 — Now we can change the shop loop’s grid accordingly.

Back to the basic function the folks at Woo gave us for changing the grid, only now we can change it based on the new information we have about the current product archive page:

add_filter( 'loop_shop_columns', 'loop_columns' );
if ( ! function_exists( 'loop_columns' ) ) {
    function loop_columns( $columns ) {

        // First make sure this is a product with a product category,
        // and if not, just send back whatever came without changing it.
        if ( ! is_product_category() || is_product() ) return $columns;

        if ( do_we_have_product_subcategories() ) {
            return 3; // we have children, let's display 3-up
        } else {
            return 5; // no children, let's display 5-up
        } // end if subcats
    } // end loop_columns()
} // end if function_exists

Notice that we’ve added a check to make sure we’re on a product archive page. And we’ve also allowed our version of the loop_columns function to receive whatever the loop count was before, so we can send it back intact should we not be on a page where it’s appropriate to alter it.

Part 2 — You’ll probably want to change the column widths

WooCommerce comes with it’s own CSS to set the widths of the grid columns. In the case of changing the grid to one size on every page, it’s easy enough to just override the default widths in your theme’s style.css file. But now that the grid is set dynamically, you need a way to dynamically change the CSS that gets loaded, too!

We all know (right!) that the proper way to get your CSS loaded is with wp_enqueue_scripts. The next level is to add a bit of CSS as an inline style. Here goes:

add_action( 'wp_enqueue_scripts', 'our_loop_columns_css' );
function our_loop_columns_css() {

    // If we're not dealing with a product category-archive template, bail.
    if ( ! is_product_category() ) return;

    if ( do_we_have_product_subcategories() ) {
        $width = '30.75%'; // we have subcategories, set width for grid of 3-up
    } else {
        $width = '16.9%'; // no kiddies, set width for grid of 5-up
    }

    $style = "
    /* WooCommerce Grid Customizations */
    @media screen and (min-width: 768px) {
        .woocommerce ul.products li.product,
        .woocommerce-page ul.products li.product {
            width: {$width};}
    }";

    wp_add_inline_style( 'our-custom-woo-grid-style', $style );
}

Let’s unpack that.

Instead of enqueuing a stylesheet from a CSS file somewhere in our plugin or theme, we’ve hooked a function to the wp_enqueue_styles event. Our style function will run at style-load time and will build the appropriate CSS and insert the CSS we generated on the fly by way of WordPress’s wp_add_inline_style() function.

Pretty cool stuff if you ask me.

Conclusion

With a little extra effort we can get a lot of extra mileage out of the way WooCommerce products can be displayed.

Happy coding!