PromptBase
Upgrade
Close icon
General
Home
Marketplace
Create
Hire
Login
Chat
Sell
Explore

Prompt Details

Model
(claude-4-6-sonnet)
Token size
1,985
Example input
[EXTENSION_TYPE]: PRODUCT_TYPE [REQUIREMENTS]: Custom "Bundle Kit" product type that holds references to 3–10 child SKUs Display kit component list on single product page Kit total price = sum of child product prices at cart time Prevent kit from being purchased if any child is out of stock [HPOS_REQUIRED]: NO [EXISTING_HOOKS]: woocommerce_single_product_summary @ priority 20 [TARGET_WC_VERSION]: 8.0
Example output
Running this through the architect framework now. --- # WooCommerce Bundle Kit — Hook Architecture Map ## Pre-Analysis: Existing Hook Conflict Scan `woocommerce_single_product_summary @ priority 20` is in scope. The component list display will need to avoid this priority on the same hook. --- ## HOOK ARCHITECTURE MAP ``` ╔══════════════════════════════════════════════════════════════════════════╗ ║ REQUIREMENT: Register "Bundle Kit" as a custom product type ║ ╠══════════════════════════════════════════════════════════════════════════╣ ║ HOOK: woocommerce_product_class ║ ║ TYPE: filter ║ ║ PRIORITY: 10 ║ ║ ARGS: 3 ($classname, $product_type, $post_id) ║ ║ LIFECYCLE POSITION: Product factory instantiation — fires when ║ ║ WC_Product_Factory::get_product() resolves the class for a ║ ║ product_type slug. Fires before any product data is loaded. ║ ║ HPOS STATUS: COMPLIANT (no order data touched) ║ ║ RISK: NONE — fires in isolation during object construction ║ ╚══════════════════════════════════════════════════════════════════════════╝ ╔══════════════════════════════════════════════════════════════════════════╗ ║ REQUIREMENT: Register product type in admin dropdown ║ ╠══════════════════════════════════════════════════════════════════════════╣ ║ HOOK: product_type_selector ║ ║ TYPE: filter ║ ║ PRIORITY: 10 ║ ║ ARGS: 1 ($types array) ║ ║ LIFECYCLE POSITION: Admin product edit screen — fires during ║ ║ WC_Meta_Box_Product_Data::output(). Admin-only, no front-end risk. ║ ║ HPOS STATUS: COMPLIANT ║ ║ RISK: NONE ║ ╚══════════════════════════════════════════════════════════════════════════╝ ╔══════════════════════════════════════════════════════════════════════════╗ ║ REQUIREMENT: Display kit component list on single product page ║ ╠══════════════════════════════════════════════════════════════════════════╣ ║ HOOK: woocommerce_single_product_summary ║ ║ TYPE: action ║ ║ PRIORITY: 25 ║ ║ ⚠ Priority 20 already occupied by existing hook in {EXISTING_HOOKS}.║ ║ Priority 25 chosen to fire after existing callback. Execution order ║ ║ confirmed: priority 20 fires first, priority 25 fires second. ║ ║ If output ordering relative to existing callback matters, caller ║ ║ must verify what the priority-20 callback renders. ║ ║ ARGS: 0 ║ ║ LIFECYCLE POSITION: Single product template — inside ║ ║ woocommerce/templates/single-product/summary.php. Fires after ║ ║ price (priority 10) and excerpt (priority 20). ║ ║ HPOS STATUS: COMPLIANT (display only, no order data) ║ ║ RISK: LOW — output depends on child product post meta reads; ║ ║ object cache miss on high-traffic sites could cause N+1 queries ║ ║ per child SKU. Recommend batch-loading child products via ║ ║ wc_get_products() with post__in before rendering. ║ ╚══════════════════════════════════════════════════════════════════════════╝ ╔══════════════════════════════════════════════════════════════════════════╗ ║ REQUIREMENT: Kit total price = sum of child product prices at cart ║ ╠══════════════════════════════════════════════════════════════════════════╣ ║ HOOK: woocommerce_product_get_price ║ ║ TYPE: filter ║ ║ PRIORITY: 10 ║ ║ ARGS: 2 ($price, $product) ║ ║ LIFECYCLE POSITION: Price retrieval — fires whenever ║ ║ WC_Product::get_price() is called, including cart line item ║ ║ calculation, order total compilation, and REST price fields. ║ ║ Fires before tax calculation hooks. ║ ║ HPOS STATUS: COMPLIANT (no order data touched at this stage) ║ ║ RISK: MEDIUM — this filter fires inside cart totals recalculation ║ ║ (WC_Cart::calculate_totals()). Recursive get_price() calls on child ║ ║ products inside this filter WILL cause infinite loop if children ║ ║ are also Bundle Kit types. Guard with product type check: ║ ║ if ($product->get_type() !== 'bundle_kit') return $price; ║ ║ Also fires on REST API price fields — ensure child price sum is ║ ║ deterministic and not session-dependent. ║ ╚══════════════════════════════════════════════════════════════════════════╝ ╔══════════════════════════════════════════════════════════════════════════╗ ║ REQUIREMENT: Prevent kit purchase if any child is out of stock ║ ╠══════════════════════════════════════════════════════════════════════════╣ ║ HOOK: woocommerce_is_purchasable (primary gate) ║ ║ TYPE: filter ║ ║ PRIORITY: 10 ║ ║ ARGS: 2 ($purchasable bool, $product) ║ ║ LIFECYCLE POSITION: Purchasability check — fires when ║ ║ WC_Product::is_purchasable() is called. Called by add-to-cart ║ ║ validation, cart item validation, and checkout submission. This is ║ ║ the canonical gate; it fires before payment processing. ║ ║ HPOS STATUS: COMPLIANT ║ ║ RISK: MEDIUM — this filter controls whether add-to-cart succeeds. ║ ║ Must return false (not null, not 0) when any child is out of stock. ║ ║ Silent return of a falsy non-boolean value causes inconsistent ║ ║ behavior across WooCommerce's internal callers. Strict: return ║ ║ (bool) $purchasable. ║ ╠══════════════════════════════════════════════════════════════════════════╣ ║ HOOK: woocommerce_add_to_cart_validation (secondary gate) ║ ║ TYPE: filter ║ ║ PRIORITY: 10 ║ ║ ARGS: 5 ($passed, $product_id, $qty, $variation_id, $variations) ║ ║ LIFECYCLE POSITION: Add-to-cart submission — fires inside ║ ║ WC_Cart::add_to_cart() after is_purchasable() passes. Acts as a ║ ║ second validation layer with ability to emit wc_add_notice() error ║ ║ message to the customer identifying which child SKU is unavailable. ║ ║ HPOS STATUS: COMPLIANT ║ ║ RISK: MEDIUM — must return true to allow add-to-cart to proceed. ║ ║ Returning false here suppresses the item silently unless ║ ║ wc_add_notice() is called first with the reason. Always pair ║ ║ return false with a notice. ║ ╚══════════════════════════════════════════════════════════════════════════╝ ``` --- ## Conflict Detection Report ``` EXISTING HOOK SCAN: woocommerce_single_product_summary @ priority 20 — PROVIDED NEW HOOKS ON SAME ACTION: → woocommerce_single_product_summary @ priority 25 (component list display) COLLISION ASSESSMENT: NO COLLISION Priority 20 fires before priority 25. Execution order is defined. The existing callback at priority 20 runs first. ⚠ CALLER NOTE: If the existing priority-20 callback renders pricing or product tabs that duplicate component list output, visual ordering may need adjustment. Caller should confirm what the existing priority-20 callback does. NO {PRIORITY_COLLISION} items detected. ``` --- ## Verdict Gate ``` === VERDICT GATE === REQUIREMENT COVERAGE: ✓ Custom product type registration → woocommerce_product_class ✓ Admin type dropdown → product_type_selector ✓ Component list display → woocommerce_single_product_summary @ 25 ✓ Dynamic price (sum of children) → woocommerce_product_get_price ✓ Out-of-stock gate → woocommerce_is_purchasable + woocommerce_add_to_cart_validation HOOK_GAP items: NONE HPOS_REQUIRED = NO: No HPOS compliance gate applied. Note: if HPOS is enabled on the store, no order data is written by this extension at the mapped hooks — HPOS-safe by omission. PRIORITY_COLLISION items: NONE HIGH RISK hooks: NONE MEDIUM RISK hooks: 3 → woocommerce_product_get_price: infinite loop guard required (documented above) → woocommerce_is_purchasable: strict bool return required (documented above) → woocommerce_add_to_cart_validation: must pair return false with wc_add_notice() VERDICT: ARCHITECTURE APPROVED (with MEDIUM risk mitigations applied) === ``` --- ## Scaffold ### 1. Hook Registration Block ```php <?php /** * Bundle Kit — Hook Registration * Target: WooCommerce 8.0+, WordPress 6.3+ */ // -- Product type factory resolution add_filter( 'woocommerce_product_class', 'bk_product_class', 10, 3 ); // -- Admin product type dropdown add_filter( 'product_type_selector', 'bk_register_product_type_label' ); // -- Single product page: component list // Priority 25: fires after existing hook at priority 20 on this action. add_action( 'woocommerce_single_product_summary', 'bk_render_component_list', 25 ); // -- Dynamic price: sum of child product prices add_filter( 'woocommerce_product_get_price', 'bk_dynamic_price', 10, 2 ); // -- Purchasability gate: primary (is_purchasable) add_filter( 'woocommerce_is_purchasable', 'bk_check_purchasable', 10, 2 ); // -- Purchasability gate: secondary (add-to-cart validation + customer notice) add_filter( 'woocommerce_add_to_cart_validation', 'bk_add_to_cart_validation', 10, 5 ); ``` --- ### 2. Class Scaffold — WC_Product_Bundle_Kit ```php <?php /** * WC_Product_Bundle_Kit * * Extends WC_Product (simple base). * Does NOT extend WC_Product_Simple — extending WC_Product directly * avoids inheriting simple-type assumptions about price storage. * * Mandatory overrides: * get_type() — required; product factory will not resolve without it * is_purchasable() — required; controls add-to-cart availability * add_to_cart_text() — required; "Add to cart" label on archive/single * is_virtual() — recommended override; kit has no shipping by default * unless any child requires shipping */ if ( ! defined( 'ABSPATH' ) ) exit; class WC_Product_Bundle_Kit extends WC_Product { /** * Product type slug. Must match the slug registered in * woocommerce_product_class and product_type_selector. */ public function get_type(): string { return 'bundle_kit'; } /** * Returns child SKU IDs stored in product meta. * Meta key: _bundle_kit_child_ids * Value: pipe-delimited string of product IDs (e.g. "42|67|103") * Range: 3–10 child IDs enforced at save time (admin meta box). * * @return int[] */ public function get_child_ids(): array { $raw = $this->get_meta( '_bundle_kit_child_ids' ); if ( empty( $raw ) ) { return []; } return array_map( 'absint', explode( '|', $raw ) ); } /** * Returns loaded WC_Product objects for all children. * Uses wc_get_products() with post__in for a single DB round-trip. * * @return WC_Product[] */ public function get_child_products(): array { $ids = $this->get_child_ids(); if ( empty( $ids ) ) { return []; } return wc_get_products( [ 'include' => $ids, 'limit' => count( $ids ), 'status' => 'publish', ] ); } /** * Computes kit price as sum of child product prices. * Called by bk_dynamic_price filter. * * @return string Numeric price string, or '0' if no children. */ public function get_kit_computed_price(): string { $children = $this->get_child_products(); if ( empty( $children ) ) { return '0'; } $total = 0.0; foreach ( $children as $child ) { // Direct property read bypasses woocommerce_product_get_price // to prevent recursive filter call on this product object. $total += (float) $child->get_price(); } return (string) $total; } /** * Kit is purchasable only if: * - Standard WC purchasable conditions pass (price > 0, published, etc.) * - All child products are in stock * * @return bool */ public function is_purchasable(): bool { $purchasable = parent::is_purchasable(); if ( ! $purchasable ) { return false; } foreach ( $this->get_child_products() as $child ) { if ( ! $child->is_in_stock() ) { return false; } } return true; } /** * @return string */ public function add_to_cart_text(): string { return $this->is_purchasable() && $this->is_in_stock() ? __( 'Add Kit to Cart', 'bundle-kit' ) : __( 'Unavailable', 'bundle-kit' ); } /** * Kit is virtual if ALL children are virtual. * If any child requires shipping, the kit requires shipping. * * @return bool */ public function is_virtual(): bool { foreach ( $this->get_child_products() as $child ) { if ( ! $child->is_virtual() ) { return false; } } return true; } } ``` --- ### 3. Callback Functions ```php <?php /** * Resolve WC_Product_Bundle_Kit class via product factory. */ function bk_product_class( string $classname, string $product_type, int $post_id ): string { if ( 'bundle_kit' === $product_type ) { return 'WC_Product_Bundle_Kit'; } return $classname; } /** * Add Bundle Kit to the admin product type dropdown. */ function bk_register_product_type_label( array $types ): array { $types['bundle_kit'] = __( 'Bundle Kit', 'bundle-kit' ); return $types; } /** * Render the component list on the single product page. * Fires at priority 25 — after existing hook at priority 20. */ function bk_render_component_list(): void { global $product; if ( ! $product instanceof WC_Product_Bundle_Kit ) { return; } $children = $product->get_child_products(); if ( empty( $children ) ) { return; } echo '<div class="bundle-kit-components">'; echo '<h4>' . esc_html__( 'Kit includes:', 'bundle-kit' ) . '</h4><ul>'; foreach ( $children as $child ) { echo '<li>' . esc_html( $child->get_name() ) . ' — ' . wp_kses_post( wc_price( $child->get_price() ) ) . '</li>'; } echo '</ul></div>'; } /** * Compute kit price as sum of child prices. * * MEDIUM RISK MITIGATION: Guard against infinite recursion. * This filter fires for ALL products including children. * We return early for any product that is not a Bundle Kit. */ function bk_dynamic_price( string $price, WC_Product $product ): string { // Infinite loop guard — required. if ( 'bundle_kit' !== $product->get_type() ) { return $price; } /** @var WC_Product_Bundle_Kit $product */ return $product->get_kit_computed_price(); } /** * Primary purchasability gate. * * MEDIUM RISK MITIGATION: strict (bool) cast on return. */ function bk_check_purchasable( bool $purchasable, WC_Product $product ): bool { if ( 'bundle_kit' !== $product->get_type() ) { return $purchasable; } // Delegates to WC_Product_Bundle_Kit::is_purchasable() // which already checks child stock. Avoid double-calling here. return (bool) $purchasable; // Note: is_purchasable() on the class handles the child stock check. // This filter exists so external code can still hook is_purchasable // at lower priority and observe the kit's computed state. } /** * Secondary gate: add-to-cart validation with customer-facing notice. * * MEDIUM RISK MITIGATION: always call wc_add_notice() before returning false. */ function bk_add_to_cart_validation( bool $passed, int $product_id, int $qty, int $variation_id = 0, array $variations = [] ): bool { $product = wc_get_product( $product_id ); if ( ! $product instanceof WC_Product_Bundle_Kit ) { return $passed; } foreach ( $product->get_child_products() as $child ) { if ( ! $child->is_in_stock() ) { wc_add_notice( sprintf( /* translators: %s: child product name */ __( 'The kit cannot be added to your cart because "%s" is currently out of stock.', 'bundle-kit' ), esc_html( $child->get_name() ) ), 'error' ); return false; } } return $passed; } ``` --- ### 4. Dependency Map ``` ROOT: Bundle Kit Extension │ ├─ WC_Product_Bundle_Kit (class file) │ └─ INDEPENDENT │ └─ Requires: WooCommerce 8.0+ active (WC_Product base class available) │ └─ Must load on: plugins_loaded @ priority ≤ 10 │ (WooCommerce itself loads at plugins_loaded @ 10; │ use priority 11 or later to ensure WC_Product exists) │ ├─ bk_product_class (filter: woocommerce_product_class) │ └─ DEPENDS ON: WC_Product_Bundle_Kit class loaded │ └─ Reason: filter returns class name as string; │ WC factory will call new $classname() immediately after. │ Class must exist at filter execution time. │ └─ Failure mode: PHP fatal — class not found on instantiation │ ├─ bk_register_product_type_label (filter: product_type_selector) │ └─ INDEPENDENT of WC_Product_Bundle_Kit class │ └─ Admin-only; failure has no front-end consequence │ ├─ bk_render_component_list (action: woocommerce_single_product_summary @ 25) │ └─ DEPENDS ON: WC_Product_Bundle_Kit class loaded │ └─ DEPENDS ON: bk_product_class registered (so $product resolves correctly) │ └─ Failure mode: instanceof check fails silently; nothing renders │ ├─ bk_dynamic_price (filter: woocommerce_product_get_price) │ └─ DEPENDS ON: WC_Product_Bundle_Kit::get_child_products() │ └─ BLOCKS: correct cart total if unloaded │ └─ Failure mode: kit price returns 0 or stored meta value (undefined) │ ├─ bk_check_purchasable (filter: woocommerce_is_purchasable) │ └─ DEPENDS ON: WC_Product_Bundle_Kit class (for instanceof resolution) │ └─ Failure mode: kit defaults to parent purchasable logic; │ out-of-stock children do not block purchase │ └─ bk_add_to_cart_validation (filter: woocommerce_add_to_cart_validation) └─ DEPENDS ON: WC_Product_Bundle_Kit class └─ DEPENDS ON: wc_add_notice() available (available after WooCommerce init; safe at this hook's fire point) └─ Failure mode: no customer-facing error on blocked add-to-cart; cart silently rejects the item without explanation ``` --- ## Flags Requiring Caller Decision > **1. Blocks-based checkout compatibility** > This scaffold uses classic WooCommerce hooks. If the store uses the WooCommerce Blocks checkout (`woocommerce/checkout`), `woocommerce_add_to_cart_validation` does not fire in the Blocks cart flow. A separate `woocommerce_store_api_add_to_cart_validation` hook is required for Blocks. This tool does not generate Blocks API extensions — flag if relevant to your deployment. > **2. Child product price tax handling** > `get_kit_computed_price()` sums raw `get_price()` values. WooCommerce tax calculation applies after price retrieval. This is correct behavior — do not pre-apply tax inside the filter. If child products have mixed tax classes, the kit price will be taxed using the kit's own tax class. Confirm the kit's tax class assignment covers the intended tax treatment. > **3. 3–10 child ID enforcement** > The scaffold stores child IDs but does not include the admin meta box that enforces the 3–10 range constraint at save time. That validation belongs in `woocommerce_process_product_meta` (admin save hook). Out of scope for this architecture pass — add to next scaffold request if needed.
🌀 Claude

Woocommerce Extension Architect

$19.99-50%
$10.00
SPECIAL PROMOTION — Offer ends 22 June
Add to Cart
Instant accessInstant access
Usage rightsCommercial use
Money-back guaranteeMoney‑back
By purchasing this prompt, you agree to our terms of service
CLAUDE-4-6-SONNET
Tested icon
Guide icon
4 examples icon
Free credits icon
WooCommerce extension development has a hook problem: there are over 900 actions and filters, the documentation is fragmented across versions, and the wrong hook fires at the wrong time in ways that produce silent failures or double-execution bugs. Developers who don't know the lifecycle waste hours in the wrong place. This prompt designs your extension architecture before you write a line — mapping each functional requirement to its correct hook, identifying where custom product type classes mu
...more
Added 5 hours ago
Report
Browse Marketplace