Product listing page

Author: Dawid Adach

-

Shop page

Now when 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 our products. Since you know how to edit templates you can achieve that adjusting archive-product.php. Note: if you want to use it you have to remove woocommerce.php file from your theme directory. Otherwise, template will be ignored. This is on of the exception in WC described in 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 also the other way to achieve the same. 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 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 condition - it's required to make sure that product page remains displayed correctly using templates.
  7. Now let's add 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 noticed we have added category and search placeholders within a bar (we will learn how to make them work in next lessons). But what the most important 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 which type is product and we limit our results to 6 per page. We have also used $paged parameter which tells us, which page we are currently browsing. We will learn more about that in next lessons about pagination. In next lines we are fetching 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 function for posts like the_title(). You can learn more about them in the previous course on how to build WP Blog theme.
  18. One of the interesting part to notice is that we are dynamically creating 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 function which adds new row every 3 columns checking remainder
  21. if ($counter % 3 == 0) { 
    ?>
        </div>
        <!--Grid row-->
        <!--Grid dynamic row-->
        <div class="row wow fadeIn">
    <?php
    }
        

Our product page is working and fully operation. In future lessons we will learn how to use 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...