Asked  2 Months ago    Answers:  5   Viewed   3.6k times

I've been pulling my hair out with this all day, please forgive the short description, I just need to validate my sanity!!

As the title says, I'm trying to create two or three different single-product layouts within woocommerce. The minimum is trying to achieve would be to have multiple single-product folders each with their own name and configurations.

No matter which way I try to override the single-product.php and make this file use logic to check for the product_cat and give out templates accordingly, I either the page not loading or what I write is skipped over and the default is loaded.

So far I've been through the following methods multiple times, trying to piece together what may be outdated code or otherwise causing all the fuss:

WooCommerce - How to create multiple single product template based on category?

Woocommerce single product - template by categories

Creating a different template file for certain Product Categories - Wordpress/Woocommerce?

I was more hoping someone may know something about this that I'm obviously missing as there are many articles out there on what to try and most claim success but I'm unable to do so.

[Update] using template_include code from @helgatheviking

No success just yet but here's where I'm up to;

File structure team-shops is the category I'm trying to get

  • /mytheme/woocommerce/single-product.php - no changes
  • /mytheme/woocommerce/content-single-product.php
  • /mytheme/woocommerce/single-product-team-shops.php - changed line 37 to<?php wc_get_template_part( 'content', 'single-product-team-shops' ); ?>
  • /mytheme/woocommerce/content-single-product-team-shops.php - added additional id to #product-id (line 39)
  • /mytheme/woocommerce/single-product-team-shops/ folder with all single product files to change.

As I said above this isn't working but hopefully with what I've provided the problem may be more obvious.

Thanks again for any help :)

