WooCommerce Custom Fields - Multiselect - php

I'm adding extra fields to the checkout page in WooCommerce, I can add basic fields like a text box, but need to add a (multi) select box, where the user can choose multiple items. I've figured out how to add a select box through code, like this:
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
echo '<div id="my_custom_checkout_field"><h3>'.__('My Field').'</h3>';
woocommerce_form_field( 'my_field_name', array(
'type' => 'select',
'class' => array('my-field-class form-row-wide'),
'label' => __('Fill in this field'),
'placeholder' => __('Enter something'),
'options' => array(
'Buick' => __('Buick', 'woocommerce' ),
'Ford' => __('Ford', 'woocommerce' )
)
), $checkout->get_value( 'my_field_name' ));
echo '</div>';
}
But that's just a single select drop down.
Can I do something similar for a multi-select?
Or do you have a WooCommerce extension that'd you recommend?
Please advice, thanks in advance!

You need to create your own custom field type handler. If you look at WooCommerce source code you will see that you can use filter: 'woocommerce_form_field_' . $args['type']
I haven't really tested this, this is just slightly altered code from single "select" control, but you get the point:
add_filter( 'woocommerce_form_field_multiselect', 'custom_multiselect_handler', 10, 4 );
function custom_multiselect_handler( $field, $key, $args, $value ) {
$options = '';
if ( ! empty( $args['options'] ) ) {
foreach ( $args['options'] as $option_key => $option_text ) {
$options .= '<option value="' . $option_key . '" '. selected( $value, $option_key, false ) . '>' . $option_text .'</option>';
}
if ($args['required']) {
$args['class'][] = 'validate-required';
$required = ' <abbr class="required" title="' . esc_attr__('required', 'woocommerce') . '">*</abbr>';
}
else {
$required = ' <span class="optional">(' . esc_html__('optional', 'woocommerce') . ')</span>';
}
$field = '<p class="form-row ' . implode( ' ', $args['class'] ) .'" id="' . $key . '_field">
<label for="' . $key . '" class="' . implode( ' ', $args['label_class'] ) .'">' . $args['label']. $required . '</label>
<select name="' . $key . '" id="' . $key . '" class="select" multiple="multiple">
' . $options . '
</select>
</p>' . $args['after'];
}
return $field;
}
And in your code just state the type as 'multiselect':
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
echo '<div id="my_custom_checkout_field"><h3>'.__('My Field').'</h3>';
woocommerce_form_field( 'my_field_name', array(
'type' => 'multiselect',
'class' => array('my-field-class form-row-wide'),
'label' => __('Fill in this field'),
'placeholder' => __('Enter something'),
'options' => array(
'Buick' => __('Buick', 'woocommerce' ),
'Ford' => __('Ford', 'woocommerce' )
)
), $checkout->get_value( 'my_field_name' ));
echo '</div>';
}

Although it's late to answer this post but I am adding this solution for the help of those still looking for the solution. We can add custom data using custom_attributes that will make it a multiple-choice dropdown.
woocommerce_form_field('my_field_name',array(
type=>"select",
custom_attributes=>array("multiple"),
options=>$options
));

You can use this woocommerce plugin to easily do that if you are having issues with the snippet. "Woocommerce Easy Checkout Fields Editor". You can find it at codecanyon

Related

WooCommerce: How to change "Our Bank Details" heading?

