Product page

Author: Dawid Adach

-

Product page

Since you know how to use templates to adjust our theme, let's adjust our Product page:

1. content-single-product.php

  1. Copy the template of content-single-product.php and paste it into our theme
  2. Replace the code of the template with the following:
  3. <?php
    /**
     * The template for displaying product content in the single-product.php template
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/content-single-product.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see     https://docs.woocommerce.com/document/template-structure/
     * @package WooCommerce/Templates
     * @version 3.4.0
     */
    
    defined( 'ABSPATH' ) || exit;
    
    /**
     * Hook: woocommerce_before_single_product.
     *
     * @hooked wc_print_notices - 10
     */
    do_action( 'woocommerce_before_single_product' );
    
    if ( post_password_required() ) {
      echo get_the_password_form(); // WPCS: XSS ok.
      return;
    }
    ?>
    <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">
    
                      <?php the_title( '<h1 class="product_title entry-title font-weight-bold mb-4">', '</h1>' ); ?>
    
                    </div>
                    <!--Grid column-->
    
                </div>
                <!--Grid row-->
    
            </div>
    
        </div>
        <!-- Intro -->
    
    </header>
    
    <main>
        
        <div id="product-<?php the_ID(); ?>" <?php wc_product_class(); ?>>
    
            <div class="container dark-grey-text">
    
                <!--Grid row-->
                <div class="row wow fadeIn">
    
                    <!--Grid column-->
                    <div class="col-md-6 mb-4">
    
            <?php
              /**
               * Hook: woocommerce_before_single_product_summary.
               *
               * @hooked woocommerce_show_product_sale_flash - 10
               * @hooked woocommerce_show_product_images - 20
               */
              do_action( 'woocommerce_before_single_product_summary' );
            ?>
    
                    </div>
                    <!--Grid column-->
    
                    <!--Grid column-->
                    <div class="col-md-6 mb-4">
                         <!--Content-->
                        <div class="p-4">
    
                        <div class="summary entry-summary">
                <?php
                  /**
                   * Hook: woocommerce_single_product_summary.
                   *
                   * @hooked woocommerce_template_single_title - 5
                   * @hooked woocommerce_template_single_rating - 10
                   * @hooked woocommerce_template_single_price - 10
                   * @hooked woocommerce_template_single_excerpt - 20
                   * @hooked woocommerce_template_single_add_to_cart - 30
                   * @hooked woocommerce_template_single_meta - 40
                   * @hooked woocommerce_template_single_sharing - 50
                   * @hooked WC_Structured_Data::generate_product_data() - 60
                   */
                  do_action( 'woocommerce_single_product_summary' );
                ?>
              </div>
    
                        </div>
                        <!--Content-->
    
                    </div>
                    <!--Grid column-->
    
                </div>
                <!--Grid row-->
    
                <hr>
    
                <?php if (wc_get_related_products(get_the_ID())) {  ?>
    
                <!--Grid row-->
                <div class="row d-flex justify-content-center wow fadeIn">
    
                    <!--Grid column-->
                    <div class="col-md-6 text-center">
                        <h4 class="my-4 h4">Related products</h4>
                    </div>
                    <!--Grid column-->
    
                </div>
                <!--Grid row-->
    
                <!--Grid row-->
                <div class="row wow fadeIn">
    
          <?php
            /**
             * Hook: woocommerce_after_single_product_summary.
             *
             * @hooked woocommerce_output_product_data_tabs - 10
             * @hooked woocommerce_upsell_display - 15
             * @hooked woocommerce_output_related_products - 20
             */
            do_action( 'woocommerce_after_single_product_summary' );
          ?>
    
                </div>
                <!--Grid row-->
                <?php } ?>
    
        </div>
              <!--Container  -->
    
      </div>
        <!--Div product -->
    
    <?php do_action( 'woocommerce_after_single_product' ); ?>
    
    </main>
    
    
        
  4. Refresh the product page.
  5. You will notice that the layout of our page has changed. Here is the list of changes:

    • Added <header>...</header> including the product name (the_title())
    • Wrapped the content with <main></main>
    • Split layout using Bootstrap Grid into two columns (md-6 each). First (left) containing the product image (woocommerce_before_single_product_summary), and second (right) containing the product summary (woocommerce_single_product_summary)
    • Added a row below to display related products (woocommerce_after_single_product_summary)
  6. Now we have to adjust our hooks to hide or move certain elements:
    • Remove "Sale!" next to product picture.
    • Remove the product title (we have it already in the header).
    • Remove the tabs and leave related products only.
  7. We will achieve that by placing the following code into functions.php:
  8. 
    remove_action('woocommerce_before_single_product_summary', 'woocommerce_show_product_sale_flash', 10);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_title', 5);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_rating', 10);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_meta', 40);
    add_action('woocommerce_single_product_summary', 'woocommerce_template_single_meta', 5);
    remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_excerpt', 20);
    
    
    function woocommerce_template_product_description() {
      woocommerce_get_template( 'single-product/tabs/description.php' );
    }
    add_action( 'woocommerce_single_product_summary', 'woocommerce_template_product_description', 20 );
    
    remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_product_data_tabs', 10);
    
          

    We are done here, so let's move to the next template

