# WooCommerce in Oxygen

_Update:_ This manual method is no longer necessary in Oxygen. Use Oxygen’s WooCommerce addon instead. This tutorial has been archived.

As of v2.3, there is no built-in integration of WooCommerce in Oxygen. But that doesn’t mean we can not build a ecommerce site in Oxygen using WooCommerce today.

This article covers

  * creating the templates for 
    1. Shop page
    2. Product Taxonomy (Category and Tag) Archives
    3. Single Products
    4. Search Results pages
  * modifying the WooCommerce CSS
  * setting up a AJAX product search box
  * adding cart data in the nav menu
  * modifying the template PHP code using hooks
  * adding a Page for showing the order tracking



Screenshots:

**Shop page w/o sidebar showing 4 products per row** :

Shop page with a sidebar showing 3 products per row:

**Single product page** :

**Ajax product search** :

**Shopping cart data and button in the navigation** :

Site members with Lifetime Access can download export file of the full demo site which can be restored with just a few clicks. 

Non-members can purchase access to the site export here for just $10.

The export does not include a copy of Oxygen. After restoring, you would need to upload and activate Oxygen.

Download details below (visible to users with access):

### Step 1 – WooCommerce Installation

Install and activate WooCommerce.

Run the Setup Wizard. This should add these necessary Pages: Shop, Cart, Checkout and My account.

Add your products.

### Step 2 – Main Shop page

Oxygen > Templates > Add New Template.

Title: Shop

Set it to inherit from your Main Catch All template having the sitewide header and footer.

Set it to apply to `product` post type archive.

Set a priority of 1 or higher depending on the priority set for your generic sitewide archives’ Template.

Ensure that the slug for this Template is not `shop`.

To see the current auto-generated slug, pull down Screen Options in the top right, enable visibility of Slug meta box, scroll down and see.

If it it something like `shop-page`, leave it as is. Otherwise, change it to `shop-page`.

### Without sidebar

Edit the template with Oxygen.

Note: If you do not need/want to make any changes to the default WooCommerce template code, you could simply add an Inner Content element instead of the Section + Code Block as mentioned in the next series of steps below. For most people, it will be fine. 

See this video by Louis.

This applies to all the templates except the Search Results one.

Add a **Section**.

Add a Code Block element inside the Section. Set its Width to 100%.

PHP & HTML:
    
    
    <?php
    
    /**
     * Hook: woocommerce_before_main_content.
     *
     * @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content)
     * @hooked woocommerce_breadcrumb - 20
     * @hooked WC_Structured_Data::generate_website_data() - 30
     */
    // this outputs the breadcrumb.
    do_action( 'woocommerce_before_main_content' );
    
    ?>
    <header class="woocommerce-products-header">
        <?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
            <h1 class="woocommerce-products-header__title page-title"><?php woocommerce_page_title(); ?></h1>
        <?php endif; ?>
    
        <?php
        /**
         * Hook: woocommerce_archive_description.
         *
         * @hooked woocommerce_taxonomy_archive_description - 10
         * @hooked woocommerce_product_archive_description - 10
         */
        do_action( 'woocommerce_archive_description' );
        ?>
    </header>
    <?php
    if ( woocommerce_product_loop() ) {
    
        /**
         * Hook: woocommerce_before_shop_loop.
         *
         * @hooked wc_print_notices - 10
         * @hooked woocommerce_result_count - 20
         * @hooked woocommerce_catalog_ordering - 30
         */
        do_action( 'woocommerce_before_shop_loop' );
    
        woocommerce_product_loop_start();
    
        if ( wc_get_loop_prop( 'total' ) ) {
            while ( have_posts() ) {
                the_post();
    
                /**
                 * Hook: woocommerce_shop_loop.
                 *
                 * @hooked WC_Structured_Data::generate_product_data() - 10
                 */
                do_action( 'woocommerce_shop_loop' );
    
                wc_get_template_part( 'content', 'product' );
            }
        }
    
        woocommerce_product_loop_end();
    
        /**
         * Hook: woocommerce_after_shop_loop.
         *
         * @hooked woocommerce_pagination - 10
         */
        do_action( 'woocommerce_after_shop_loop' );
    } else {
        /**
         * Hook: woocommerce_no_products_found.
         *
         * @hooked wc_no_products_found - 10
         */
        do_action( 'woocommerce_no_products_found' );
    }
    
    /**
     * Hook: woocommerce_after_main_content.
     *
     * @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content)
     */
    do_action( 'woocommerce_after_main_content' );
    
    ?>
    