In the email WooCommerce sends to customers if they ordered a product and picked bank transfer (BACS) as payment method, by default there is an h2 heading: "Our bank details". I want to change this wording.
I figure I best do this via functions.php, but I am not sure how to address this. Could someone please help me?
Alternative approaches to solve this problem are welcome, too.
The line I want to change is in this if-statement in class-wc-gateways-bacs.php:
if ( $has_details ) { echo '<section class="woocommerce-bacs-bank-details"><h2 class="wc-bacs-bank-details-heading">' . esc_html__( 'Our bank details', 'woocommerce' ) . '</h2>' . wp_kses_post( PHP_EOL . $account_html ) . '</section>';
Here is the complete function from within the class WC_Gateway_BACS extends WC_Payment_Gateway:
private function bank_details( $order_id = '' ) {
if ( empty( $this->account_details ) ) {
return;
}
// Get order and store in $order.
$order = wc_get_order( $order_id );
// Get the order country and country $locale.
$country = $order->get_billing_country();
$locale = $this->get_country_locale();
// Get sortcode label in the $locale array and use appropriate one.
$sortcode = isset( $locale[ $country ]['sortcode']['label'] ) ? $locale[ $country ]['sortcode']['label'] : __( 'Sort code', 'woocommerce' );
$bacs_accounts = apply_filters( 'woocommerce_bacs_accounts', $this->account_details, $order_id );
if ( ! empty( $bacs_accounts ) ) {
$account_html = '';
$has_details = false;
foreach ( $bacs_accounts as $bacs_account ) {
$bacs_account = (object) $bacs_account;
if ( $bacs_account->account_name ) {
$account_html .= '<h3 class="wc-bacs-bank-details-account-name">' . wp_kses_post( wp_unslash( $bacs_account->account_name ) ) . ':</h3>' . PHP_EOL;
}
$account_html .= '<ul class="wc-bacs-bank-details order_details bacs_details">' . PHP_EOL;
// BACS account fields shown on the thanks page and in emails.
$account_fields = apply_filters(
'woocommerce_bacs_account_fields',
array(
'bank_name' => array(
'label' => __( 'Bank', 'woocommerce' ),
'value' => $bacs_account->bank_name,
),
'account_number' => array(
'label' => __( 'Account number', 'woocommerce' ),
'value' => $bacs_account->account_number,
),
'sort_code' => array(
'label' => $sortcode,
'value' => $bacs_account->sort_code,
),
'iban' => array(
'label' => __( 'IBAN', 'woocommerce' ),
'value' => $bacs_account->iban,
),
'bic' => array(
'label' => __( 'BIC', 'woocommerce' ),
'value' => $bacs_account->bic,
),
),
$order_id
);
foreach ( $account_fields as $field_key => $field ) {
if ( ! empty( $field['value'] ) ) {
$account_html .= '<li class="' . esc_attr( $field_key ) . '">' . wp_kses_post( $field['label'] ) . ': <strong>' . wp_kses_post( wptexturize( $field['value'] ) ) . '</strong></li>' . PHP_EOL;
$has_details = true;
}
}
$account_html .= '</ul>';
}
if ( $has_details ) {
echo '<section class="woocommerce-bacs-bank-details"><h2 class="wc-bacs-bank-details-heading">' . esc_html__( 'Our bank details', 'woocommerce' ) . '</h2>' . wp_kses_post( PHP_EOL . $account_html ) . '</section>';
}
}
}
Please try this
function update_bank_details_heading($translated_text, $text, $domain) {
if ( did_action('woocommerce_email_before_order_table') ) {
switch ( $text ) {
case 'Our bank details' :
$translated_text = __( 'Bank Details Are...', 'woocommerce' );
break;
}
}
return $translated_text;
}
add_filter( 'gettext', 'update_bank_details_heading', 20, 3 );

Display custom WooCommerce checkout fields in order details and make them editable

I'm updating a plugin for WooCommerce, and I have got a hooks.php file in which I handle WooCommerce hooks. I added four new billing fields to checkout and I need to display them in order details; I need also to make them editable.
Here is the code in which I succeed in displaying my fields:
add_action('woocommerce_admin_order_data_after_billing_address', function($order)
echo p(strong(__('Codice Fiscale', 'fatt-24')).': <br />' . order_c_fis($order));
echo p(strong(__('Partita Iva / VAT number', 'fatt-24')).': <br />' . order_p_iva($order));
echo p(strong(__('Codice destinatario', 'fatt-24')) . ': <br />' . order_recipientcode($order));
echo p(strong(__('Indirizzo PEC', 'fatt-24')) . ': <br />' . order_pec_address($order));
}, 10, 1);
I'm not able to edit and save it.
For display in admin order details as editable fields
add_filter('woocommerce_admin_billing_fields', 'custom_admin_billing_fields', 10, 1);
function custom_admin_billing_fields($fields) {
$fields['billing_fiscalcode'] = array(
'label' => __('billing_fiscalcode', 'woocommerce'),
'show' => true,
);
$fields['billing_vatcode'] = array(
'label' => __('billing_vatcode', 'woocommerce'),
'show' => true,
);
$fields['billing_recipientcode'] = array(
'label' => __('billing_recipientcode', 'woocommerce'),
'show' => true,
);
$fields['billing_pecaddress'] = array(
'label' => __('billing_pecaddress', 'woocommerce'),
'show' => true,
);
return $fields;
}
add_action('woocommerce_admin_order_data_after_billing_address', 'display_billing_options_value_in_admin_order', 10, 1);
function display_billing_options_value_in_admin_order($order) {
if ($value = get_post_meta($order->get_id(), 'billing_fiscalcode', true))
echo '<p><strong>' . __('Billing Fiscalcode', 'woocommerce') . ':</strong> ' . $value . '</p>';
if ($value = get_post_meta($order->get_id(), 'billing_vatcode', true))
echo '<p><strong>' . __('billing_vatcode', 'woocommerce') . ':</strong> ' . $value . '</p>';
if ($value = get_post_meta($order->get_id(), 'billing_recipientcode', true))
echo '<p><strong>' . __('billing_recipientcode', 'woocommerce') . ':</strong> ' . $value . '</p>';
if ($value = get_post_meta($order->get_id(), 'billing_pecaddress', true))
echo '<p><strong>' . __('billing_pecaddress', 'woocommerce') . ':</strong> ' . $value . '</p>';
}
At last I found this solution:
<div class="edit_address">
<?php
/* here I make the fields editable */
if (customer_use_cf()){
woocommerce_wp_text_input( array( 'id' => '_billing_fiscalcode', 'label' =>__('Codice Fiscale', 'fatt-24'), 'wrapper_class' => '_billing_company_field' ) ); // here I make the fields editable
}
if (customer_use_vat()){
woocommerce_wp_text_input( array( 'id' => '_billing_vatcode', 'label' => __('Partita Iva / VAT number', 'fatt-24'), 'wrapper_class' => '_billing_company_field' ) );
}
if(customer_use_recipientcode()) {
woocommerce_wp_text_input( array( 'id' => '_billing_recipientcode', 'label' => __('Codice destinatario', 'fatt-24'), 'wrapper_class' => '_billing_company_field' ) );
woocommerce_wp_text_input( array( 'id' => '_billing_pecaddress', 'label' => __('Indirizzo PEC', 'fatt-24'), 'wrapper_class' => '_billing_company_field' ) );
}?>
</div>
<?php
}, 10, 1);
/* here I update the post */
add_action( 'woocommerce_process_shop_order_meta', function ( $post_id, $post ){
update_post_meta( $post_id, '_billing_pecaddress', wc_clean( sanitize_text_field($_POST[ '_billing_pecaddress' ] )) ); // here I update the post
update_post_meta( $post_id, '_billing_recipientcode ', wc_clean(sanitize_text_field( $_POST[ '_billing_recipientcode' ] )) );
update_post_meta( $post_id, '_billing_fiscalcode', wc_clean( sanitize_text_field($_POST[ '_billing_fiscalcode' ]) ) );
update_post_meta( $post_id, '_billing_vatcode', wc_clean( sanitize_text_field($_POST[ '_billing_vatcode' ]) ) );
}, 45, 2 );