[Think I've got it]

Ok so I think I've something that works, at least for now it seems to, still have some further testing to do but any thoughts more than welcome, this is what I've got so far along with a single-product-team-shops folder in my theme

add_filter( 'woocommerce_locate_template', 'so_25789472_locate_template', 10, 3 );

function so_25789472_locate_template( $template, $template_name, $template_path ){

    $term_id = 2854;
    $taxonomy_name = 'product_cat';
    $term_children = get_term_children( $term_id, $taxonomy_name );

    foreach ( $term_children as $child ) {

    // on single posts with mock category and only for single-product/something.php templates
    if( is_product() && has_term( $child, 'product_cat' ) && strpos( $template_name, 'single-product/') !== false ){

        // replace single-product with single-product-mock in template name
        $mock_template_name = str_replace("single-product/", "single-product-team-shops/", $template_name );

        // look for templates in the single-product-mock/ folder
        $mock_template = locate_template(
            array(
                trailingslashit( $template_path ) . $mock_template_name,
                $mock_template_name
            )
        );

        // if found, replace template with that in the single-product-mock/ folder
        if ( $mock_template ) {
            $template = $mock_template;
        }
    }}

    return $template;
}

 Answers

39

Use a single-product-custom.php template for any product in the "custom" category:

add_filter( 'template_include', 'so_43621049_template_include' );

function so_43621049_template_include( $template ) {
  if ( is_singular('product') && (has_term( 'custom', 'product_cat')) ) {
    $template = get_stylesheet_directory() . '/woocommerce/single-product-custom.php';
  } 
  return $template;
}

NB: If you use the same action hooks in your single-product-custom.php template you will get the same look as the default single-product.php. You could 'rename' all the hooks and then could add existing functions (such as those for add to cart buttons, etc) to the new hooks in order to achieve a totally custom look.

Wednesday, August 18, 2021
 
Kai
answered 2 Months ago
Kai
21

There is probably more than one way to do this, but they all kind of turn around the idea of filtering the template before it is included.

You could totally skip WooCommerce's simple-product.php template (without needing to override that template) and go directly to simple-product-mock.php and create everything there. You'd do that by filtering template_include.

add_filter( 'template_include', 'so_25789472_template_include' );

function so_25789472_template_include( $template ) {
  if ( is_singular('product') && (has_term( 'mock', 'product_cat')) ) {
    $template = get_stylesheet_directory() . '/woocommerce/single-product-mock.php';
  } 
  return $template;
}

You could edit single-product-mock.php to call content-single-product-mock.php and hard-code that file. Nothing requires you to keep using Woo's hooks and functions. The point of them is just to make it easy for you to customize.

Or to be really tricky, you could duplicate templates such as single-product/title.php into a single-product-mock folder... ex: single-product-mock/title.php and then any time we're on a single product template in the mock category, we'll intercept calls to the single-product/something.php template and redirect them to single-product-mock/something.php if it exists and keep pointing to the single-product/something.php if it does not. We'll do this via the woocommerce_locate_template filter.

add_filter( 'woocommerce_locate_template', 'so_25789472_locate_template', 10, 3 );

function so_25789472_locate_template( $template, $template_name, $template_path ){

    // on single posts with mock category and only for single-product/something.php templates
    if( is_product() && has_term( 'mock', 'product_cat' ) && strpos( $template_name, 'single-product/') !== false ){

        // replace single-product with single-product-mock in template name
        $mock_template_name = str_replace("single-product/", "single-product-mock/", $template_name );

        // look for templates in the single-product-mock/ folder
        $mock_template = locate_template(
            array(
                trailingslashit( $template_path ) . $mock_template_name,
                $mock_template_name
            )
        );

        // if found, replace template with that in the single-product-mock/ folder
        if ( $mock_template ) {
            $template = $mock_template;
        }
    }

    return $template;
}

Update to filter wc_get_template instead.

/**
 * Change wc template part for product with a specific category
 *
 * @param  string $templates
 * @param  string $slug
 * @param  string $name
 * @return string
 */
function so_25789472_get_template_part( $template, $slug, $name ) {
  if ( $slug == 'content' && $name = 'single-product' && has_term( 'test', 'product_cat' ) ) {
    $template = locate_template( array( WC()->template_path() . 'content-single-product-test.php' ) );
  } 
  return $template;
}
add_filter( 'wc_get_template_part', 'so_25789472_get_template_part', 10, 3 );
Wednesday, March 31, 2021
 
Jauco
answered 7 Months ago
84

If you have already integrate WooCommerce in your theme, you might to override woocommerce_content() function.

It works perfectly on WooCommerce 2.5.5

  • Place you custom template files to your_theme/woocommerce/

  • Add code-snippet listed below to your theme functions.php:

/** 
 * Override 'woocommerce_content' function
 */

if ( ! function_exists( 'woocommerce_content' ) ) {

/**
 * Output WooCommerce content.
 *
 * This function is only used in the optional 'woocommerce.php' template.
 * which people can add to their themes to add basic woocommerce support.
 * without hooks or modifying core templates.
 *
 */
function woocommerce_content() {

    if ( is_singular( 'product' ) ) {

        while ( have_posts() ) : the_post();

            // Template depends from category slug
        
            if ( has_term( 'my-cat-slug', 'product_cat' ) ) {

              woocommerce_get_template_part( 'content', 'single-product-dogs' );  

            } else {

              woocommerce_get_template_part( 'content', 'single-product' ); 

            }

        endwhile;

    } else { ?>

        <?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>

            <h1 class="page-title"><?php woocommerce_page_title(); ?></h1>

        <?php endif; ?>

        <?php do_action( 'woocommerce_archive_description' ); ?>

        <?php if ( have_posts() ) : ?>

            <?php do_action('woocommerce_before_shop_loop'); ?>

            <?php woocommerce_product_loop_start(); ?>

                <?php woocommerce_product_subcategories(); ?>

                <?php while ( have_posts() ) : the_post(); ?>

                    <?php wc_get_template_part( 'content', 'product' ); ?>

                <?php endwhile; // end of the loop. ?>

            <?php woocommerce_product_loop_end(); ?>

            <?php do_action('woocommerce_after_shop_loop'); ?>

        <?php elseif ( ! woocommerce_product_subcategories( array( 'before' => woocommerce_product_loop_start( false ), 'after' => woocommerce_product_loop_end( false ) ) ) ) : ?>

            <?php wc_get_template( 'loop/no-products-found.php' ); ?>

        <?php endif;

    }
  }
}
Wednesday, March 31, 2021
 
CMOS
answered 7 Months ago
27

Here is the complete code that will display billing birthdate in checkout, in My account Addresses, In admin Orders pages and in WordPress user dashboard:

// Display Billing birthdate field to checkout and My account addresses
add_filter( 'woocommerce_billing_fields', 'display_birthdate_billing_field', 20, 1 );
function display_birthdate_billing_field($billing_fields) {

    $billing_fields['billing_birthdate'] = array(
        'type'        => 'date',
        'label'       => __('Birthdate'),
        'class'       => array('form-row-wide'),
        'priority'    => 25,
        'required'    => true,
        'clear'       => true,
    );
    return $billing_fields;
}

// Save Billing birthdate field value as user meta data
add_action( 'woocommerce_checkout_update_customer', 'save_account_billing_birthdate_field', 10, 2 );
function save_account_billing_birthdate_field( $customer, $data ){
    if ( isset($_POST['billing_birthdate']) && ! empty($_POST['billing_birthdate']) ) {
         $customer->update_meta_data( 'billing_birthdate', sanitize_text_field($_POST['billing_birthdate']) );
    }
}

// Admin orders Billing birthdate editable field and display
add_filter('woocommerce_admin_billing_fields', 'admin_order_billing_birthdate_editable_field');
function admin_order_billing_birthdate_editable_field( $fields ) {
    $fields['birthdate'] = array( 'label' => __('Birthdate', 'woocommerce') );

    return $fields;
}

// WordPress User: Add Billing birthdate editable field
add_filter('woocommerce_customer_meta_fields', 'wordpress_user_account_billing_birthdate_field');
function wordpress_user_account_billing_birthdate_field( $fields ) {
    $fields['billing']['fields']['billing_birthdate'] = array(
        'label'       => __('Birthdate', 'woocommerce'),
        'description' => __('', 'woocommerce')
    );
    return $fields;
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.

Saturday, May 29, 2021
 
PeanutsMcgee
answered 5 Months ago
29

If you look at wc-deprecated-functions.php you will see

/**
 * @deprecated
 */
function woocommerce_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique = false ) {
    return wc_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique );
}

Basically, the function was renamed to wc_add_order_item_meta(), so if you need the function then use that. The action hook was not renamed and remains in class-wc-checkout.php as:

// Allow plugins to add order item meta
do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
Tuesday, June 8, 2021
 
themihai
answered 5 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share