### With sidebar

Note: Instead of using a plugin to register a custom sidebar and adding widgets inside it, you could simply add your desired widgets via + Add > WordPress > Widget element. 

I personally prefer the method below and like managing widgets at Appearance > Widgets.

Install and activate WooSidebars plugin.

Go to Appearance > Widget Areas and create a new Widget Area named say, “Shop”.

Go to Appearance > Widgets and drag in your desired widgets into the Shop widget area.

Edit the Shop Template with Oxygen.

Add a **Section**.

Add a **Columns** element having 2 columns.

Set the Width of Left Div column to say, 30%. This should automatically set the Right Div column to 70%.

Add a class of say, `sidebar` to this Left Div. We will use this class later for styling the widgets.

Set Horizontal Item Alignment to Stretch.

Add the Shop **sidebar** inside the Left Div by going to +Add > WordPress > Sidebars.

Add a **Code Block** element in the Right Div. Set its Width to 100%.

PHP & HTML:
    
    
    <?php
    
    /**
     * Hook: woocommerce_before_main_content.
     *
     * @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content)
     * @hooked woocommerce_breadcrumb - 20
     * @hooked WC_Structured_Data::generate_website_data() - 30
     */
    // this outputs the breadcrumb.
    do_action( 'woocommerce_before_main_content' );
    
    ?>
    <header class="woocommerce-products-header">
        <?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
            <h1 class="woocommerce-products-header__title page-title"><?php woocommerce_page_title(); ?></h1>
        <?php endif; ?>
    
        <?php
        /**
         * Hook: woocommerce_archive_description.
         *
         * @hooked woocommerce_taxonomy_archive_description - 10
         * @hooked woocommerce_product_archive_description - 10
         */
        do_action( 'woocommerce_archive_description' );
        ?>
    </header>
    <?php
    if ( woocommerce_product_loop() ) {
    
        /**
         * Hook: woocommerce_before_shop_loop.
         *
         * @hooked wc_print_notices - 10
         * @hooked woocommerce_result_count - 20
         * @hooked woocommerce_catalog_ordering - 30
         */
        do_action( 'woocommerce_before_shop_loop' );
    
        woocommerce_product_loop_start();
    
        if ( wc_get_loop_prop( 'total' ) ) {
            while ( have_posts() ) {
                the_post();
    
                /**
                 * Hook: woocommerce_shop_loop.
                 *
                 * @hooked WC_Structured_Data::generate_product_data() - 10
                 */
                do_action( 'woocommerce_shop_loop' );
    
                wc_get_template_part( 'content', 'product' );
            }
        }
    
        woocommerce_product_loop_end();
    
        /**
         * Hook: woocommerce_after_shop_loop.
         *
         * @hooked woocommerce_pagination - 10
         */
        do_action( 'woocommerce_after_shop_loop' );
    } else {
        /**
         * Hook: woocommerce_no_products_found.
         *
         * @hooked wc_no_products_found - 10
         */
        do_action( 'woocommerce_no_products_found' );
    }
    
    /**
     * Hook: woocommerce_after_main_content.
     *
     * @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content)
     */
    do_action( 'woocommerce_after_main_content' );
    
    ?>
    

CSS:
    
    
    .sidebar .widget {
        list-style-type: none;
    }
    
    .sidebar .widget:not(:last-child) {
        margin-bottom: 60px;
    }
    
    .sidebar .widget h2 {
        font-size: 20px;
        margin-bottom: 1em;
    }
    

By default each Div column in a Columns element will have 20px padding. That means there’s a horizontal space of 40px between the two columns.

If you want to increase this, set a right padding of say 40px to the left Div and a left padding of 40px to the right Div.

From 992px and below the two columns will appear one below the other.

You will likely want to have the sidebar appear below the content. As such, go to 992px media query and add this in the Custom CSS:
    
    
    order: 1;
    

Below the title of the Shop page, that Page’s content – if present will appear. Therefore, if you want to add some intro paragraph, go to Pages and add your desired content for the Shop Page.

If you would like to change the number of products per row from the default 4 to say 3, add the following Code Snippet:

Title: Change number or products per row to 3
    
    
    /**
     * 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
        }
    }
    

### Step 3 – Product Category and Tag Archives

Oxygen > Templates > Add New Template.

Title: Product Taxonomy (Category and Tag) Archives

Set it to inherit from your Main Catch All template having the sitewide header and footer.

Set it to apply to `All product categories` and `All product tags` taxonomies.

Set a priority of 1 or higher depending on the priority set for your generic sitewide archives’ Template.

Edit with Oxygen.

Add a **Section** and inside that, a **Code Block**. Set its Width to 100%.

PHP & HTML:
    
    
    <?php
    /**
     * Hook: woocommerce_before_main_content.
     *
     * @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content)
     * @hooked woocommerce_breadcrumb - 20
     * @hooked WC_Structured_Data::generate_website_data() - 30
     */
    do_action( 'woocommerce_before_main_content' );
    
    ?>
    <header class="woocommerce-products-header">
        <?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
            <h1 class="woocommerce-products-header__title page-title"><?php woocommerce_page_title(); ?></h1>
        <?php endif; ?>
    
        <?php
        /**
         * Hook: woocommerce_archive_description.
         *
         * @hooked woocommerce_taxonomy_archive_description - 10
         * @hooked woocommerce_product_archive_description - 10
         */
        do_action( 'woocommerce_archive_description' );
        ?>
    </header>
    <?php
    if ( woocommerce_product_loop() ) {
    
        /**
         * Hook: woocommerce_before_shop_loop.
         *
         * @hooked wc_print_notices - 10
         * @hooked woocommerce_result_count - 20
         * @hooked woocommerce_catalog_ordering - 30
         */
        do_action( 'woocommerce_before_shop_loop' );
    
        woocommerce_product_loop_start();
    
        if ( wc_get_loop_prop( 'total' ) ) {
            while ( have_posts() ) {
                the_post();
    
                /**
                 * Hook: woocommerce_shop_loop.
                 *
                 * @hooked WC_Structured_Data::generate_product_data() - 10
                 */
                do_action( 'woocommerce_shop_loop' );
    
                wc_get_template_part( 'content', 'product' );
            }
        }
    
        woocommerce_product_loop_end();
    
        /**
         * Hook: woocommerce_after_shop_loop.
         *
         * @hooked woocommerce_pagination - 10
         */
        do_action( 'woocommerce_after_shop_loop' );
    } else {
        /**
         * Hook: woocommerce_no_products_found.
         *
         * @hooked wc_no_products_found - 10
         */
        do_action( 'woocommerce_no_products_found' );
    }
    
    /**
     * Hook: woocommerce_after_main_content.
     *
     * @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content)
     */
    do_action( 'woocommerce_after_main_content' );
    ?>
    

### Step 4 – Single Product pages

Oxygen > Templates > Add New Template.

Title: Single Products

Set it to inherit from your Main Catch All template having the sitewide header and footer.

Set it to apply to Singular Products.

Set a priority of 1 or higher depending on the priority set for your generic sitewide single Template.

Edit with Oxygen.

Add a **Section** and inside that, a **Code Block**. Set its Width to 100%.

PHP & HTML:
    
    
    <?php
        /**
            * woocommerce_before_main_content hook.
            *
            * @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content)
            * @hooked woocommerce_breadcrumb - 20
            */
        do_action( 'woocommerce_before_main_content' );
    ?>
    
        <?php while ( have_posts() ) : the_post(); ?>
    
            <?php wc_get_template_part( 'content', 'single-product' ); ?>
    
        <?php endwhile; // end of the loop. ?>
    
    <?php
        /**
            * woocommerce_after_main_content hook.
            *
            * @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content)
            */
        do_action( 'woocommerce_after_main_content' );
    ?>
    