2. single-product/product-image.php

  1. Copy the template into your theme.
  2. Update the code with the following:
  3. <?php
    /**
     * Single Product Image
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/product-image.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see     https://docs.woocommerce.com/document/template-structure/
     * @author  WooThemes
     * @package WooCommerce/Templates
     * @version 3.3.2
     */
    
    defined( 'ABSPATH' ) || exit;
    
    // Note: `wc_get_gallery_image_html` was added in WC 3.3.2 and did not exist prior. This check protects against theme overrides being used on older versions of WC.
    if ( ! function_exists( 'wc_get_gallery_image_html' ) ) {
      return;
    }
    
    global $product;
    
    $columns           = apply_filters( 'woocommerce_product_thumbnails_columns', 4 );
    $post_thumbnail_id = $product->get_image_id();
    $wrapper_classes   = apply_filters( 'woocommerce_single_product_image_gallery_classes', array(
      'woocommerce-product-gallery',
      'woocommerce-product-gallery--' . ( has_post_thumbnail() ? 'with-images' : 'without-images' ),
      'woocommerce-product-gallery--columns-' . absint( $columns ),
      'images',
    ) );
    ?>
    <div class="<?php echo esc_attr( implode( ' ', array_map( 'sanitize_html_class', $wrapper_classes ) ) ); ?>" data-columns="<?php echo esc_attr( $columns ); ?>" style="opacity: 0; transition: opacity .25s ease-in-out;">
      <figure class="woocommerce-product-gallery__wrapper">
        <?php
        if ( has_post_thumbnail() ) {
          $html  = wc_get_gallery_image_html( $post_thumbnail_id, true );
          $html = str_replace( 'class="wp-post-image"', 'class="wp-post-image img-fluid"' , $html );
    
    
        } else {
          $html  = '<div class="woocommerce-product-gallery__image--placeholder">';
          $html .= sprintf( '<img src="%s" alt="%s" class="wp-post-image " />', esc_url( wc_placeholder_img_src() ), esc_html__( 'Awaiting product image', 'woocommerce' ) );
          $html .= '</div>';
        }
    
        echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', $html, $post_thumbnail_id );
    
        //do_action( 'woocommerce_product_thumbnails' );
        ?>
      </figure>
    </div>
          

    Changes:

    • Added a img-fluid class using:
    • $html = str_replace( 'class="wp-post-image"', 'class="wp-post-image img-fluid"' , $html );
            
    • Commented thumbnails action:
    • //do_action( 'woocommerce_product_thumbnails' );
      
            

