Developers

Writing a Custom Product-Type Adapter

Published · Last updated

The plugin ships adapters for WooCommerce Product Bundles and Composite Products that enrich the AJAX stock payload with per-child / per-component data. You can add the same kind of support for any custom or third-party product type using a single filter — no core changes.

The extension point

ssap_stock_data_for_product filters the per-product stock payload before it’s returned in the AJAX response. It only runs when Refresh stock via AJAX is enabled.

  • Params: ( array $data, WC_Product $product )
  • Return: the (augmented) $data array.

Default keys in $data:

KeyTypeMeaning
availability_htmlstringWooCommerce’s rendered stock HTML
is_in_stockbool$product->is_in_stock()
is_purchasablebool$product->is_purchasable()
add_to_cart_htmlstringRendered loop Add-to-Cart markup

Minimal example

add_filter( 'ssap_stock_data_for_product', function ( $data, $product ) {
    $data['my_field'] = (bool) get_post_meta( $product->get_id(), '_my_flag', true );
    return $data;
}, 10, 2 );

A proper adapter (mirroring the built-ins)

Self-registering, and a no-op unless the target product type/plugin is present:

class My_SSAP_Adapter {

    public static function maybe_register() {
        // Bail if the target plugin/type isn't loaded — keeps this harmless everywhere else.
        if ( ! class_exists( 'WC_Product_MyType' ) ) {
            return;
        }
        add_filter( 'ssap_stock_data_for_product', array( __CLASS__, 'add_data' ), 10, 2 );
    }

    public static function add_data( $data, $product ) {
        if ( ! is_object( $product ) || ! method_exists( $product, 'is_type' ) || ! $product->is_type( 'mytype' ) ) {
            return $data;
        }
        // Guard every type-specific call with method_exists for cross-version safety.
        $children = array();
        if ( method_exists( $product, 'get_my_children' ) ) {
            foreach ( $product->get_my_children() as $child ) {
                $children[ (int) $child->get_id() ] = array(
                    'is_in_stock'    => (bool) $child->is_in_stock(),
                    'is_purchasable' => (bool) $child->is_purchasable(),
                );
            }
        }
        $data['mytype_children'] = $children;
        return $data;
    }
}
add_action( 'init', array( 'My_SSAP_Adapter', 'maybe_register' ) );
Common mistake

Registering the filter without a class_exists guard, or skipping the is_type() check inside the callback. Without the guard your adapter does work (or fatals) when the target plugin is inactive; without the type check it runs for every product on the page, not just the type you meant to enrich.

Patterns to copy from the built-in adapters (includes/adapters/)

  • Guard registration with class_exists and each call with method_exists / is_type — so the adapter tolerates the plugin being absent or a different version.
  • Return only JSON-safe values — cast to int/bool, key child data by ID.
  • Keep it cheap — this runs for every product on the page (subject to the ssap_max_products_per_request cap). Avoid heavy per-product queries; cache if you must.
Performance Tip

This filter fires once per product in the batch, up to ssap_max_products_per_request (default 200). A get_post_meta() or database call inside it is multiplied by the number of products on the page, so read from the WC_Product object you already have, or cache the lookup, rather than querying per product.

Consuming it on the front end

The whole stock map is delivered to JavaScript via the ssap_stock_updated event:

jQuery( document.body ).on( 'ssap_stock_updated', function ( e, stock ) {
  // stock[ productId ].mytype_children …
} );

Notes

  • Data only appears in the response when Refresh stock via AJAX is enabled.
  • The payload is per-page and capped — don’t bloat it.
  • For the request/response shape, see Developer Reference.

Further reading

The ssap_stock_data_for_product filter and the full endpoint contract your adapter feeds into.
What the built-in Bundles and Composite adapters already cover.
How adapters stay decoupled from the core via the filter.
Consuming your adapter’s fields on the front end via the stock event.

Comments

▲
▼
1/1