Creating Custom WooCommerce Product Types: A Developer’s Guide

Ever had a client ask for something that WooCommerce just doesn’t offer out of the box? If you’re nodding your head, you’re not alone. Sometimes, the standard product types in WooCommerce just don’t cut it, and that’s where custom product types come into play. Today, I’m going to walk you through creating a custom WooCommerce product type—specifically, a “Membership” product that lets you set a start and end date. Let’s dive in!

Why Bother with Custom Product Types?

Before we jump into the code, let’s quickly talk about why you’d even want to create a custom product type. Maybe you’re dealing with products that have a specific duration, like a membership or subscription. Or perhaps you just want to make the checkout experience more tailored to what you’re selling. Whatever the reason, creating a custom product type gives you the flexibility to make WooCommerce work exactly the way you need it to.

Step 1: Register the Custom Product Type

First things first, we need to make sure our new “Membership” product type shows up in the WooCommerce product data dropdown. Here’s how you do that:

// Add new product type to the dropdown
function register_membership_product_type( $types ) {
    $types[ 'membership' ] = __( 'Membership' );
    return $types;
}
add_filter( 'product_type_selector', 'register_membership_product_type' );
PHP
What’s Happening Here?

This little snippet hooks into the product_type_selector filter and adds “Membership” as a new option in the product type dropdown.

Step 2: Create the Product Type Class

Now that WooCommerce knows our product type exists, we need to define what it actually does. We’ll do this by creating a new class that extends WooCommerce’s built-in WC_Product class.

class WC_Product_Membership extends WC_Product {

    public function __construct( $product ) {
        $this->product_type = 'membership';
        parent::__construct( $product );
    }

    // Additional functionality can be added here
}
PHP
What’s Happening Here?

This class is where the magic happens. By extending the WC_Product class, we’re telling WooCommerce that our “Membership” product type is just like a regular product, but with some extra features that we’ll add later.

Step 3: Load the Custom Product Class

We’ve got our custom product class; now let’s make sure WooCommerce knows when to use it.

function load_membership_product_class( $classname, $product_type ) {
    if ( $product_type == 'membership' ) {
        $classname = 'WC_Product_Membership';
    }
    return $classname;
}
add_filter( 'woocommerce_product_class', 'load_membership_product_class', 10, 2 );
PHP
What’s Happening Here?

This filter ensures that whenever a “Membership” product is selected, WooCommerce loads our custom class instead of the default product class.

Step 4: Add Custom Fields for Start and End Dates

Alright, here’s where things get interesting. Let’s add custom fields for the Membership start and end dates. These fields will appear in the product data section of the WooCommerce admin.

// Add custom fields to Membership product type
function membership_custom_fields() {
    global $post;

    echo '<div class="options_group">';
    
    woocommerce_wp_text_input( array(
        'id'          => '_membership_start_date',
        'label'       => __( 'Start Date', 'woocommerce' ),
        'placeholder' => 'YYYY-MM-DD',
        'description' => __( 'The date when the membership starts.', 'woocommerce' ),
        'type'        => 'date',
        'desc_tip'    => 'true'
    ));

    woocommerce_wp_text_input( array(
        'id'          => '_membership_expiration_date',
        'label'       => __( 'Expiration Date', 'woocommerce' ),
        'placeholder' => 'YYYY-MM-DD',
        'description' => __( 'The date when the membership expires.', 'woocommerce' ),
        'type'        => 'date',
        'desc_tip'    => 'true'
    ));

    echo '</div>';
}
add_action( 'woocommerce_product_options_general_product_data', 'membership_custom_fields' );

// Save custom fields values
function save_membership_custom_fields( $post_id ) {
    $start_date = isset( $_POST['_membership_start_date'] ) ? sanitize_text_field( $_POST['_membership_start_date'] ) : '';
    $expiration_date = isset( $_POST['_membership_expiration_date'] ) ? sanitize_text_field( $_POST['_membership_expiration_date'] ) : '';

    update_post_meta( $post_id, '_membership_start_date', $start_date );
    update_post_meta( $post_id, '_membership_expiration_date', $expiration_date );
}
add_action( 'woocommerce_process_product_meta', 'save_membership_custom_fields' );
PHP
What’s Happening Here?

These functions add two new fields “Start Date” and “Expiration Date” to the product data section when you select the “Membership” product type. They also save whatever data you input into those fields as post meta.

Step 5: Display the Dates on the Product Page

Finally, let’s make sure that the start and end dates are visible on the product page so your customers know when their membership starts and ends.