3. single-product/meta.php

  1. Copy the template into your theme.
  2. Update with the following code:
  3. <?php
    /**
     * Single Product Meta
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/meta.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see       https://docs.woocommerce.com/document/template-structure/
     * @author    WooThemes
     * @package   WooCommerce/Templates
     * @version     3.0.0
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
      exit;
    }
    
    global $product;
    ?>
    <div class="product_meta">
    
      <?php do_action( 'woocommerce_product_meta_start' ); ?>
    
      <?php if (false) : // wc_product_sku_enabled() && ( $product->get_sku() || $product->is_type( 'variable' ) ) ) : ?>
    
        <span class="sku_wrapper"><?php esc_html_e( 'SKU:', 'woocommerce' ); ?> <span class="sku"><?php echo ( $sku = $product->get_sku() ) ? $sku : esc_html__( 'N/A', 'woocommerce' ); ?></span></span>
    
      <?php endif; ?>
      <div class="mb-3">
      <?php echo wc_get_product_category_list( $product->get_id(), ', ', '<span class="badge purple white-text mr-1">' . _n( '', '', count( $product->get_category_ids() ), 'woocommerce' ) . ' ', '</span>' ); ?>
      </div>
      <?php echo wc_get_product_tag_list( $product->get_id(), ', ', '<span class="tagged_as">' . _n( 'Tag:', 'Tags:', count( $product->get_tag_ids() ), 'woocommerce' ) . ' ', '</span>' ); ?>
    
      <?php do_action( 'woocommerce_product_meta_end' ); ?>
    
    </div>
    
          

    Changes:

    • Disabled condition to disable SKU by setting it to false (you could also remove this part).
    • Added a surrounding <div></div> and class to <span></span> with category.

4. single-product/add-to-cart/simple.php

  1. Copy the template into your theme.
  2. Update with following code:
  3. <?php
    /**
     * Simple product add to cart
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/add-to-cart/simple.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see https://docs.woocommerce.com/document/template-structure/
     * @package WooCommerce/Templates
     * @version 3.4.0
     */
    
    defined( 'ABSPATH' ) || exit;
    
    global $product;
    
    if ( ! $product->is_purchasable() ) {
      return;
    }
    
    echo wc_get_stock_html( $product ); // WPCS: XSS ok.
    
    if ( $product->is_in_stock() ) : ?>
    
      <?php do_action( 'woocommerce_before_add_to_cart_form' ); ?>
    
      <form class="cart d-flex justify-content-left" action="<?php echo esc_url( apply_filters( 'woocommerce_add_to_cart_form_action', $product->get_permalink() ) ); ?>" method="post" enctype='multipart/form-data'>
        <?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
    
        <?php
        do_action( 'woocommerce_before_add_to_cart_quantity' );
    
        woocommerce_quantity_input( array(
          'min_value'   => apply_filters( 'woocommerce_quantity_input_min', $product->get_min_purchase_quantity(), $product ),
          'max_value'   => apply_filters( 'woocommerce_quantity_input_max', $product->get_max_purchase_quantity(), $product ),
          'input_value' => isset( $_POST['quantity'] ) ? wc_stock_amount( wp_unslash( $_POST['quantity'] ) ) : $product->get_min_purchase_quantity(), // WPCS: CSRF ok, input var ok.
        ) );
    
        do_action( 'woocommerce_after_add_to_cart_quantity' );
        ?>
    
        <button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="single_add_to_cart_button button alt btn btn-info btn-md my-0 p"><?php echo esc_html( $product->single_add_to_cart_text() ); ?> <i class="fa fa-shopping-cart ml-1"></i></button>
    
        <?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
      </form>
    
      <?php do_action( 'woocommerce_after_add_to_cart_form' ); ?>
    
    <?php endif; ?>
    
          

    Changes:

    • Added classes d-flex and justify-content-left to <form></form>
    • Updated an Add to cart button
    •     <button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="single_add_to_cart_button button alt btn btn-info btn-md my-0 p"><?php echo esc_html( $product->single_add_to_cart_text() ); ?> <i class="fa fa-shopping-cart ml-1"></i></button>
            

