docs/apis/store-api/extending-store-api/extend-store-api-add-custom-fields.md
This document describes how a developer can insert an input field into the Checkout block and have its value passed to the Store API so it's available when processing the checkout.
Developers can extend the Checkout block to add new inner blocks and process additional data through the checkout POST request. This involves leveraging the extensibility interfaces provided by Gutenberg and WooCommerce Blocks. This is demonstrated in more detail in our tutorial: Tutorial: Extending the WooCommerce Checkout Block .
Ensure you have the following files in your project:
index.js: Entry point for Webpack, imports, and registers the block type.edit.js: Handles the rendering of the block in the editor interface.block.json: Provides metadata and configurations for the block.block.js: Manages the block's state and user interactions.frontend.js: Registers the checkout block component for the frontend.Refer to this tutorial for an example of adding a custom shipping option to the checkout block.
To add a field block to the Checkout Block you will need to add the following entries to the block.json file of your block:
"parent": [ "woocommerce/checkout-shipping-methods-block" ],
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
}
woocommerce/checkout-shipping-methods-block. This means that your block will be rendered within the woocommerce/checkout-shipping-methods-block. If the shipping methods block is not required, your block will not be rendered.We can set the added field data to send it to the wc/store/checkout endpoint when processing orders using the function setExtensionData:
setExtensionData(
'namespace-of-your-block',
'key-of-your-data',
value
);
string - The namespace of your block.string - The key of your data.any - The value of your data.setExtensionData is passed to inner blocks via props.extensionData key of the wc/store/checkout data store.// block.js
export const Block = ( { checkoutExtensionData, extensions } ) => {
/**
* setExtensionData will update the wc/store/checkout data store with the values supplied. It
* can be used to pass data from the client to the server when submitting the checkout form.
*/
const { setExtensionData } = checkoutExtensionData;
}
// ... Some code here
useEffect( () => {
/**
* This code should use `setExtensionData` to update the `key-of-your-data` key
* in the `namespace-of-your-block` namespace of the checkout data store.
*/
setExtensionData(
'namespace-of-your-block',
'key-of-your-data',
value
);
}, [ setExtensionData, value ] );
Screenshots of Redux Dev tool showing the data store before and after the setExtensionData call:
| Before | After |
|---|---|
To process the added field data, we'll need extend the Store API to tell it to expect additional data. See more details in the Exposing your data in the Store API
We will use the following PHP files in our example:
custom-inner-block-blocks-integration.php file: Enqueue scripts, styles, and data on the frontend when the Checkout blocks is being used. See more details in the IntegrationInterface documentation.use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface;
/**
* Class for integrating with WooCommerce Blocks
*/
class Custom_Inner_Block_Blocks_Integration implements IntegrationInterface {
/**
* The name of the integration.
*
* @return string
*/
public function get_name() {
return 'new-field-block';
}
/**
* When called invokes any initialization/setup for the integration.
*/
public function initialize() {
// ... Some code here: (e.g. init functions that registers scripts and styles, and other instructions)
}
// ... Other functions here
}
custom-inner-block-extend-store-endpoint.php file: extends the Store API and adds hooks to save and display your new field block instructions. This doesn't save the data from the custom block anywhere by default, but you can add your own logic to save the data to the database.use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CheckoutSchema;
/**
* Your New Field Block Extend Store API.
*/
class Custom_Inner_Block_Extend_Store_Endpoint {
/**
* Stores Rest Extending instance.
*
* @var ExtendRestApi
*/
private static $extend;
/**
* Plugin Identifier, unique to each plugin.
*
* @var string
*/
const IDENTIFIER = 'new-field-block';
/**
* Bootstraps the class and hooks required data.
*
*/
public static function init() {
self::$extend = Automattic\WooCommerce\StoreApi\StoreApi::container()->get( Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema::class );
self::extend_store();
}
/**
* Registers the actual data into each endpoint.
*/
public static function extend_store() {
if ( is_callable( [ self::$extend, 'register_endpoint_data' ] ) ) {
self::$extend->register_endpoint_data(
[
'endpoint' => CheckoutSchema::IDENTIFIER,
'namespace' => self::IDENTIFIER,
'schema_callback' => [ 'Custom_Inner_Block_Extend_Store_Endpoint', 'extend_checkout_schema' ],
'schema_type' => ARRAY_A,
]
);
}
}
/**
* Register the new field block schema into the Checkout endpoint.
*
* @return array Registered schema.
*
*/
public static function extend_checkout_schema() {
return [
'Value_1' => [
'description' => 'A description of the field',
'type' => 'string', // ... type of the field, this should be a string
'context' => [ 'view', 'edit' ], // ... context of the field, this should be an array containing 'view' and 'edit'
'readonly' => true, // ... whether the field is readonly or not, this should be a boolean
'optional' => true, // ... whether the field is optional or not, this should be a boolean
],
// ... other values
];
}
}
new-field-block.php file: the main plugin file that loads the custom-inner-block-blocks-integration.php and custom-inner-block-extend-store-endpoint.php files.<?php
/**
* Plugin Name: New Field Block
* Version: 1.0
* Author: Your Name Here
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: new-field-block
*
* @package create-block
*/
// ... Some code here
/**
* Include the dependencies needed to instantiate the block.
*/
add_action(
'woocommerce_blocks_loaded',
function() {
require_once __DIR__ . '/custom-inner-block-blocks-integration.php';
// Initialize our store endpoint extension when WC Blocks is loaded.
Custom_Inner_Block_Extend_Store_Endpoint::init();
add_action(
'woocommerce_blocks_checkout_block_registration',
function( $integration_registry ) {
$integration_registry->register( new Custom_Inner_Block_Blocks_Integration() );
}
);
}
);
// ... Some code here
Here is an example from our tutorial of how to get this custom field's data while processing the checkout. This example is from the shipping-workshop-blocks-integration.php file. The complete code can be found in this GitHub repository.
private function save_shipping_instructions() {
/**
* We write a hook, using the `woocommerce_store_api_checkout_update_order_from_request` action
* that will update the order metadata with the shipping-workshop alternate shipping instruction.
*
* The documentation for this hook is at: https://github.com/woocommerce/woocommerce-blocks/blob/b73fbcacb68cabfafd7c3e7557cf962483451dc1/docs/third-party-developers/extensibility/hooks/actions.md#woocommerce_store_api_checkout_update_order_from_request
*/
add_action(
'woocommerce_store_api_checkout_update_order_from_request',
function( \WC_Order $order, \WP_REST_Request $request ) {
$shipping_workshop_request_data = $request['extensions'][$this->get_name()];
$alternate_shipping_instruction = $shipping_workshop_request_data['alternateShippingInstruction'];
$other_shipping_value = $shipping_workshop_request_data['otherShippingValue'];
$order->update_meta_data( 'shipping_workshop_alternate_shipping_instruction', $alternate_shipping_instruction );
$order->save();
},
10,
2
);
}
By following the steps above, you can add and process new field blocks in the WooCommerce checkout block. For complete implementation and additional examples, refer to the provided tutorial and the corresponding GitHub repository.