• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » How to Hooks and Filters in Custom Post Types with Custom Single Page Templates for Seamless WooCommerce Integrations

How to Hooks and Filters in Custom Post Types with Custom Single Page Templates for Seamless WooCommerce Integrations

Leveraging Custom Post Types and Single Page Templates for WooCommerce Integration

Integrating custom content types with WooCommerce requires a nuanced understanding of WordPress hooks and template hierarchy. This guide details how to create custom post types (CPTs) that can seamlessly display WooCommerce product data, leveraging custom single-page templates and strategic hook usage for advanced customization. We’ll focus on scenarios where you need to display product information alongside other custom content, or present product details in a non-standard layout.

Registering a Custom Post Type for WooCommerce-Compatible Content

First, we need to register a custom post type. For this example, let’s create a ‘Book’ CPT. It’s crucial to ensure this CPT is compatible with WooCommerce’s product query system if you intend to display WooCommerce products within it, or vice-versa. While we won’t directly make this CPT a WooCommerce product, we’ll structure it to allow for data association.

The following PHP code, typically placed in your theme’s `functions.php` file or a custom plugin, registers the ‘book’ CPT. We’ll enable features relevant to content management.

add_action( 'init', 'register_book_cpt' );
function register_book_cpt() {
    $labels = array(
        'name'                  => _x( 'Books', 'Post Type General Name', 'text_domain' ),
        'singular_name'         => _x( 'Book', 'Post Type Singular Name', 'text_domain' ),
        'menu_name'             => __( 'Books', 'text_domain' ),
        'name_admin_bar'        => __( 'Book', 'text_domain' ),
        'archives'              => __( 'Book Archives', 'text_domain' ),
        'attributes'            => __( 'Book Attributes', 'text_domain' ),
        'parent_item_colon'     => __( 'Parent Book:', 'text_domain' ),
        'all_items'             => __( 'All Books', 'text_domain' ),
        'add_new_item'          => __( 'Add New Book', 'text_domain' ),
        'add_new'               => __( 'Add New', 'text_domain' ),
        'new_item'              => __( 'New Book', 'text_domain' ),
        'edit_item'             => __( 'Edit Book', 'text_domain' ),
        'update_item'           => __( 'Update Book', 'text_domain' ),
        'view_item'             => __( 'View Book', 'text_domain' ),
        'view_items'            => __( 'View Books', 'text_domain' ),
        'search_items'          => __( 'Search Book', 'text_domain' ),
        'not_found'             => __( 'Not found', 'text_domain' ),
        'not_found_in_trash'    => __( 'Not found in Trash', 'text_domain' ),
        'featured_image'        => __( 'Featured Image', 'text_domain' ),
        'set_featured_image'    => __( 'Set featured image', 'text_domain' ),
        'remove_featured_image' => __( 'Remove featured image', 'text_domain' ),
        'use_featured_image'    => __( 'Use as featured image', 'text_domain' ),
        'insert_into_item'      => __( 'Insert into book', 'text_domain' ),
        'uploaded_to_this_item' => __( 'Uploaded to this book', 'text_domain' ),
        'items_list'            => __( 'Books list', 'text_domain' ),
        'items_list_navigation' => __( 'Books list navigation', 'text_domain' ),
        'filter_items_list'     => __( 'Filter books list', 'text_domain' ),
    );
    $args = array(
        'label'                 => __( 'Book', 'text_domain' ),
        'description'           => __( 'Custom post type for books', 'text_domain' ),
        'labels'                => $labels,
        'supports'              => array( 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' ),
        'taxonomies'            => array( 'genre' ), // Example taxonomy
        'hierarchical'          => false,
        'public'                => true,
        'show_ui'               => true,
        'show_in_menu'          => true,
        'menu_position'         => 5,
        'menu_icon'             => 'dashicons-book-alt',
        'show_in_admin_bar'     => true,
        'show_in_nav_menus'     => true,
        'can_export'            => true,
        'has_archive'           => true,
        'exclude_from_search'   => false,
        'publicly_queryable'    => true,
        'capability_type'       => 'post',
        'rewrite'               => array( 'slug' => 'books' ), // Custom slug for permalinks
        'show_in_rest'          => true, // Enable REST API support
    );
    register_post_type( 'book', $args );
}

// Register a custom taxonomy for books
add_action( 'init', 'register_genre_taxonomy' );
function register_genre_taxonomy() {
    $labels = array(
        'name'              => _x( 'Genres', 'taxonomy general name', 'text_domain' ),
        'singular_name'     => _x( 'Genre', 'taxonomy singular name', 'text_domain' ),
        'search_items'      => __( 'Search Genres', 'text_domain' ),
        'all_items'         => __( 'All Genres', 'text_domain' ),
        'parent_item'       => __( 'Parent Genre', 'text_domain' ),
        'parent_item_colon' => __( 'Parent Genre:', 'text_domain' ),
        'edit_item'         => __( 'Edit Genre', 'text_domain' ),
        'update_item'       => __( 'Update Genre', 'text_domain' ),
        'add_new_item'      => __( 'Add New Genre', 'text_domain' ),
        'new_item_name'     => __( 'New Genre Name', 'text_domain' ),
        'menu_name'         => __( 'Genres', 'text_domain' ),
    );
    $args = array(
        'hierarchical'      => true,
        'labels'            => $labels,
        'show_ui'           => true,
        'show_admin_bar'    => true,
        'show_in_nav_menus' => true,
        'query_var'         => true,
        'rewrite'           => array( 'slug' => 'genre' ),
        'show_in_rest'      => true,
    );
    register_taxonomy( 'genre', array( 'book' ), $args );
}

Creating a Custom Single Page Template for the ‘Book’ CPT

To display books with a unique layout, we’ll create a custom template file. WordPress uses a specific naming convention to identify templates for custom post types. For our ‘book’ CPT, the template file should be named `single-book.php` and placed in your theme’s root directory.

This template will inherit the standard WordPress loop but allows for custom presentation of the book’s content. We can also use hooks within this template to inject dynamic content or modify the output.

<?php
/**
 * Template Name: Single Book
 * Template Post Type: book
 */

get_header(); ?>

<!-- wp:group -->
<div class="wp-block-group">
    <div class="wp-block-group__inner-container">

        <!-- wp:post-title -->
        <h1 class="wp-block-post-title"><?php the_title(); ?></h1>
        <!-- /wp:post-title -->

        <!-- wp:columns -->
        <div class="wp-block-columns">
            <div class="wp-block-column">
                <!-- wp:image -->
                <figure class="wp-block-image size-large"><img src="<?php echo get_the_post_thumbnail_url( get_the_ID(), 'large' ); ?>" alt="<?php echo esc_attr( get_the_title() ); ?>"></figure>
                <!-- /wp:image -->
            </div>
            <div class="wp-block-column">
                <!-- wp:post-content -->
                <div class="wp-block-post-content">
                    <?php
                    while ( have_posts() ) :
                        the_post();
                        the_content();
                    endwhile; // End of the loop.
                    ?>
                </div>
                <!-- /wp:post-content -->

                <!-- wp:paragraph -->
                <p>
                    <strong>Author:</strong> <?php echo esc_html( get_post_meta( get_the_ID(), '_book_author', true ) ); ?>
                </p>
                <!-- /wp:paragraph -->

                <!-- wp:paragraph -->
                <p>
                    <strong>ISBN:</strong> <?php echo esc_html( get_post_meta( get_the_ID(), '_book_isbn', true ) ); ?>
                </p>
                <!-- /wp:paragraph -->

                <!-- wp:post-terms -->
                <div class="wp-block-post-terms">
                    <?php
                    $terms = get_the_terms( get_the_ID(), 'genre' );
                    if ( $terms && ! is_wp_error( $terms ) ) :
                        echo '<strong>Genres:</strong> ';
                        $term_links = array();
                        foreach ( $terms as $term ) {
                            $term_links[] = '<a href="' . esc_url( get_term_link( $term ) ) . '">' . esc_html( $term->name ) . '</a>';
                        }
                        echo implode( ', ', $term_links );
                    endif;
                    ?>
                </div>
                <!-- /wp:post-terms -->

                <!-- wp:button -->
                <div class="wp-block-button"><a class="wp-block-button__link" href="#">Add to Cart</a></div>
                <!-- /wp:button -->

            </div>
        </div>
        <!-- /wp:columns -->

    </div>
</div>
<!-- /wp:group -->

<?php get_footer(); ?>

In this template:

  • We use the `Template Name` and `Template Post Type` directives to inform WordPress about this template’s purpose.
  • The standard WordPress loop (`while ( have_posts() ) : the_post(); … endwhile;`) is used to fetch and display the post content.
  • `the_title()`, `the_content()`, and `get_the_post_thumbnail_url()` are standard WordPress functions for displaying post elements.
  • We’ve added custom fields for ‘Author’ and ‘ISBN’ using `get_post_meta()`. These would typically be managed via a plugin like Advanced Custom Fields (ACF) or custom meta boxes.
  • The ‘genre’ taxonomy is displayed using `get_the_terms()`.
  • A placeholder “Add to Cart” button is included. This is where WooCommerce integration hooks will be essential.

Integrating WooCommerce Product Data and Functionality

The real power comes from integrating WooCommerce’s product data and functionality. We can achieve this by:

Associating Custom Post Types with WooCommerce Products

A common pattern is to link your custom post type entries to actual WooCommerce products. This allows you to manage product pricing, stock, and checkout functionality through WooCommerce while using your CPT for custom display logic. A robust way to do this is by using a custom field to store the associated WooCommerce Product ID.

// In functions.php or a custom plugin

// Add a field to the 'book' CPT to store the WooCommerce Product ID
add_action( 'show_user_profile', 'add_wc_product_id_field' );
add_action( 'edit_user_profile', 'add_wc_product_id_field' );
add_action( 'woocommerce_product_options_general_product_data', 'add_custom_product_id_field' );
add_action( 'woocommerce_process_product_meta', 'save_custom_product_id_field' );

function add_custom_product_id_field( $post ) {
    // For WooCommerce products
    woocommerce_wp_text_input(
        array(
            'id'          => '_associated_wc_product_id',
            'label'       => __( 'Associated Book ID', 'text_domain' ) . ' (' . __( 'optional', 'text_domain' ) . ')',
            'placeholder' => 'Enter the Book Post ID',
            'desc_tip'    => 'true',
            'description' => __( 'Enter the ID of the Book post to associate with this product.', 'text_domain' ),
            'type'        => 'number',
            'custom_attributes' => array(
                'step' => 'any',
                'min'  =>'0'
            )
        )
    );
}

function save_custom_product_id_field( $post_id ) {
    $wc_product_id = isset( $_POST['_associated_wc_product_id'] ) ? intval( $_POST['_associated_wc_product_id'] ) : '';

    if ( $wc_product_id ) {
        update_post_meta( $post_id, '_associated_wc_product_id', $wc_product_id );
        // Also update the book post to link back to this WC product
        update_post_meta( $wc_product_id, '_associated_wc_product_id', $post_id );
    } else {
        delete_post_meta( $post_id, '_associated_wc_product_id' );
        // If the book post was linked, unlink it
        $linked_book_id = get_post_meta( $post_id, '_associated_wc_product_id', true );
        if ( $linked_book_id ) {
            delete_post_meta( $linked_book_id, '_associated_wc_product_id' );
        }
    }
}

// Add a field to the 'book' CPT to store the WooCommerce Product ID
add_action( 'add_meta_boxes', 'add_book_meta_box' );
function add_book_meta_box() {
    add_meta_box(
        'book_wc_association',
        __( 'WooCommerce Product Association', 'text_domain' ),
        'render_book_meta_box',
        'book', // CPT slug
        'side', // Context (normal, side, advanced)
        'default' // Priority
    );
}

function render_book_meta_box( $post ) {
    wp_nonce_field( 'save_book_wc_association', 'book_wc_association_nonce' );
    $associated_product_id = get_post_meta( $post->ID, '_associated_wc_product_id', true );
    ?>
    <label for="associated_wc_product_id"><?php _e( 'Associate with WooCommerce Product ID:', 'text_domain' ); ?></label>
    <input type="number" id="associated_wc_product_id" name="associated_wc_product_id" value="<?php echo esc_attr( $associated_product_id ); ?>" min="1" step="1" />
    <p class="description"><?php _e( 'Enter the ID of the WooCommerce product to link this book to. Leave blank if not applicable.', 'text_domain' ); ?></p>
    



This code snippet adds meta boxes to both WooCommerce products and our 'book' CPT, allowing you to specify an association. When a WooCommerce product is linked to a book, we store the respective IDs in custom fields (`_associated_wc_product_id` and `_associated_book_id`). This bidirectional linking is crucial for many integration scenarios.

Modifying the Single Book Template to Display WooCommerce Data

Now, let's update `single-book.php` to fetch and display data from the associated WooCommerce product.

<?php
/**
 * Template Name: Single Book
 * Template Post Type: book
 */

get_header(); ?>

<!-- wp:group -->
<div class="wp-block-group">
    <div class="wp-block-group__inner-container">

        <!-- wp:post-title -->
        <h1 class="wp-block-post-title"><?php the_title(); ?></h1>
        <!-- /wp:post-title -->

        <!-- wp:columns -->
        <div class="wp-block-columns">
            <div class="wp-block-column">
                <!-- wp:image -->
                <figure class="wp-block-image size-large"><img src="<?php echo get_the_post_thumbnail_url( get_the_ID(), 'large' ); ?>" alt="<?php echo esc_attr( get_the_title() ); ?>"></figure>
                <!-- /wp:image -->
            </div>
            <div class="wp-block-column">
                <!-- wp:post-content -->
                <div class="wp-block-post-content">
                    <?php
                    while ( have_posts() ) :
                        the_post();
                        the_content();
                    endwhile; // End of the loop.
                    ?>
                </div>
                <!-- /wp:post-content -->

                <!-- wp:paragraph -->
                <p>
                    <strong>Author:</strong> <?php echo esc_html( get_post_meta( get_the_ID(), '_book_author', true ) ); ?>
                </p>
                <!-- /wp:paragraph -->

                <!-- wp:paragraph -->
                <p>
                    <strong>ISBN:</strong> <?php echo esc_html( get_post_meta( get_the_ID(), '_book_isbn', true ) ); ?>
                </p>
                <!-- /wp:paragraph -->

                <!-- wp:post-terms -->
                <div class="wp-block-post-terms">
                    <?php
                    $terms = get_the_terms( get_the_ID(), 'genre' );
                    if ( $terms && ! is_wp_error( $terms ) ) :
                        echo '<strong>Genres:</strong> ';
                        $term_links = array();
                        foreach ( $terms as $term ) {
                            $term_links[] = '<a href="' . esc_url( get_term_link( $term ) ) . '">' . esc_html( $term->name ) . '</a>';
                        }
                        echo implode( ', ', $term_links );
                    endif;
                    ?>
                </div>
                <!-- /wp:post-terms -->

                <!-- Dynamically display WooCommerce product data -->
                <?php
                $associated_product_id = get_post_meta( get_the_ID(), '_associated_wc_product_id', true );
                if ( $associated_product_id ) {
                    $product = wc_get_product( $associated_product_id );
                    if ( $product ) {
                        // Display Price
                        echo '<div class="book-price"><strong>Price:</strong> ' . $product->get_price_html() . '</div>';

                        // Display Add to Cart button
                        echo '<div class="book-add-to-cart">';
                        woocommerce_template_loop_add_to_cart( array( 'quantity' => 1 ) );
                        echo '</div>';

                        // Display Stock Status (optional)
                        if ( $product->is_in_stock() ) {
                            echo '<p class="book-stock"><strong>Availability:</strong> In Stock</p>';
                        } else {
                            echo '<p class="book-stock"><strong>Availability:</strong> Out of Stock</p>';
                        }
                    }
                }
                ?>

            </div>
        </div>
        <!-- /wp:columns -->

    </div>
</div>
<!-- /wp:group -->

<?php get_footer(); ?>

Key changes in the template:

  • We retrieve the `_associated_wc_product_id` from the current book post.
  • If an ID exists, we use `wc_get_product()` to instantiate the WooCommerce product object.
  • `$product->get_price_html()` displays the product's price in a formatted way.
  • `woocommerce_template_loop_add_to_cart()` is a WooCommerce function that renders the appropriate "Add to Cart" button (including quantity selector and variations if applicable).
  • We check stock status using `$product->is_in_stock()`.

Advanced Integration with WooCommerce Hooks

Beyond template modifications, hooks offer powerful ways to inject custom logic and data. For instance, you might want to:

Modifying the Add to Cart Behavior

Suppose you want to add specific book metadata (like author or ISBN) to the cart item data when a book is added. You can use the `woocommerce_add_cart_item_data` filter.

// In functions.php or a custom plugin
add_filter( 'woocommerce_add_cart_item_data', 'add_book_data_to_cart_item', 10, 3 );
function add_book_data_to_cart_item( $cart_item_data, $product_id, $variation_id ) {
    // Check if the product is associated with a book CPT
    $associated_book_id = get_post_meta( $product_id, '_associated_book_id', true );

    if ( $associated_book_id ) {
        $cart_item_data['book_author'] = get_post_meta( $associated_book_id, '_book_author', true );
        $cart_item_data['book_isbn']   = get_post_meta( $associated_book_id, '_book_isbn', true );
        $cart_item_data['is_book']     = true; // Flag to identify book items
    }
    return $cart_item_data;
}

// Display custom data in the cart
add_filter( 'woocommerce_get_item_data', 'display_book_data_in_cart', 10, 2 );
function display_book_data_in_cart( $item_data, $cart_item ) {
    if ( isset( $cart_item['is_book'] ) && $cart_item['is_book'] ) {
        if ( ! empty( $cart_item['book_author'] ) ) {
            $item_data[] = array(
                'key'     => __( 'Author', 'text_domain' ),
                'value'   => wc_clean( $cart_item['book_author'] ),
                'display' => '',
            );
        }
        if ( ! empty( $cart_item['book_isbn'] ) ) {
            $item_data[] = array(
                'key'     => __( 'ISBN', 'text_domain' ),
                'value'   => wc_clean( $cart_item['book_isbn'] ),
                'display' => '',
            );
        }
    }
    return $item_data;
}

// Save custom data to order items
add_action( 'woocommerce_checkout_create_order_line_item', 'save_book_data_to_order_item', 10, 4 );
function save_book_data_to_order_item( $item, $cart_item_key, $values, $order ) {
    if ( isset( $values['is_book'] ) && $values['is_book'] ) {
        if ( ! empty( $values['book_author'] ) ) {
            $item->add_meta_data( __( 'Author', 'text_domain' ), $values['book_author'] );
        }
        if ( ! empty( $values['book_isbn'] ) ) {
            $item->add_meta_data( __( 'ISBN', 'text_domain' ), $values['book_isbn'] );
        }
    }
}

This set of hooks ensures that when a book (associated with a WooCommerce product) is added to the cart, its author and ISBN are stored as custom cart item data. This data is then displayed in the cart and saved to the order item meta upon checkout.

Customizing the Single Product Page for Associated Books

If you want to display the book's custom details *on* the WooCommerce single product page when it's associated with a book, you can hook into WooCommerce's template actions.

// In functions.php or a custom plugin
add_action( 'woocommerce_single_product_summary', 'display_associated_book_details', 15 ); // Hook priority 15 to appear after price
function display_associated_book_details() {
    global $product;
    $product_id = $product->get_id();
    $associated_book_id = get_post_meta( $product_id, '_associated_book_id', true );

    if ( $associated_book_id ) {
        $book_author = get_post_meta( $associated_book_id, '_book_author', true );
        $book_isbn   = get_post_meta( $associated_book_id, '_book_isbn', true );
        $genres      = get_the_terms( $associated_book_id, 'genre' );

        if ( $book_author ) {
            echo '<div class="book-details-on-product-page"><strong>Author:</strong> ' . esc_html( $book_author ) . '</div>';
        }
        if ( $book_isbn ) {
            echo '<div class="book-details-on-product-page"><strong>ISBN:</strong> ' . esc_html( $book_isbn ) . '</div>';
        }
        if ( $genres && ! is_wp_error( $genres ) ) {
            echo '<div class="book-details-on-product-page"><strong>Genres:</strong> ';
            $genre_links = array();
            foreach ( $genres as $genre ) {
                $genre_links[] = '<a href="' . esc_url( get_term_link( $genre ) ) . '">' . esc_html( $genre->name ) . '</a>';
            }
            echo implode( ', ', $genre_links );
            echo '</div>';
        }
    }
}

This hook

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Web Session Persistence: PHP Sessions (Laravel/WordPress) vs. Ruby on Rails CookieStore Security Models
  • Templates Compilation: Blade Engines vs. ERB (Ruby) vs. Perl Template Toolkit render overhead
  • Background Task Workers: Laravel Horizon vs. Ruby Sidekiq Redis Engines vs. Perl Minion Worker Queues
  • Active Record Architectures: Eloquent (PHP) vs. ActiveRecord (Ruby) vs. Perl DBIx::Class Schema Performance
  • Optimizing CPU-Bound Logic: Writing Custom PHP C Extensions vs. Implementing Core PHP Optimizations

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (583)
  • DevOps (7)
  • DevOps & Cloud Scaling (956)
  • Django (1)
  • Laravel (2)
  • Migration & Architecture (192)
  • MySQL (1)
  • Performance & Optimization (783)
  • PHP (5)
  • PHP Development (12)
  • Plugins & Themes (244)
  • Programming Languages (1)
  • Python (3)
  • Ruby on Rails (1)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • Web Applications & Frontend (1)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (355)

Recent Posts

  • Web Session Persistence: PHP Sessions (Laravel/WordPress) vs. Ruby on Rails CookieStore Security Models
  • Templates Compilation: Blade Engines vs. ERB (Ruby) vs. Perl Template Toolkit render overhead
  • Background Task Workers: Laravel Horizon vs. Ruby Sidekiq Redis Engines vs. Perl Minion Worker Queues
  • Active Record Architectures: Eloquent (PHP) vs. ActiveRecord (Ruby) vs. Perl DBIx::Class Schema Performance
  • Optimizing CPU-Bound Logic: Writing Custom PHP C Extensions vs. Implementing Core PHP Optimizations
  • Inside Zend API: Direct Allocation and Manipulation of Zend Variables (zvals) and HashTables in C

Top Categories

  • DevOps & Cloud Scaling (956)
  • Performance & Optimization (783)
  • Debugging & Troubleshooting (583)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • School Management & Student Administration System
  • Integrated Hospital & Clinic Management System
  • Real Estate Directory & Agent Portal
  • Restaurant POS & Table Booking System
  • Retail Inventory POS & Billing System
  • Pharmacy Inventory & Clinic Billing System

Our Services

  • Vibe Engineering & AI Code Auditing Services
  • Prompt Engineering & "Vibe Coding" Workflow Consulting
  • AI-Augmented "Vibe Coding" & Rapid MVP Development
  • Figma to Shopify Liquid Theme Customization
  • Figma to WooCommerce Frontend Development
  • Figma to Magento 2 Theme Development

Copyright © 2026 · Vinay Vengala