namespace Automattic\WooCommerce\Blocks\Domain\Services;
use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;
* Service class managing checkout fields and its related extensibility points in the admin area.
class CheckoutFieldsAdmin {
* Checkout field controller.
private $checkout_fields_controller;
* @param CheckoutFields $checkout_fields_controller Instance of the checkout field controller.
public function __construct( CheckoutFields $checkout_fields_controller ) {
$this->checkout_fields_controller = $checkout_fields_controller;
* Initialize hooks. This is not run Store API requests.
add_filter( 'woocommerce_admin_billing_fields', array( $this, 'admin_address_fields' ), 10, 3 );
add_filter( 'woocommerce_admin_billing_fields', array( $this, 'admin_contact_fields' ), 10, 3 );
add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_address_fields' ), 10, 3 );
add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_order_fields' ), 10, 3 );
* Converts the shape of a checkout field to match whats needed in the WooCommerce meta boxes.
* @param array $field The field to format.
* @param string $key The field key. This will be used for the ID of the field when passed to the meta box.
* @return array Formatted field.
protected function format_field_for_meta_box( $field, $key ) {
$formatted_field = array(
'label' => $field['label'],
'value' => $field['value'],
'type' => $field['type'],
'update_callback' => array( $this, 'update_callback' ),
'wrapper_class' => 'form-field-wide',
if ( 'select' === $field['type'] ) {
$formatted_field['options'] = array_column( $field['options'], 'label', 'value' );
if ( 'checkbox' === $field['type'] ) {
$formatted_field['checked_value'] = '1';
$formatted_field['unchecked_value'] = '0';
* Updates a field value for an order.
* @param string $key The field key.
* @param mixed $value The field value.
* @param \WC_Order $order The order to update the field for.
public function update_callback( $key, $value, $order ) {
list( $group, $key ) = explode( '/', $key, 2 );
$group = CheckoutFields::get_group_name( $group );
$this->checkout_fields_controller->persist_field_for_order( $key, $value, $order, $group, false );
* Injects address fields in WC admin orders screen.
* @param array $fields The fields to show.
* @param \WC_Order|boolean $order The order to show the fields for.
* @param string $context The context to show the fields for.
public function admin_address_fields( $fields, $order = null, $context = 'edit' ) {
if ( ! $order instanceof \WC_Order ) {
$group_name = doing_action( 'woocommerce_admin_billing_fields' ) ? 'billing' : 'shipping';
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'address', $group_name, $context );
foreach ( $additional_fields as $key => $field ) {
$prefixed_key = CheckoutFields::get_group_key( $group_name ) . $key;
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
* Injects contact fields in WC admin orders screen.
* @param array $fields The fields to show.
* @param \WC_Order|boolean $order The order to show the fields for.
* @param string $context The context to show the fields for.
public function admin_contact_fields( $fields, $order = null, $context = 'edit' ) {
if ( ! $order instanceof \WC_Order ) {
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'contact', 'other', $context );
foreach ( $additional_fields as $key => $field ) {
$prefixed_key = CheckoutFields::get_group_key( 'other' ) . $key;
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
return array_merge( $fields, $additional_fields );
* Injects additional fields in WC admin orders screen.
* @param array $fields The fields to show.
* @param \WC_Order|boolean $order The order to show the fields for.
* @param string $context The context to show the fields for.
public function admin_order_fields( $fields, $order = null, $context = 'edit' ) {
if ( ! $order instanceof \WC_Order ) {
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'order', 'other', $context );
foreach ( $additional_fields as $key => $field ) {
$prefixed_key = CheckoutFields::get_group_key( 'other' ) . $key;
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
return array_merge( $fields, $additional_fields );