GeneratorsWooCommerceProduct Fields

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.

product-details.php
Saved just now
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' );