Product Builder for WooCommerce

Visual product configurator with tile selection, conditional logic, file uploads, and smart formula pricing. The modern WooCommerce product addons alternative.

Product Builder Developer Reference

Product Builder is built around two registries (layouts and field types) and a small set of hooks. Everything below is public API — safe to use from a child theme or companion plugin.

Bootstrap

Hook into carticy_pb_init after the plugin has registered its core services. Extensions register layouts and field types here.

my-extension.php
add_action( 'carticy_pb_init', function ( $plugin ) {
    $plugin->layouts()->register( new My_Wizard_Layout() );
    $plugin->field_types()->register( new My_DatePicker_Field() );
} );

Actions

  • carticy_pb_init — Plugin initialized. Register layouts and field types here.
  • carticy_pb_config_saved(int $product_id, array $config) — fired after a builder configuration is saved.
  • carticy_pb_order_item_created(WC_Order_Item $item, array $selections) — fired after selections are written to an order line.
  • carticy_pb_render_group_extension_fields(int $product_id) — render custom fields inside the option group modal. Use data-extension-field="key" on inputs for automatic binding.

Filters

  • carticy_pb_option_groups(array $groups, int $product_id) — modify option groups before display.
  • carticy_pb_calculated_price(float $total, array $selections, array $context) — modify the calculated total.
  • carticy_pb_cart_item_data(array $data, array $selections) — modify cart item data before adding.
  • carticy_pb_sanitize_group_extension_data(array $data, array $group, string $field_type) — sanitize extension data before save.

Custom field type

Implement FieldTypeInterface to add a new field type (date picker, range slider, dropdown, etc.). The shape of get_editor_config() drives the option group modal — including which option-table columns appear.

my-date-picker.php
class My_DatePicker_Field implements \Carticy\ProductBuilder\Fields\FieldTypeInterface {
    public function get_type(): string { return 'date_picker'; }
    public function get_name(): string { return __( 'Date', 'my-ext' ); }
    public function get_description(): string { return __( 'Customer picks a date.', 'my-ext' ); }
    public function supports_multiple(): bool { return false; }

    public function render( $option, array $settings, array $context ): string { /* HTML */ }
    public function render_container( string $inner_html, array $group, array $settings ): string { /* HTML */ }

    public function validate( $value, $option, array $context ): bool { /* … */ }
    public function sanitize( $value, $option, array $context ): mixed { /* … */ }

    public function get_assets(): array { return [ 'css' => [], 'js' => [] ]; }
    public function get_js_data( array $options, array $settings ): array { return [ 'type' => 'date_picker' ]; }

    public function get_editor_config(): array {
        return [
            'supports_options' => false,  // group-level field, no per-option rows
            'supports_images'  => false,
            'supports_columns' => false,
            'supports_default' => false,
            'supports_pricing' => true,
            'option_fields'    => [],
            'group_settings'   => [
                'min_date' => [ 'type' => 'text', 'label' => 'Min date' ],
                'max_date' => [ 'type' => 'text', 'label' => 'Max date' ],
            ],
        ];
    }
}

Custom layout

Implement LayoutInterface for a new layout style (tabs, accordion, drawer, etc.). The default layout stacks groups; the wizard turns each group into a step.

Modal extension fields

Extensions can add arbitrary settings to the option group modal without forking the modal template. Values are stored on the group’s extension_data property — separate from the field-type-specific meta bag.

my-extension-fields.php
// 1. Render the field inside the modal.
add_action( 'carticy_pb_render_group_extension_fields', function ( $product_id ) {
    ?>
    <div class="cpb-field-row">
        <label>
            <input type="checkbox" data-extension-field="my_feature" value="1" />
            <?php esc_html_e( 'Enable my feature', 'my-ext' ); ?>
        </label>
    </div>
    <?php
} );

// 2. Sanitize before save.
add_filter( 'carticy_pb_sanitize_group_extension_data', function ( $data, $group, $field_type ) {
    $data['my_feature'] = ! empty( $data['my_feature'] );
    return $data;
}, 10, 3 );

The data-extension-field attribute auto-binds the input value to group.extension_data[key] in the admin JS — no extra wiring needed.

Reading selections programmatically

read-selections.php
foreach ( $order->get_items() as $item ) {
    $selections = $item->get_meta( '_carticy_pb_selections', true );
    if ( ! $selections ) { continue; }
    // $selections is an array keyed by group_id. Each value is either a
    // string (option_id) or { option_id, qty } for Quantity fields, or an
    // array of option_ids for Multi-choice fields.
}

Configuration storage

The full per-product configuration is stored in post meta _carticy_pb_config as an array with enabled, layout, option_groups[] and settings. Use the plugin’s Repository service rather than reading the raw meta — it handles versioning and sanitization.