WooCommerce

WooCommerce自定义结账字段(2021)

WooCommerce自定义结账字段的插件有Custom Checkout Fields for WooCommerce等,如果你想要更多的自由,就用代码来解决吧。本文介绍一种用代码来自定义WooCommerce结账字段的方法。

WooCommerce自定义结账字段代码

代码单独保存一个文件引入,或直接放到子主题的functions.php中,第二种方式请将 defined('WPINC') or exit; 这一段删掉。

<?php
// Exit if accessed directly.
defined('WPINC') or exit;
/**
 * A class for creating custom checkout fields, please use the template below to add fields
 *
 * $field = array(
		// type support 
		// text,datetime,datetime-local,date,month,month,time,number,email,url,tel,checkbox,textarea,hidden,select,radio,state,country
		'type'                  => 'text', 
		'label'                 => '',
		'description'           => '',
		'placeholder'           => '',
		'maxlength'             => false,
		'required'              => false,
		'autocomplete'          => false,
		'id'                    => $key,
		'class'                 => array(),
		'label_class'           => array(),
		'input_class'           => array(),
		'return'                => false, // true to return the html string
		'options'               => array(),
		'custom_attributes'     => array(),
		'validate'              => array(), // add a validate-classname to the field, could be used to do custom validation
		'default'               => '',
		'autofocus'             => '',
		'priority'              => '',
		'save_usermeta'         => false, // Do not save this value to usermeta
		'show_in_email'         => true,
		'show_in_order_details' => true,
		'show_in_admin'         => true,
		'display_position'      => '1'
	);
 */
class Sola_Custom_WC_Checkout_Fields{
	private $field_names     = array( 'billing_fields','shipping_fields','special_fields');
	private $shipping_fields = false;
	private $billing_fields  = false;
	private $special_fields  = false;
	public function __construct( $billing_fields = false, $shipping_fields = false, $special_fields = false ){
		if( is_array($billing_fields) && sizeof($billing_fields) ){
			$this->billing_fields = $billing_fields;
		}
		if( is_array($shipping_fields) && sizeof($shipping_fields) ){
			$this->shipping_fields = $shipping_fields;
		}
		if( is_array($special_fields) && sizeof($special_fields) ){
			$this->special_fields = $special_fields;
		}
		if( $this->billing_fields || $this->shipping_fields || $this->special_fields ){
			$this->init();
		}
		
	}

	function init(){
		$this->show_fields_for_checkout();
		$this->validate_fields();
		/**
		 * Check if the fields need to be saved to usermeta
		 */
		add_action( 'woocommerce_checkout_update_customer',[$this,'may_update_customer'],10, 2 );
		$this->save_special_fields();
		$this->display_fields_for_order_details();
		$this->display_fields_for_admin();
		$this->display_fields_for_email();
	
	}

	function show_fields_for_checkout(){
		/**
		 * Define checkout fields
		 * Fields defined in checkout_fields array is automatically processed and saved to the order post meta
		 * 
		 */
		add_filter( 'woocommerce_checkout_fields' , [$this,'override_checkout_fields'] );

		/**
		 * Define special custom fields which are displayed after order notes
		 * 
		 */
		add_action( 'woocommerce_after_order_notes', [$this,'custom_special_fields'] );
	}
	function override_checkout_fields( $checkout_fields ) {
		if( $this->billing_fields ){
			$checkout_fields['billing'] += $this->billing_fields;
		}
		if( $this->shipping_fields ){
			$checkout_fields['shipping'] += $this->shipping_fields;
		}
		
	    return $checkout_fields;
	}

	function custom_special_fields( $checkout ) {
		if( ! $this->special_fields ){
			return $checkout;
		}
		echo '<div class="cp-checkout-special-fields">';
		foreach( $this->special_fields as $field_key => $field ){
			woocommerce_form_field( $field_key, $field, $checkout->get_value($field_key) );
		}
		echo '</div>';
	}