5. single-product/related.php

  1. Copy template into your theme.
  2. Update with the following code:
  3. <?php
    /**
     * Related Products
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/related.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see       https://docs.woocommerce.com/document/template-structure/
     * @author    WooThemes
     * @package   WooCommerce/Templates
     * @version     3.0.0
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
      exit;
    }
    
    if ( $related_products ) : ?>
    
      <section class="related products">
    
        <div class="row wow fadeIn">
        <?php woocommerce_product_loop_start(0); ?>
    
          <?php $counter = 1; ?>
    
            <?php foreach ( $related_products as $related_product ) : ?>
    
              <?php
                $post_object = get_post( $related_product->get_id() );
    
                setup_postdata( $GLOBALS['post'] =& $post_object ); ?>
                <?php wc_get_template_part( 'content', 'product-related' ); ?>
              
                <?php 
                if($counter==3) {
                  break;
                } 
                $counter++;
                ?>
    
            <?php endforeach; ?>
    
        <?php woocommerce_product_loop_end(); ?>
        </div>
      </section>
    
    <?php endif;
    
    wp_reset_postdata();
    
          
  4. Create a new file woocommerce/content-product-related.php and paste in the following code:
  5. <?php
    /**
     * The template for displaying product content within loops
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/content-product.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see     https://docs.woocommerce.com/document/template-structure/
     * @package WooCommerce/Templates
     * @version 3.4.0
     */
    
    defined( 'ABSPATH' ) || exit;
    
    global $product;
    
    // Ensure visibility.
    if ( empty( $product ) || ! $product->is_visible() ) {
      return;
    }
    ?>
    <div  <?php wc_product_class('col-lg-4 col-md-6 mb-4 text-center'); ?> >
      <?php
      /**
       * Hook: woocommerce_before_shop_loop_item.
       *
       * @hooked woocommerce_template_loop_product_link_open - 10
       */
      do_action( 'woocommerce_before_shop_loop_item' );
    
      /**
       * Hook: woocommerce_before_shop_loop_item_title.
       *
       * @hooked woocommerce_show_product_loop_sale_flash - 10
       * @hooked woocommerce_template_loop_product_thumbnail - 10
       */
      do_action( 'woocommerce_before_shop_loop_item_title' );
    
      /**
       * Hook: woocommerce_shop_loop_item_title.
       *
       * @hooked woocommerce_template_loop_product_title - 10
       */
      //do_action( 'woocommerce_shop_loop_item_title' );
    
      /**
       * Hook: woocommerce_after_shop_loop_item_title.
       *
       * @hooked woocommerce_template_loop_rating - 5
       * @hooked woocommerce_template_loop_price - 10
       */
      //do_action( 'woocommerce_after_shop_loop_item_title' );
    
      /**
       * Hook: woocommerce_after_shop_loop_item.
       *
       * @hooked woocommerce_template_loop_product_link_close - 5
       * @hooked woocommerce_template_loop_add_to_cart - 10
       */
      //do_action( 'woocommerce_after_shop_loop_item' );
      ?>
    </div>
    
          
  6. Add the following code to functions.php to unhook the Sale! notification:
  7. //content-product.php
    remove_action('woocommerce_before_shop_loop_item_title', 'woocommerce_show_product_loop_sale_flash', 10);
        

    Changes in single-product/related.php:

    • Wrapped with row
    • Added counter and break; option to the loop to show at most three related products.
    • Adjusted wc_get_templated_part to use the new template which we have just created (content-product-related.php)

    Changes in content-product-related.php, which is a modified copy of the template content-product.php

    • Added classes col-lg-4 col-md-6 mb-4 text-center to surrounding div
    • Commented out the: woocommerce_shop_loop_item_title, woocommerce_after_shop_loop_item_title and woocommerce_after_shop_loop_item hooks.

6. Minor changes:

  1. Copied single-product/price.php template and class lead to <p></p>
  2. Added the following class to style.css
  3. .white-text>a {
      color: white;
    }
        

Now as you know how to use templates and hooks let's adjust our product page.


Previous lesson Download Live preview Next lesson

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...