`, gated on WC 9.2 REST * support) — that is a payload-shape concern and stays in * {@see \WCPOS\WooCommercePOS\Sync\Config_Fingerprint}, which reads its key from * here. */ final class Barcode_Field { /** * The meta key used when the `barcode_field` setting is unset or blank. */ public const DEFAULT_FIELD = '_global_unique_id'; /** * Fields the settings picker must always offer. Both are WooCommerce product * PROPERTIES with their own accessors, not guaranteed postmeta rows, so * meta-key discovery cannot be relied on to surface them. * * @var string[] */ public const CORE_FIELDS = array( '_sku', self::DEFAULT_FIELD ); /** * The active barcode meta key. * * Read from production settings and coerced to a non-empty string: a blank * option falls back to the default rather than producing an empty meta key. * * @return string */ public static function meta_key(): string { $value = Settings::instance()->barcode_field(); return '' === trim( $value ) ? self::DEFAULT_FIELD : $value; } /** * Read a product's barcode. * * A custom meta key can hold anything another plugin put there, including an * array. Only scalars become a barcode — casting an array would emit an * "Array to string conversion" warning and serve the literal string "Array". * * @param \WC_Data $product The product or variation object. * * @return string */ public static function read( $product ): string { $barcode_field = self::meta_key(); if ( '_sku' === $barcode_field ) { return (string) $product->get_sku(); } // get_global_unique_id() requires WC 9.1+, fall back to raw meta on older versions. if ( self::DEFAULT_FIELD === $barcode_field && method_exists( $product, 'get_global_unique_id' ) ) { return (string) $product->get_global_unique_id(); } $value = $product->get_meta( $barcode_field ); return is_scalar( $value ) ? (string) $value : ''; } /** * Write a barcode onto a product and persist it. * * The SKU and GTIN paths take a FULL save (not `save_meta_data()`) so product * update hooks fire for sync. * * @param \WC_Data $product The product or variation object. * @param mixed $value The barcode value. */ public static function write( $product, $value ): void { $barcode_field = self::meta_key(); if ( '_sku' === $barcode_field ) { $product->set_sku( $value ); $product->save(); } elseif ( self::DEFAULT_FIELD === $barcode_field ) { if ( method_exists( $product, 'set_global_unique_id' ) ) { $product->set_global_unique_id( $value ); } else { // WC < 9.1 has no GTIN setter — write the same postmeta key it uses. $product->update_meta_data( $barcode_field, $value ); } // Full save (not save_meta_data) so product update hooks fire for sync. $product->save(); } else { $product->update_meta_data( $barcode_field, $value ); $product->save_meta_data(); } } /** * The postmeta keys a product/variation search covers: always `_sku`, plus the * active barcode field when it is something else. * * @return array */ public static function search_keys(): array { $keys = array( '_sku' ); $barcode_field = self::meta_key(); if ( '_sku' !== $barcode_field ) { $keys[] = $barcode_field; } return $keys; } /** * The postmeta key `orderby=barcode` sorts on. * * @return string */ public static function orderby_key(): string { return self::meta_key(); } }