	/**
	 * Validate fields, both for normal fields and special fields
	 * 
	 */
	function validate_fields(){
		
		add_action( 'woocommerce_checkout_process', [$this,'validate_all_checkout_fields'] );
	}
	function validate_all_checkout_fields(){
		if( $this->special_fields ){
			foreach( $this->special_fields as $field_key => $field ){
				if( $field['required'] && isset($_POST[$field_key]) && ! $_POST[$field_key] ){
					wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), '<strong>' . esc_html( $field['label'] ) . '</strong>' ), 'error' );
				}
			}
		}
	}

	/**
	 * Wheather to save data to custom user meta or not
	 * If saved to user meta, the fields will be populated with saved data on checkout page
	 * 
	 */
	function may_update_customer( $customer, $data ){
		$this->maybe_not_save_to_usermeta( $this->billing_fields, $customer );
		$this->maybe_not_save_to_usermeta( $this->shipping_fields, $customer );
	}
	function maybe_not_save_to_usermeta( $fields, $customer ){
		if( $fields ){
			foreach( $fields as $field_key => $field ){
				if( isset($field['save_usermeta']) && ! $field['save_usermeta'] ){
					$customer->delete_meta_data($field_key);
				}
			}
		}	
	}

	/**
	 * Save the fields after order notes to order post meta
	 * 
	 */
	function save_special_fields(){
		
		add_action( 'woocommerce_checkout_update_order_meta', [$this,'save_speical_fields_to_order_meta'] );
	}
	function save_speical_fields_to_order_meta( $order_id ) {
		if( $this->special_fields ){
			foreach( $this->special_fields as $field_key => $field ){
				if ( ! empty( $_POST[$field_key] ) ) {
				    update_post_meta( $order_id, '_' . $field_key, sanitize_text_field( $_POST[$field_key] ) );
				}
			}
		}
	}

	/**
	 * Display fields in order details
	 * 
	 */
	function display_fields_for_order_details(){
		/**
		 * Position 1 - Display inside order total after shipping method
		 */
		add_filter( 'woocommerce_get_order_item_totals', [$this, 'display_after_shipping_method'], 10, 2 );
		/**
		 * Position 2 - Display after order total
		 */
		add_action( 'woocommerce_order_details_after_order_table', [$this,'display_after_order_details'] );
		
	}
	function display_after_shipping_method( $total_rows, $order ){
		$new_row = array();
		foreach( $this->field_names as $field_name ){
			if( $formatted = $this->get_field_values( $this->$field_name, $order ) ){
				
				foreach( $formatted as $field ){
					if( isset($field['display_position']) && ($field['display_position'] != '1')){
						continue;
					}
					$new_row[$field['field_key']] = array(
						'label' => $field['label'],
						'value' => $field['value']
					);
				}
			}
		}
		if( sizeof($new_row) ){
			$total_rows = array_merge( array_splice( $total_rows,0,2), $new_row, $total_rows );
		}
		
		
		return $total_rows;
	}
	function display_after_order_details( $order ){
		ob_start();
		$html = '';
		foreach( $this->field_names as $field_name ){
			if( $formatted = $this->get_field_values( $this->$field_name, $order ) ){
				foreach( $formatted as $field ){
					if( isset($field['display_position']) && $field['display_position'] != '2' ){
						continue;
					}
					if( isset($field['show_in_order_details']) && ! $field['show_in_order_details'] ){
						continue;
					}
					echo '<tr><th>', $field['label'], '</th><td>', $field['value'],'</td></tr>';
				}
			}
		}
		$html= ob_get_clean();
		if( $html ){
			echo '<table class="shop_table order_details order_extra"><tbody>', $html,'</tbody></table>';
		}
		
	}

	/**
	 * Display fields in user email
	 * 
	 */
	function display_fields_for_email(){
		add_filter( 'woocommerce_email_order_meta_fields', [$this,'display_after_email_item_details'],10, 3 );
	}
	function display_after_email_item_details( $fields, $sent_to_admin, $order ){
		$billing_fields  = $this->get_email_order_meta_keys( $this->billing_fields, $order );
		$shipping_fields = $this->get_email_order_meta_keys( $this->shipping_fields, $order );
		$special_fields  = $this->get_email_order_meta_keys( $this->special_fields, $order );
		
		return $billing_fields + $shipping_fields + $special_fields;
	}
	
	function get_email_order_meta_keys( $fields, $order ){
		$formatted = array();
		if( $fields ){
			$field_values = $this->get_field_values( $fields, $order );
			foreach( $field_values as $field ){
				if( isset($field['display_position']) && ($field['display_position'] != '2') ){
					continue;
				}
				if( isset($field['show_in_email']) && ! $field['show_in_email'] ){
					continue;
				}
				$value = $field['value'];
				$field_key = $field['field_key'];
				$formatted[$field_key] = array(
					'label' => $field['label'],
					'value' => $value
				);	
			}
		}
		return $formatted;
	}

	/**
	 * Display meta info in admin order after billing address
	 * 
	 */
	function display_fields_for_admin(){
		add_action( 'woocommerce_admin_order_data_after_billing_address', [$this,'display_in_admin_order_meta_billing'],20 );
		add_action( 'woocommerce_admin_order_data_after_shipping_address', [$this,'display_in_admin_order_meta_shipping'],20 );
	}
	function display_in_admin_order_meta_billing( $order ){
		$this->admin_show_fields( $this->special_fields, $order );
		$this->admin_show_fields( $this->billing_fields, $order );
	}
	function display_in_admin_order_meta_shipping( $order ){
		$this->admin_show_fields( $this->shipping_fields, $order );
	}
	function admin_show_fields( $fields, $order ){
		if( $formatted = $this->get_field_values( $fields, $order ) ){
			foreach( $formatted as $field ){
				if( isset($field['show_in_admin']) && ! $field['show_in_admin'] ){
					continue;
				}
				echo '<p><strong style="display:block">', $field['label'], ':</strong> ', $field['value'],'</p>';
			}
		}
	}

	function get_field_values( $fields, $order  ){
		$formatted = array();
		if( $fields ){
			foreach( $fields as $field_key => $field ){
				$saved_field_value = $this->get_order_meta( $field_key, $order );
				if( in_array($field['type'], array('select','radio','checkbox') ) ){
					$saved_field_value = $field['options'][$saved_field_value] ?? false;
				} 
				$formatted[] = array(
					'label'                 => esc_html($field['label']),
					'value'                 => $saved_field_value,
					'show_in_email'         => $field['show_in_email'] ?? true,
					'show_in_admin'         => $field['show_in_admin'] ?? true,
					'show_in_order_details' => $field['show_in_order_details'] ?? true,
					'field_key'             => $field_key,
					'display_position'	    => $field['display_position'] ?? '1'
				);
			}
			return $formatted;
		}
		return false;
	}

	function get_order_meta( $field_key, $order ){
		return get_post_meta( $order->get_id(), '_' . $field_key,true );
	}
}