Wordpress - How to specify an ID in custom Visual Composer element

I'm current building a website for a non-profit, and am using an older edition of the Wordpress theme, Savior.
There is a custom post type of Donations, and it allows the admin to set up different "Causes", each of which is a post with a specific ID.
By default, the theme has a custom Visual Composer element (Screenshot 1 - https://i.stack.imgur.com/cmhTu.png) that allows you to feature the most recent "Cause"; the only customization option you have is for the Title of the VC element.
I'm trying to update this custom VC element so that the admin can specify the exact ID for the "Cause" they would like to feature on a page/post vs. only showing the latest "Cause."
I've adjusted the VC mapping for the cause.php template where commented below:
<?php
class STM_VC_Causes {
function __construct() {
add_action( 'init', array( $this, 'integrateWithVC' ) );
add_shortcode( 'stm_causes', array( $this, 'render' ) );
}
public function integrateWithVC() {
if ( function_exists( 'vc_map' ) ) {
vc_map( array(
'name' => __( 'Causes', STM_DOMAIN ),
'base' => 'stm_causes',
'category' => __( 'STM', STM_DOMAIN ),
'params' => array(
array(
'type' => 'textfield',
'class' => '',
'heading' => __( 'Title', STM_DOMAIN ),
'param_name' => 'title',
'value' => __( 'Our Causes', STM_DOMAIN )
),
/** ========================================
* Qing Custom 8-6-2018
* Allow admin to select Cause to feature
======================================== **/
array(
'type' => 'textfield',
'class' => '',
'heading' => __( 'Cause ID', STM_DOMAIN ),
'param_name' => 'id',
'value' => __( '', STM_DOMAIN )
)
)
) );
}
}
public function render( $atts, $content = null ) {
/** ========================================
* Qing Custom 8-6-2018
* Display selected Cause ID
======================================== **/
$title = '';
$id = '';
extract( shortcode_atts( array(
'title' => '',
'id' => ''
), $atts ) );
$fea_cause = $atts['id'];
$donations = new WP_Query( array( 'post_type' => 'donation', 'posts_per_page' => 1, 'post__in' => $fea_cause ) );
$output = '';
$output .= '<ul class="donation-grid first clearfix">';
while ( $donations->have_posts() ) {
$donations->the_post();
$target_amount = ( get_post_meta( get_the_ID(), 'donation_target', true ) == '' ) ? 0 : get_post_meta( get_the_ID(), 'donation_target', true );
$raised_amount = ( get_post_meta( get_the_ID(), 'donation_raised', true ) == '' ) ? 0 : get_post_meta( get_the_ID(), 'donation_raised', true );
$currency = ( get_post_meta( get_the_ID(), 'donation_currency', true ) == '' ) ? '$' : get_post_meta( get_the_ID(), 'donation_currency', true );
$donors = ( get_post_meta( get_the_ID(), 'donation_donors', true ) == '' ) ? 0 : get_post_meta( get_the_ID(), 'donation_donors', true );
$target_amount_percent = ( $raised_amount / $target_amount ) * 100;
$output .= '<li id="post-' . get_the_ID() . '" class="' . implode( ' ', get_post_class() ) . '">';
$output .= '<div class="donation-thumbnail">';
$output .= '<a href="' . get_the_permalink() . '">';
if ( has_post_thumbnail() ) {
$output .= get_the_post_thumbnail( get_the_ID(), 'thumb-150x150' );
}
$output .= '</a>';
$output .= '</div>';
$output .= '<div class="donation-content">';
$output .= '<h4>' . get_the_title() . '</h4>';
$output .= '<div class="progress_bar"><span style="width: ' . $target_amount_percent . '%;"></span></div>';
$output .= '<div class="donation-stat">';
$output .= '<span><i class="fa fa-child"></i> ' . __( 'Raised', STM_DOMAIN ) . '<br/>' . $currency . $raised_amount . '</span>';
$output .= '<span><i class="fa fa-users"></i> ' . __( 'Donors', STM_DOMAIN ) . '<br/>' . $donors . '</span>';
$output .= '<span><i class="fa fa-thumbs-up"></i> ' . __( 'Goal', STM_DOMAIN ) . '<br/>' . $currency . $target_amount . '</span>';
$output .= '</div>';
$output .= '<div class="donate_now">';
$output .= '' . __( 'DONATE NOW', STM_DOMAIN ) . '';
$output .= '</div>';
$output .= '</div>';
$output .= '</li>';
}
$output .= '</ul>';
wp_reset_query();
return $output;
}
}
if( defined( 'WPB_VC_VERSION' ) ){
new STM_VC_Causes();
}
?>
The custom VC element is showing up correctly for me on the admin backend side of the site (Screenshot 2 - https://i.stack.imgur.com/zKCgs.png), but I cannot figure out how to get the admin-inputted ID to show up on the frontend - no matter what, it still shows the most recent "Cause." Screenshot 3 (https://i.stack.imgur.com/13Qw4.png) is simply an example screenshot of what the individual "Cause" looks like when a page with the custom VC element is pushed live.
I've contacted the support team for the theme, but they only suggested this Post Types Order WP plugin, which only allows you to change the displayed "Cause" across the entire site, instead of allowing you to specify it in a page-by-page/post-by-post basis. I've also scoured Google/StackOverflow and tried various queries in the WP Codex, building out a custom shortcode (the custom VC element itself is a custom shortcode: [stm_causes]), but it just displays the most recent "Cause."
Edit 8/7/18:
I've made several edits to the causes.php template within the Savior theme, but for some reason, the updated WP_Query loop ended up not pulling in any data (Screenshot 3: https://i.stack.imgur.com/JLibO.png ).
The only exception is if I omit any ID in the VC editor backend; If I don't enter an ID, it defaults to the most recent Cause. However, if I enter any ID, even if it's the same ID as the most recent post, nothing shows up...
Any idea what could be wrong with my logic?
Thank you!
render function Fixed!
$fea_cause = $atts['id'];
$donations = new WP_Query( array( 'post_type' => 'donation', 'posts_per_page' => 1, 'post__in' => array($fea_cause )));
Huge props to Nilesh Sanura for his help!

Warning: implode(): Invalid arguments passed in line 2040

add_filter( 'woocommerce_form_field_args', 'custom_form_field_args', 10, 3 );
function custom_form_field_args( $args, $key, $value ) {
if ( $args['id'] == 'billing_city' ) {
$args = array(
'label' => __( 'Town / City', 'woocommerce' ),
'required' => TRUE,
'clear' => TRUE,
'type' => 'select',
'options' => array(
'' => ('Select City' ),
'Duliajan' => ('Duliajan' )
),
'class' => array( 'update_totals_on_change' )
);
} // elseif … and go on
return $args;
};
When I add the piece of code for a single city in (woocommerce) function.php, I got the following error.
Line number 2040-- $field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '">
Line no 2064-- $field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . $args['label'] . $required . '</label>';
How can I solve the error?
$args['input_class'] and $args['label_class'] must be arrays so they are probably not. Your should check:
if (!is_array($args['label_class']) {
// initialize to some empty array? depends on your application, what it needs to do
}

WooCommerce Multi Select for Single Product Field

I am trying to add the list box on the single product page, I was wondering no option for multiselect in woocommerce, For get_option fields woocommerce supporting multiselect but for post_meta woocommerce support only single select, Not sure is there any limitation in woocommerce or i could miss something to get multiselect? Here is the below code i tried
function create_multiselect() {
woocommerce_wp_select(array(
'id' => 'newoptions',
'class' => 'newoptions',
'label' => __('Testing Multiple Select', 'woocommerce'),
'options' => array(
'1' => 'User1',
'2' => 'User2',
'3' => 'User3',
))
);
}
add_action("woocommerce_product_options_pricing","create_multiselect");
Any Suggestion would be great.
It is not necessary to create a new function. The woocommerce_wp_select handles this out of the box. One of the attributes available is the custom_attributes which can accept an array. If you pass in an array('multiple'=>'multiple) then it renders a multiselect. In order to serialize and handle the input, you just provide a name[] in the name field and it works like magic. Here is an example -
woocommerce_wp_select(
array(
'id' => '_cb_days_available',
'name' => '_cb_days_available[]',
'label' => __('Days Offered', 'woocommerce'),
'description' => 'Which days of the week is this charter available?',
'default' => get_option('cb_open_days'),
'desc_tip' => 'true',
'class' => 'cb-admin-multiselect',
'options' => array(
'Mon' => 'Monday',
'Tue' => 'Tuesday',
'Wed' => 'Wednesday',
'Thu' => 'Thursday',
'Fri' => 'Friday',
'Sat' => 'Saturday',
'Sun' => 'Sunday'
),
'custom_attributes' => array('multiple' => 'multiple')
)
);
woocommerce_wp_select() function does not support multiselect, you can open the wc-meta-box-functions.php file in /woocommerce/includes/admin directory to see the default behavior.
But what's stopping you to create your own function that will be based on the default Woo function, and add the required features, or even change the default function ( but still, modifying the plugin files is not the best practice ). Here's an example of how to write a new function with multiple support, the only changes from original are added support for name and multiple attributes, and different logic for the selected items ( since post meta is now an array ).
function woocommerce_wp_select_multiple( $field ) {
global $thepostid, $post, $woocommerce;
$thepostid = empty( $thepostid ) ? $post->ID : $thepostid;
$field['class'] = isset( $field['class'] ) ? $field['class'] : 'select short';
$field['wrapper_class'] = isset( $field['wrapper_class'] ) ? $field['wrapper_class'] : '';
$field['name'] = isset( $field['name'] ) ? $field['name'] : $field['id'];
$field['value'] = isset( $field['value'] ) ? $field['value'] : ( get_post_meta( $thepostid, $field['id'], true ) ? get_post_meta( $thepostid, $field['id'], true ) : array() );
echo '<p class="form-field ' . esc_attr( $field['id'] ) . '_field ' . esc_attr( $field['wrapper_class'] ) . '"><label for="' . esc_attr( $field['id'] ) . '">' . wp_kses_post( $field['label'] ) . '</label><select id="' . esc_attr( $field['id'] ) . '" name="' . esc_attr( $field['name'] ) . '" class="' . esc_attr( $field['class'] ) . '" multiple="multiple">';
foreach ( $field['options'] as $key => $value ) {
echo '<option value="' . esc_attr( $key ) . '" ' . ( in_array( $key, $field['value'] ) ? 'selected="selected"' : '' ) . '>' . esc_html( $value ) . '</option>';
}
echo '</select> ';
if ( ! empty( $field['description'] ) ) {
if ( isset( $field['desc_tip'] ) && false !== $field['desc_tip'] ) {
echo '<img class="help_tip" data-tip="' . esc_attr( $field['description'] ) . '" src="' . esc_url( WC()->plugin_url() ) . '/assets/images/help.png" height="16" width="16" />';
} else {
echo '<span class="description">' . wp_kses_post( $field['description'] ) . '</span>';
}
}
echo '</p>';
}
Example of how to use the function:
woocommerce_wp_select_multiple( array(
'id' => 'newoptions',
'name' => 'newoptions[]',
'class' => 'newoptions',
'label' => __('Testing Multiple Select', 'woocommerce'),
'options' => array(
'1' => 'User1',
'2' => 'User2',
'3' => 'User3',
))
);

Categories