You still with me? Good `cos we are not done yet. Refill that coffee mug!

### Step 5 – Search Results pages

Next, we shall add an Oxygen Template that will handle search results pages – “regular” content like posts as well as the products search.

For this, we need to do some prep work.

**a)** Install and activate Code Snippets plugin.

**b)** Add a Code Snippet named say, “Oxygen Template Shortcode” having:
    
    
    add_shortcode( 'oxygen-template', 'func_oxygen_template' );
    /**
     * Add a custom shortcode for displaying a Oxygen template/reusable part.
     *
     * Sample usage: [oxygen-template id="478"]
     *
     * @param array $atts Shortcode attributes.
     * @return string HTML output of the specified Oxygen template/reusable part.
     */
    function func_oxygen_template( $atts ) {
    
        return do_shortcode( get_post_meta( $atts['id'], 'ct_builder_shortcodes', true ) );
    
    }
    

**c)** Add another Code Snippet named say, “Use HTML5 markup” having:
    
    
    add_theme_support( 'html5', array( 'comment-list', 'comment-form', 'search-form', 'gallery', 'caption' ) );
    

**d)** Oxygen > Templates > Add New Reusable Part.

Give it a title of say, “Search Results Reusable Part” and edit with Oxygen.

Add a **Heading** having this text:
    
    
    Search results:  
    

Add an **Easy Posts** element.

Add another Heading having the text “Make another search:”. You might want to set it as a h3.

Add a Search Form by going to + Add > WordPress > Search Form.

Save and go back to Admin.

Make a note of the ID of the reusable part in the URL.

Ex.: if the URL is https://wc.wpdd.site/wp-admin/post.php?post=116&action=edit, the ID is 116.

**e)** Oxygen > Templates > Add New Template.

Title: Search Results

Set it to inherit from your Main Catch All template having the sitewide header and footer.

Set it to apply to Search Results under “Other”.

Set a priority of 1 or higher depending on the priority set for your generic sitewide Template for archives.

Edit with Oxygen.

Add a **Section** and inside that, a **Code Block**. Set its Width to 100%.

PHP & HTML:
    
    
    <?php
    
    if ( is_search() ) { // regular WP search
        echo do_shortcode('[oxygen-template id="116"]');
    } else { // product search
        /**
         * Hook: woocommerce_before_main_content.
         *
         * @hooked woocommerce_output_content_wrapper - 10 (outputs opening divs for the content)
         * @hooked woocommerce_breadcrumb - 20
         * @hooked WC_Structured_Data::generate_website_data() - 30
         */
        // this outputs the breadcrumb.
        do_action( 'woocommerce_before_main_content' );
    
        ?>
        <header class="woocommerce-products-header">
            <?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
                <h1 class="woocommerce-products-header__title page-title"><?php woocommerce_page_title(); ?></h1>
            <?php endif; ?>
    
            <?php
            /**
             * Hook: woocommerce_archive_description.
             *
             * @hooked woocommerce_taxonomy_archive_description - 10
             * @hooked woocommerce_product_archive_description - 10
             */
            do_action( 'woocommerce_archive_description' );
            ?>
        </header>
        <?php
        if ( woocommerce_product_loop() ) {
    
            /**
             * Hook: woocommerce_before_shop_loop.
             *
             * @hooked wc_print_notices - 10
             * @hooked woocommerce_result_count - 20
             * @hooked woocommerce_catalog_ordering - 30
             */
            do_action( 'woocommerce_before_shop_loop' );
    
            woocommerce_product_loop_start();
    
            if ( wc_get_loop_prop( 'total' ) ) {
                while ( have_posts() ) {
                    the_post();
    
                    /**
                     * Hook: woocommerce_shop_loop.
                     *
                     * @hooked WC_Structured_Data::generate_product_data() - 10
                     */
                    do_action( 'woocommerce_shop_loop' );
    
                    wc_get_template_part( 'content', 'product' );
                }
            }
    
            woocommerce_product_loop_end();
    
            /**
             * Hook: woocommerce_after_shop_loop.
             *
             * @hooked woocommerce_pagination - 10
             */
            do_action( 'woocommerce_after_shop_loop' );
        } else {
            /**
             * Hook: woocommerce_no_products_found.
             *
             * @hooked wc_no_products_found - 10
             */
            do_action( 'woocommerce_no_products_found' );
        }
    
        /**
         * Hook: woocommerce_after_main_content.
         *
         * @hooked woocommerce_output_content_wrapper_end - 10 (outputs closing divs for the content)
         */
        do_action( 'woocommerce_after_main_content' );
    
        }
    ?>
    

### Step 6 – Modifying the WooCommerce CSS

To provide an example of how to customize the CSS in WooCommerce, let’s take the “Sale” labels that are positioned absolutely to the top right of product images on the Shop page and product taxonomy archives.

Let us make these rectangular like this:

Inspecting one of these elements, we see the following CSS coming from WooCommerce:

Here is the CSS we need to add:
    
    
    .woocommerce ul.products li.product .onsale {
        line-height: 1;
        min-width: 0;
        min-height: 0;
        border-radius: 0;
        padding: 7px;
    }
    
    html .woocommerce ul.products li.product .onsale {
        margin: -1em -1.5em 0 0;
    }
    

The reason for having to use `html .woocommerce ul.products li.product .onsale` instead of `.woocommerce ul.products li.product .onsale` for the margin rule is because WooCommerce already sets the margin for `.woocommerce ul.products li.product .onsale` and we need to use a more specific selector to trump it.

You may be tempted to use !important (this is the Donald Trump way of trumping – don’t do this) like so:
    
    
    margin: -1em -1.5em 0 0 !important;
    

for `.woocommerce ul.products li.product .onsale` but it is not recommended.

As to where the CSS should be added, in the Oxygen editor go to Manage > Stylesheets. Add a “Folder” by your name or the project name. Select that and add a Stylesheet named say, `woocommerce` and add WooCommerce specific CSS in there.

### Step 7 – AJAX Product Search

To dynamically display products that match the search terms, install and activate AJAX Search for WooCommerce plugin.

Go to WooCommerce > AJAX search form > Form (tab).

Tick “Show product image” and “Show price”.

Save Changes.

Edit your Main Template’s Header (or any other location where you would like to insert the product search box) and add a Shortcode element having this shortcode:
    
    
    [wcas-search-form]
    

### Step 8 – Cart in Nav

To display a cart icon along with the number of items and the total cart price in the navigation menu, install and activate WooCommerce Menu Cart plugin.

Go to WooCommerce > Menu Cart Setup.

Select the menu in which you want to display the cart info and save changes.

### Step 9 – Customizing the WooCommerce Templates

Using hooks and filters is the recommended method to customize the structure or output of WooCommerce as it involves less maintenance as compared to overriding the template files.

As an example, let’s move the excerpt above the price on single product pages.

Before:

After:

Here’s an easy way to do this:

a) Install and activate Simply Show Hooks plugin.

b) Go to any single product page on the frontend and in the WordPress toolbar, click on Simply Show Hooks > Show Action Hooks.

Hooks (think of these as specific locations inside the code) that have “actions” have dark green background and the red number indicates the number of actions hooked to that location.

The name of the hook that is immediately above the price and excerpt (the elements we are interested in rearranging) is `woocommerce_single_product_summary`.

When we hover on it, we can see

As we can see, `woocommerce_template_single_price` is a function that is hooked to `woocommerce_single_product_summary` at a (default) priority of 10.

and `woocommerce_template_single_excerpt` is hooked at a _lower_ priority of 20.

Unlike the priority numbers in Oxygen Templates when it comes to WordPress, the smaller the number, the higher the priority.

Highest priority hooks run first.

So, in this case, woocommerce_template_single_price() runs or gets executed first and gets “printed” on the page before the woocommerce_template_single_excerpt().

To make the excerpt appear before the price all we have to do is reverse the priority numbers.

c) Add a new Code Snippet having a title of say, “Rearrange price and excerpt on single product pages” with this code:
    
    
    remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price' );
    remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_excerpt', 20 );
    
    add_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 20 );
    add_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_excerpt' );
    

With the above code we are first removing both the price and excerpt and then adding them but in the order we want.

Note that
    
    
    remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price' );
    

is the same as
    
    
    remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 10 );
    

and similarly,
    
    
    add_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_excerpt' );
    

is the same as
    
    
    add_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_excerpt', 10 );
    

If the priority is omitted, it means it is 10 – the default.

`add_action` and `remove_action` are WordPress functions that take 3 parameters: The name of the hook, The name of the function, the priority.

wp-content/plugins/woocommerce/includes/wc-template-hooks.php is a handy file you might want to refer to.

### Step 10 – Order Tracking Page

If you want your customers to be able to pull up data on their purchases, you can create a Page having this shortcode:

`[woocommerce_order_tracking]`

====

Phew! If you have made it this far, congratulations.

WooCommerce is an ocean and this is just the beginning.

#### References:

https://wpdevdesign.com/how-to-add-a-link-to-templates-on-oxygen-template-edit-screens/

https://wpdevdesign.com/how-to-add-templates-admin-bar-menu-item-in-wordpress/

https://stackoverflow.com/a/26299614/778809

https://wpdevdesign.com/shortcode-for-displaying-oxygen-templates-and-reusable-parts/

https://wpdevdesign.com/how-to-create-a-search-results-page-in-oxygen/