代码如何使用

定义好 Sola_Custom_WC_Checkout_Fields 这个class后,我们来使用它。继续在functions.php里写。

add_action( 'init', function(){
  $billing_fields = array(
    'billing_field_company_type' => array(
      'type'          => 'select',
      'label'         => '公司类型',
      'placeholder'   => '',
      'required'      => false,
      'class'         => array('form-row-wide'),
      'label_class'   => array(),
      'clear'         => true,
      'priority'      => 20,
      'options'       => array('1'=>'有限责任公司','2' => '个人独资企业', '3' => '外商独资公司'),
      'default'       => '1',
      'display_position' => '1'
    ),
  );
  $shipping_fields = array();
  
  $special_fields = array(
    'order_dropshipping' => array(
      'type'          => 'radio',
      'label'         => '是否为Dropshipping订单',
      'placeholder'   => '',
      'required'      => false,
      'class'         => array('form-row-wide'),
      'label_class'   => array(),
      'clear'         => true,
      'priority'      => 1,
      'options'       => array('yes'=>'是','no' => '否'),
      'default'       => 'no',
      'display_position' => '2'
    ),
    
  );
  new Sola_Custom_WC_Checkout_Fields( $billing_fields,$shipping_fields, $special_fields );
});

定义了两个字段,billing表单里增加公司类型,order notes下面增加 是否为Dropshipping订单 。然后下单,结账,效果如下。

WooCommerce自定义结账字段
WooCommerce自定义结账字段

结算完成后,订单详情和后台订单详情显示自定义字段。

WooCommerce自定义结账字段 - 订单详情
WooCommerce自定义结账字段 - 后台订单详情

邮件里也会显示。

邮件自定义字段

留言

您的电子邮箱地址不会被公开。