Product Fields Generator
Fields in an existing Product Data tab or a new one, plus the save handler that writes them — through the product's own CRUD methods, not a raw postmeta write.
Placement
Limit to product types
Adds a new “Product Details” tab. Limited to simple, variable products — hidden elsewhere and skipped on save.
Fields
4 fields · keys prefixed _acme_
Manufacturer part number_acme_manufacturer_part_no
Assembly required_acme_assembly_required
Assembly difficulty_acme_difficulty
Care instructions_acme_care_instructions
Save method
Show on the single product page
Prints the values on the frontend, read back through $product->get_meta().
Hide the row when empty
Skips the frontend output for a field with no saved value.
Clean up on uninstall
delete_post_meta_by_key() for each key, as an uninstall.php comment.
REST
Expose to REST and the block editor
Registers every field with register_post_meta() on the product post type, authorised per-product.
A complete single-file plugin that declares WooCommerce as a dependency.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: Product Details * Description: Adds custom fields to WooCommerce products and saves their values. * Version: 1.0.0 * Requires PHP: 7.4 * Requires Plugins: woocommerce * Text Domain: acme */ defined( 'ABSPATH' ) || exit; /** * Add the Product Details tab. * * @param array $tabs Existing tabs. * @return array */ function acme_add_product_tab( $tabs ) { $tabs['product_details'] = array( 'label' => __( 'Product Details', 'acme' ), 'target' => 'product_details_data', 'class' => array( 'show_if_simple', 'show_if_variable' ), 'priority' => 21, ); return $tabs; } add_filter( 'woocommerce_product_data_tabs', 'acme_add_product_tab' ); /** * Render the panel. */ function acme_product_tab_panel() { echo '<div id="product_details_data" class="panel woocommerce_options_panel">'; echo '<div class="options_group">'; woocommerce_wp_text_input( array( 'id' => '_acme_manufacturer_part_no', 'label' => __( 'Manufacturer part number', 'acme' ), 'desc_tip' => true, 'description' => __( 'Printed on the packing slip.', 'acme' ), ) ); woocommerce_wp_checkbox( array( 'id' => '_acme_assembly_required', 'label' => __( 'Assembly required', 'acme' ), 'description' => __( 'Shows an assembly notice on the product page.', 'acme' ), ) ); woocommerce_wp_select( array( 'id' => '_acme_difficulty', 'label' => __( 'Assembly difficulty', 'acme' ), 'options' => array( 'easy' => __( 'Easy', 'acme' ), 'moderate' => __( 'Moderate', 'acme' ), 'advanced' => __( 'Advanced', 'acme' ), ), ) ); woocommerce_wp_textarea_input( array( 'id' => '_acme_care_instructions', 'label' => __( 'Care instructions', 'acme' ), ) ); echo '</div>'; echo '</div>'; } add_action( 'woocommerce_product_data_panels', 'acme_product_tab_panel' ); /** * Save the fields. * * @param int $post_id Product ID. */ function acme_save_product_fields( $post_id ) { $product = wc_get_product( $post_id ); if ( ! $product ) { return; } if ( ! in_array( $product->get_type(), array( 'simple', 'variable' ), true ) ) { return; } if ( isset( $_POST['_acme_manufacturer_part_no'] ) ) { $product->update_meta_data( '_acme_manufacturer_part_no', wc_clean( wp_unslash( $_POST['_acme_manufacturer_part_no'] ) ) ); } $product->update_meta_data( '_acme_assembly_required', isset( $_POST['_acme_assembly_required'] ) ? 'yes' : 'no' ); if ( isset( $_POST['_acme_difficulty'] ) && in_array( sanitize_key( $_POST['_acme_difficulty'] ), array( 'easy', 'moderate', 'advanced' ), true ) ) { $product->update_meta_data( '_acme_difficulty', sanitize_key( $_POST['_acme_difficulty'] ) ); } if ( isset( $_POST['_acme_care_instructions'] ) ) { $product->update_meta_data( '_acme_care_instructions', sanitize_textarea_field( wp_unslash( $_POST['_acme_care_instructions'] ) ) ); } $product->save(); } add_action( 'woocommerce_process_product_meta', 'acme_save_product_fields' ); /** * Show the values on the single product page. */ function acme_display_product_fields() { global $product; if ( ! $product ) { return; } $manufacturer_part_no = $product->get_meta( '_acme_manufacturer_part_no', true ); if ( '' !== $manufacturer_part_no ) { printf( '<span class="acme-manufacturer-part-no"><strong>%1$s:</strong> %2$s</span>', esc_html__( 'Manufacturer part number', 'acme' ), esc_html( $manufacturer_part_no ) ); } if ( 'yes' === $product->get_meta( '_acme_assembly_required', true ) ) { printf( '<span class="acme-assembly-required">%s</span>', esc_html__( 'Assembly required', 'acme' ) ); } $difficulty = $product->get_meta( '_acme_difficulty', true ); if ( '' !== $difficulty ) { printf( '<span class="acme-difficulty"><strong>%1$s:</strong> %2$s</span>', esc_html__( 'Assembly difficulty', 'acme' ), esc_html( $difficulty ) ); } $care_instructions = $product->get_meta( '_acme_care_instructions', true ); if ( '' !== $care_instructions ) { printf( '<span class="acme-care-instructions"><strong>%1$s:</strong> %2$s</span>', esc_html__( 'Care instructions', 'acme' ), esc_html( $care_instructions ) ); } } add_action( 'woocommerce_product_meta_end', 'acme_display_product_fields' ); /** * Expose the fields to the REST API and the block editor. */ function acme_register_product_meta() { register_post_meta( 'product', '_acme_manufacturer_part_no', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'wc_clean', 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) { return current_user_can( 'edit_product', $post_id ); }, ) ); register_post_meta( 'product', '_acme_assembly_required', array( 'type' => 'boolean', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => static function ( $value ) { return $value ? 'yes' : 'no'; }, 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) { return current_user_can( 'edit_product', $post_id ); }, ) ); register_post_meta( 'product', '_acme_difficulty', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_key', 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) { return current_user_can( 'edit_product', $post_id ); }, ) ); register_post_meta( 'product', '_acme_care_instructions', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => 'sanitize_textarea_field', 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) { return current_user_can( 'edit_product', $post_id ); }, ) ); } add_action( 'init', 'acme_register_product_meta' );