2
votes

I have been trying to add a custom 'select' field to a woocommerce checkout. The select options are comprised of entries in an array, which in turn is comprised of titles and dates from a query of custom post types.

This all works fine, I can add new custom posts and the title and dates are concatenated, added to the array and in turn added to the select drop-down at the checkout, however when I submit the form and complete the order the index of the chosen select field value is added to the meta data, not the value.

I have added images in here to show you what I mean. e.g. if I select the third option from the dropdown 'Tain Farmers Market' which is array index 2 that's what is saved for the order meta instead of the value at that index entry.

The screenshot includes the results of the var dump showing the array contents as well as the select field below it.

image of how the array appears in var dump the order meta showing in the admin screen, displaying the index not the value

Here is my code covering this section, any pointers would be really handy. I feel there is probably some simple solution to get the value at the chosen index when the order is processed?

/**
* Add custom Pickup Field field to the checkout page
*/
add_filter( 'woocommerce_after_order_notes', 'hiwoo_add_checkout_fields' );
function hiwoo_add_checkout_fields( $checkout ) {

  $args = array(
    'post_type'         => 'pickup-point',
    'posts_per_page'    => -1,
    'post_status'       => 'publish'
    );

// Custom query to pull in the pickup points.
$query = new WP_Query( $args );

$pickup_comb_option = [];

while ($query->have_posts()) {
    $query->the_post();

    $postid = $post->ID;
    $pickuptitle = get_the_title($post);
    $pickupdate = get_post_meta(get_the_id($post), 'available_date', true);

    $pickupoption = $pickuptitle . ' - ' . $pickupdate;
    array_push($pickup_comb_option, $pickupoption);
}

var_dump($pickup_comb_option);

// Restore original post data.
wp_reset_postdata();


echo '<div id="custom_checkout_field"><h2>' . __('Order Pickup Location/Date') . '</h2>';

 woocommerce_form_field( 'pickup_point_options', array(
        'type' => 'select',
        'class' => array(
        'my-field-class form-row-wide'
            ) ,
        'label' => __('Select Pickup Location/Date') ,
        'placeholder' => __('Pickup Point') ,
        'options' => $pickup_comb_option,
        ), 
        $checkout->get_value( 'pickup_point_options' ));

echo '</div>';

} /* Close custom field function */

/**
 * Process the checkout
 */
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');

function my_custom_checkout_field_process() {
    // Check if set, if its not set add an error.
    if ( ! $_POST['pickup_point_options'] )
        wc_add_notice( __( 'Please select a pickup location from the list.' ), 'error' );
}

/**
 * Update the order meta with field value
 */
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );

function my_custom_checkout_field_update_order_meta( $order_id ) {
    if ( ! empty( $_POST['pickup_point_options'] ) ) {
        update_post_meta( $order_id, 'Pickup Location/Date', sanitize_text_field( $_POST['pickup_point_options'] ) );
    }
}

/**
 * Display field value on the order edit page
 */
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );

function my_custom_checkout_field_display_admin_order_meta($order){
    echo '<p><strong>'.__('Pickup Location/Date').':</strong> ' . get_post_meta( $order->id, 'Pickup Location/Date', true ) . '</p>';
}

The html produced on the checkout page for the select field is:

array(3) {
  [0]=>
  string(32) "Tain Farmers Market - 2019-12-25"
  [1]=>
  string(30) "Hi Create Offices - 2019-10-25"
  [2]=>
  string(27) "Dornoch Stores - 2019-09-26"
}


<select name="pickup_point_options" id="pickup_point_options" class="select " data-placeholder="Pickup Point">
    <option value="0">Tain Farmers Market - 2019-12-25</option>
    <option value="1">Hi Create Offices - 2019-10-25</option>
    <option value="2">Dornoch Stores - 2019-09-26</option>
</select>
1
Your questions holds way too many information which seem not necessary. It's not clear to me what's being saved finally. Is it the array-index or is it the text shown in the select box? (Value is a bad word in this case, since the value should be the array-index). Can you show the produced HTML of the select? - Jonathan
Sorry - to clarify. When you save the form i.e. complete the order what is saved to the order_meta is the index of that entry in the array i.e. 2, as opposed to the value i.e. 'Tain Farmers Market' - Dan Sutherland
You are saying value "Tain Farmers Market". I assume that's stated from a user's perspective. When I speak of value I mean the actual content of the value-attribute inside the select's option. Can you show the html of the <select> produced? - Jonathan
Thanks, I've updated the original post to include what the HTML is for the select field when it appears on the checkout page. Thanks - Dan Sutherland

1 Answers

2
votes

According to your HTML I assume the value being saved in your example is "2" instead of "Tainy Farmers Market", right? If so, that is technically correct, as the value of a <select> being submitted on a form submit is always the value in the option's value attribute (<option value="2">) and not the text in between (<option value="2">Some text</option>)

To have it saving the text in between it has to be set inside the value-attribute. Therefor you need to change the option's array from numeric to associative, like so:

$pickup_comb_option = [];

while ($query->have_posts()) {
    $query->the_post();

    // ...
    // change this line
    array_push($pickup_comb_option, $pickupoption); 
    // to:
    $pickup_comb_option[$pickupoption] = $pickupoption;
}

To verify this worked check for the generated HTML, it should look something like this:

<select>
   <option value="Tain Farmers Market">Tain Farmers Market</option>
</select>

@edit I should mention this approach has some downsides: You should ensure that the string in $pickupoption is not used multiple times. It's also necessary that the string is valid for use as an array key (not NULL, not empty). Since the value is being written inside a html-attribute (value="$arrKey") you may also want to check how Woocommerce handle's such things as doublequotes inside the string. Just to avoid possible bugs beforehand.

An alternative approach could be to save an id instead of a string.