function display_membership_dates() {
    global $post;

    $product = wc_get_product( $post->ID );
    if ( $product->get_type() == 'membership' ) {
        $start_date = get_post_meta( $product->get_id(), '_membership_start_date', true );
        $expiration_date = get_post_meta( $product->get_id(), '_membership_expiration_date', true );
        
        if ( $start_date ) {
            echo '<p><strong>' . __( 'Membership Starts On:', 'woocommerce' ) . '</strong> ' . date_i18n( get_option( 'date_format' ), strtotime( $start_date ) ) . '</p>';
        }
        if ( $expiration_date ) {
            echo '<p><strong>' . __( 'Membership Expires On:', 'woocommerce' ) . '</strong> ' . date_i18n( get_option( 'date_format' ), strtotime( $expiration_date ) ) . '</p>';
        }
    }
}
add_action( 'woocommerce_single_product_summary', 'display_membership_dates', 20 );
PHP
What’s Happening Here?

This bit of code adds the start and end dates to the product page, making them visible to the customer. It’s a nice touch that adds transparency to what they’re buying.

Where Should You Add This Code?

You can drop all of this code into your theme’s functions.php file if you’re in a hurry. But I recommend creating a custom plugin for this, it keeps things tidy and makes it easier to maintain or reuse later.

Putting It All Together

To save you some scrolling, here’s the full code all in one place:

// Register custom product type
function register_membership_product_type( $types ) {
    $types[ 'membership' ] = __( 'Membership' );
    return $types;
}
add_filter( 'product_type_selector', 'register_membership_product_type' );

// Define custom product class
class WC_Product_Membership extends WC_Product {

    public function __construct( $product ) {
        $this->product_type = 'membership';
        parent::__construct( $product );
    }

    // Additional functionality can be added here
}

// Load custom product class
function load_membership_product_class( $classname, $product_type ) {
    if ( $product_type == 'membership' ) {
        $classname = 'WC_Product_Membership';
    }
    return $classname;
}
add_filter( 'woocommerce_product_class', 'load_membership_product_class', 10, 2 );

// Add custom fields to Membership product type
function membership_custom_fields() {
    global $post;

    echo '<div class="options_group">';
    
    woocommerce_wp_text_input( array(
        'id'          => '_membership_start_date',
        'label'       => __( 'Start Date', 'woocommerce' ),
        'placeholder' => 'YYYY-MM-DD',
        'description' => __( 'The date when the membership starts.', 'woocommerce' ),
        'type'        => 'date',
        'desc_tip'    => 'true'
    ));

    woocommerce_wp_text_input( array(
        'id'          => '_membership_expiration_date',
        'label'       => __( 'Expiration Date', 'woocommerce' ),
        'placeholder' => 'YYYY-MM-DD',
        'description' => __( 'The date when the membership expires.', 'woocommerce' ),
        'type'        => 'date',
        'desc_tip'    => 'true'
    ));

    echo '</div>';
}
add_action( 'woocommerce_product_options_general_product_data', 'membership_custom_fields' );

// Save custom fields values
function save_membership_custom_fields( $post_id ) {
    $start_date = isset( $_POST['_membership_start_date'] ) ? sanitize_text_field( $_POST['_membership_start_date'] ) : '';
    $expiration_date = isset( $_POST['_membership_expiration_date'] ) ? sanitize_text_field( $_POST['_membership_expiration_date'] ) : '';

    update_post_meta( $post_id, '_membership_start_date', $start_date );
    update_post_meta( $post_id, '_membership_expiration_date', $expiration_date );
}
add_action( 'woocommerce_process_product_meta', 'save_membership_custom_fields' );

// Display custom fields on the product page
function display_membership_dates() {
    global $post;

    $product = wc_get_product( $post->ID );
    if ( $product->get_type() == 'membership' ) {
        $start_date = get_post_meta( $product->get_id(), '_membership_start_date', true );
        $expiration_date = get_post_meta( $product->get_id(), '_membership_expiration_date', true );
        
        if ( $start_date ) {
            echo '<p><strong>' . __( 'Membership Starts On:', 'woocommerce' ) . '</strong> ' . date_i18n( get_option( 'date_format' ), strtotime( $start_date ) ) . '</p>';
        }
        if ( $expiration_date ) {
            echo '<p><strong>' . __( 'Membership Expires On:', 'woocommerce' ) . '</strong> ' . date_i18n( get_option( 'date_format' ), strtotime( $expiration_date ) ) . '</p>';
        }
    }
}
add_action( 'woocommerce_single_product_summary', 'display_membership_dates', 20 );
PHP
Expand

Conclusion

By creating a custom WooCommerce product type, you can tailor your online store to meet specific needs. The steps we covered allow you to add a “Membership” product type with custom start and end dates. This flexibility is one of WooCommerce’s greatest strengths, enabling you to build unique eCommerce experiences that go beyond the basics.

Happy coding! If you have any questions or need further help, feel free to leave a comment or reach out.

Leave a Reply