Product listing page

Author: Dawid Adach

-

Shop page

Now you know how to adjust WooCommerce templates to customize your shop. In this tutorial, we will learn how to style our main shop page (/shop/) which lists all of our products. Since you know how to edit templates you can achieve this by adjusting archive-product.php. Note: if you want to use that you have to remove the woocommerce.php file from your theme directory. Otherwise, the template will be ignored. This is one of the exceptions in WC described in the WC docs.

Warning

If your theme has a woocommerce.php file, you will be unable to override the woocommerce/archive-product.php custom template in your theme, as woocommerce.php has priority over other template files. This is intended to prevent display issues.

However, I want to show you the other way to achieve the same effect. In this scenario, we will use woocommerce.php and write our custom functions to loop through the products. This is a more complex technique however it gives you total control over each and every element on the page.

  1. Update the existing code of woocommerce.php with the following
  2. <?php  get_header(); 
    require_once('components/navbar.inc.php'); 
    if ( !is_shop()) {
         woocommerce_content();
    } else {
    ?>
    
    <!--Main Navigation-->
    <header>
    
        <!-- Intro -->
        <div class="card card-intro blue-gradient mb-4">
    
            <div class="card-body white-text rgba-black-light text-center pt-5 pb-4">
    
                <!--Grid row-->
                <div class="row d-flex justify-content-center">
    
                    <!--Grid column-->
                    <div class="col-md-6">
    
                        <h1 class="font-weight-bold mb-4">Website Name</h1>
                        <p class="lead mb-4">Lorem ipsum dolor sit amet consectetur adipisicing elit. Deleniti ad impedit corporis ratione facere?
                            Cupiditate unde aliquid reiciendis animi, quas inventore, praesentium neque voluptatem, iusto
                            perferendis placeat similique dolor eum?
                        </p>
    
                    </div>
                    <!--Grid column-->
    
                </div>
                <!--Grid row-->
    
            </div>
    
        </div>
        <!-- Intro -->
    
    </header>
    <!--Main Navigation-->
    
    <?php
    } // end else (if single-product)
    get_footer(); 
    ?>
        
  3. Visit the mydomain.com/shop/ page
  4. Product
  5. Adjust your heading and meta description
  6. You might notice that we have added an condition — it's required to make sure that the product page remains displayed correctly using templates.
  7. Now let's add a custom loop to our theme. Add following code below <header></header>
  8. <!--Main layout-->
    <main>
        <div class="container">
    
            <!--Section: Dynamic Content Wrapper-->
            <section>
                <div class="dynamic-content"></div>
            </section>
            <!--Section: Dynamic Content Wrapper-->
    
            <!--Section: Products-->
            <section class="text-center">
    
                <!--Navbar-->
                <nav class="navbar navbar-expand-lg navbar-dark info-color-dark mt-3 mb-5">
    
                    <!-- Navbar brand -->
                    <span class="navbar-brand">Categories:</span>
    
                    <!-- Collapse button -->
                    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#basicExampleNav" aria-controls="basicExampleNav"
                        aria-expanded="false" aria-label="Toggle navigation">
                        <span class="navbar-toggler-icon"></span>
                    </button>
    
                    <!-- Collapsible content -->
                    <div class="collapse navbar-collapse" id="basicExampleNav">
    
                        <!-- Links -->
                        <ul class="navbar-nav mr-auto">
                            <li class="nav-item active">
                                <a class="nav-link" href="#">All
                                    <span class="sr-only">(current)</span>
                                </a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link" href="#">Cat 1</a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link" href="#">Cat 2</a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link" href="#">Cat 3</a>
                            </li>
    
                        </ul>
                        <!-- Links -->
    
                        <form class="form-inline">
                            <div class="md-form mt-0">
                                <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
                            </div>
                            <button class="btn btn-outline-white btn-md my-2 my-sm-0 ml-3" type="submit"><i class="fa fa-search"></i></button>
                        </form>
                    </div>
                    <!-- Collapsible content -->
    
                </nav>
                <!--/.Navbar-->
    
                <!--Grid row-->
    
                <div class="row wow fadeIn">
                <?php
                    // Define custom query parameters
                    $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
                    $args = array(
                        'post_type' => 'product',
                        'posts_per_page' => 6,
                        'paged'          => $paged
                        );
                    $counter = 1;
                    $loop = new WP_Query( $args );
    
                    if ( $loop->have_posts() ) {
                        while ( $loop->have_posts() ) : $loop->the_post();
                ?>
                    <!--Grid column-->
                    <div class="col-lg-4 col-md-12 mb-4">
    
                        <!-- Product Card naked -->
                        <div class="card-naked">
    
                            <!--Featured image-->
                            <div class="view overlay hm-white-slight rounded mb-3">
                                <?php  
                                $image = wp_get_attachment_image_src( get_post_thumbnail_id( $loop->post->ID ), 'single-post-thumbnail' );
                                $regular_price = get_post_meta( get_the_ID(), '_regular_price', true);  
                                $sale_price = get_post_meta( get_the_ID(), '_sale_price', true);  
                                $terms = get_the_terms( $post->ID, 'product_cat' );
                                foreach ($terms as $term) {
                                    $product_cat_name = $term->name;
                                    $product_cat_id = $term->term_id;
                                    break;
                                }
                                ?>
                                <img src="<?php  echo $image[0]; ?>" class="img-fluid" data-id="<?php echo $loop->post->ID; ?>">
                                <a href ="<?php echo get_permalink() ?>">
                                    <div class="mask"></div>
                                </a>
                            </div>
    
                            <!--Content-->
                            <h6 class="mb-3">
                                <a href="<?php echo esc_url( get_term_link( $product_cat_id, 'product_cat' ) ); ?>">
                                    <span class="badge purple mr-1"><?php echo $product_cat_name ?></span>
                                </a>
                            </h6>
                            <h5 class="mb-3">
                                <strong><?php the_title() ?></strong>
                            </h5>
                            <p>
                                <?php if($sale_price) {
                                ?>
                                <span class="mr-1">
                                    <del><?php echo "$" . $regular_price; ?></del>
                                </span>
                                <?php
                                }
                                ?>
                                <span>
                            <?php 
                            echo "$";
                            echo  ($sale_price) ? $sale_price : $regular_price;
                            ?></span>
                            </p>
                            <a href="<?php echo get_permalink(wc_get_page_id( 'cart' ))  . "?add-to-cart=" .  get_the_ID() ; ?> " class="btn btn-info btn-sm" data-toggle="tooltip" data-placement="left" title="Add to cart">
                                <i class="fa fa-shopping-cart"></i>
                            </a>
                            <a href="<?php echo get_permalink() ?>" class="btn btn-info btn-sm">Details</a>
    
                        </div>
                        <!-- Product Card naked -->
    
                    </div>
                    <!--Grid column-->
                    <?php
                    if ($counter % 3 == 0) { 
                    ?>
                        </div>
                        <!--Grid row-->
                        <!--Grid dynamic row-->
                        <div class="row wow fadeIn">
                    <?php
                    }
                    $counter++;                  
                    endwhile;
                    } else {
                        echo __( 'No products found' );
                    }
                    // Custom query loop pagination
    
                    ?>
                </div>
                <!--Grid row-->
    
            </section>
            <!--Section: Products-->
    
        </div>
    </main>
    <!--Main layout-->
            
  9. Refresh the page:
  10. Products
  11. As you have noticed we have added both category and search placeholders within a bar (we will learn how to make them work in later lessons). But what is most important is that we have added a custom loop for our products.
  12. <?php
    // Define custom query parameters
    $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
    $args = array(
        'post_type' => 'product',
        'posts_per_page' => 6,
        'paged'          => $paged
        );
    
    $loop = new WP_Query( $args );
    $counter = 1;
    if ( $loop->have_posts() ) {
        while ( $loop->have_posts() ) : $loop->the_post();
    ?>
        
  13. We are looking for posts whose type is product and we limit our results to six per page. We have also used a $paged parameter which tells us which page we are currently browsing. We will learn more about that in later lessons about pagination. In the next lines we are fetching the product info:
  14. <?php  
    $image = wp_get_attachment_image_src( get_post_thumbnail_id( $loop->post->ID ), 'single-post-thumbnail' );
    $regular_price = get_post_meta( get_the_ID(), '_regular_price', true);  
    $sale_price = get_post_meta( get_the_ID(), '_sale_price', true);  
    $terms = get_the_terms( $post->ID, 'product_cat' );
    foreach ($terms as $term) {
        $product_cat_name = $term->name;
        $product_cat_id = $term->term_id;
        break;
    }
    ?>
        
  15. Like product image, regular and sale price as well as first category (product can be assigned to more than one category, in this scenario we are interested only in the first one). Then finally, like we did in our blog tutorial we are displaying every product in a loop using "naked" cards
  16. <!-- Product Card naked -->
    <div class="card-naked">
    
        <!--Featured image-->
        <div class="view overlay hm-white-slight rounded mb-3">
            <?php  
            $image = wp_get_attachment_image_src( get_post_thumbnail_id( $loop->post->ID ), 'single-post-thumbnail' );
            $regular_price = get_post_meta( get_the_ID(), '_regular_price', true);  
            $sale_price = get_post_meta( get_the_ID(), '_sale_price', true);  
            $terms = get_the_terms( $post->ID, 'product_cat' );
            foreach ($terms as $term) {
                $product_cat_name = $term->name;
                $product_cat_id = $term->term_id;
                break;
            }
            ?>
            <img src="<?php  echo $image[0]; ?>" class="img-fluid" data-id="<?php echo $loop->post->ID; ?>">
            <a href ="<?php echo get_permalink() ?>">
                <div class="mask"></div>
            </a>
        </div>
    
        <!--Content-->
        <h6 class="mb-3">
            <a href="<?php echo esc_url( get_term_link( $product_cat_id, 'product_cat' ) ); ?>">
                <span class="badge purple mr-1"><?php echo $product_cat_name ?></span>
            </a>
        </h6>
        <h5 class="mb-3">
            <strong><?php the_title() ?></strong>
        </h5>
        <p>
            <?php if($sale_price) {
            ?>
            <span class="mr-1">
                <del><?php echo "$" . $regular_price; ?></del>
            </span>
            <?php
            }
            ?>
            <span>
        <?php 
        echo "$";
        echo  ($sale_price) ? $sale_price : $regular_price;
        ?></span>
        </p>
        <a href="<?php echo get_permalink(wc_get_page_id( 'cart' ))  . "?add-to-cart=" .  get_the_ID() ; ?> " class="btn btn-info btn-sm" data-toggle="tooltip" data-placement="left" title="Add to cart">
            <i class="fa fa-shopping-cart"></i>
        </a>
        <a href="<?php echo get_permalink() ?>" class="btn btn-info btn-sm">Details</a>
    
    </div>
        
  17. Except for custom fields, like the price we are using default WP functions for posts like the_title(). You can learn more about them in the previous lessons on how to build WP Blog theme.
  18. One of the interesting parts to notice is that we are dynamically creating an Add to cart url:
  19. <a href="<?php echo get_permalink(wc_get_page_id( 'cart' ))  . "?add-to-cart=" .  get_the_ID() ; ?> " class="btn btn-info btn-sm" data-toggle="tooltip" data-placement="left" title="Add to cart">
        <i class="fa fa-shopping-cart"></i>
    </a>
        
  20. Also like we did in our Blog before, we have a function which adds new row every 3 columns checking the remainder (of division)
  21. if ($counter % 3 == 0) { 
    ?&gt;
        &lt;/div&gt;
        &lt;!--Grid row--&gt;
        &lt;!--Grid dynamic row--&gt;
        &lt;div class=&quot;row wow fadeIn&quot;&gt;
    &lt;?php
    }
        

Our product page is now working and fully operational. In future lessons we will learn how to use and add pagination to display more products.


Previous lesson Download Live preview

Spread the word:
Do you need help? Use our support forum

About the author

Dawid Adach
For more than 5 years Dawid worked as an IT Consultant specializing in SOA/EAI/ESB in the banking domain. He gained experience working in countries like Netherlands, Belgium, Poland and India developing enterprise-class systems for the most prestigious companies. Since co-founding mdbootstrap.com & brandflow.net in 2016 he has been using and teaching technologies such as Angular, TypeScript, PHP, AJAX, Mongo, SQL, Hadoop Stack, Virtualization, Automation and many others...