Search code examples
wordpresswoocommercebackendproducthook-woocommerce

Add custom Select2 search field into WooCommerce product data metabox


I have successfully added a product search field in my edit product page, but I need to be able to clear the selection. How can I achieve this?

Alternatively: how do I manage to only be able to select 1 product if I set multiple on the select component?

My code looks like below:

add_action('woocommerce_product_data_panels', function() {
    global $post, $woocommerce;

    $product_ids = get_post_meta( $post->ID, '_stock_sync_data_ids', true );
    if( empty($product_ids) )
        $product_ids = array();
    ?>
    <div id="stock_sync_data" class="panel woocommerce_options_panel hidden">
        <?php if ( $woocommerce->version >= '3.0' ) : ?>
            <p class="form-field stock_sync_data">
                <label for="stock_sync_data"><?php _e( 'Select product to sync with. (Only select 1 item)', 'woocommerce' ); ?></label>
                <select class="wc-product-search" style="width: 50%;" id="stock_sync_data" name="stock_sync_data[]" data-placeholder="<?php esc_attr_e( 'Search for a product&hellip;', 'woocommerce' ); ?>" data-action="woocommerce_json_search_products_and_variations">
                    <?php
                        foreach ( $product_ids as $product_id ) {
                            $product = wc_get_product( $product_id );
                            if ( is_object( $product )) {
                                echo '<option value="' . esc_attr( $product_id ) . '"' . selected( true, true, false ) . '>' . wp_kses_post( $product->get_formatted_name() ) . '</option>';
                            }
                        }
                    ?>
                </select> <?php echo wc_help_tip( __( 'Select product to syncronize stock.', 'woocommerce' ) ); ?>
                
            </p>
        <?php endif; ?>
    </div>
    <?php
});

Solution

  • You're very close with your code attempt, but I've tweaked it slightly and removed some unnecessary checks (like the WooCommerce version, as it's 3.0.. from 2017)

    So you get:

    // Add custom product setting tab
    function filter_woocommerce_product_data_tabs( $default_tabs ) {
        $default_tabs['custom_tab'] = array(
            'label'     => __( 'Custom Tab', 'woocommerce' ),
            'target'    => 'stock_sync_data_tab',
            'priority'  => 80,
            'class'     => array()
        );
    
        return $default_tabs;
    }
    add_filter( 'woocommerce_product_data_tabs', 'filter_woocommerce_product_data_tabs', 10, 1 );
    
    // Contents custom product setting tab
    function action_woocommerce_product_data_panels() {
        global $post;
    
        $product_ids = get_post_meta( $post->ID, '_stock_sync_data_ids', true );
        if ( empty ( $product_ids )  ) { 
            $product_ids = array();
        }
        ?>
        <!-- Note the 'id' attribute needs to match the 'target' parameter set above -->
        <div id="stock_sync_data_tab" class="panel woocommerce_options_panel hidden">
            <p class="form-field stock_sync_data">
                <label for="stock_sync_data"><?php esc_html_e( 'Select product to sync with. (Only select 1 item)', 'woocommerce' ); ?></label>
                <select class="wc-product-search" multiple="multiple" style="width: 50%;" id="stock_sync_data" name="stock_sync_data[]" data-placeholder="<?php esc_attr_e( 'Search for a product&hellip;', 'woocommerce' ); ?>" data-action="woocommerce_json_search_products_and_variations" data-exclude="<?php echo intval( $post->ID ); ?>">
                    <?php
                    foreach ( $product_ids as $product_id ) {
                        $product = wc_get_product( $product_id );
                        if ( is_object( $product ) ) {
                            echo '<option value="' . esc_attr( $product_id ) . '"' . selected( true, true, false ) . '>' . esc_html( wp_strip_all_tags( $product->get_formatted_name() ) ) . '</option>';
                        }
                    }
                    ?>
                </select> <?php echo wc_help_tip( __( 'Select product to syncronize stock.', 'woocommerce' ) ); // WPCS: XSS ok. ?>
            </p>
        </div>
        <?php
    }
    add_action( 'woocommerce_product_data_panels', 'action_woocommerce_product_data_panels' );
    

    Result:

    enter image description here


    "Alternatively: how do I manage to only be able to select 1 product if I set multiple on the select component?"

    Just add data-maximum-selection-length="1"

    So you get:

    <select class="wc-product-search" multiple="multiple" style="width: 50%;" id="stock_sync_data" name="stock_sync_data[]" data-maximum-selection-length="1"..
    

    Related: Issue with saving value from Select2 field in WooCommerce backend