Commit a4d8c4f145 for woocommerce
commit a4d8c4f145024ab58182f04395d6b29ee3655a74
Author: Brian <brian@brianhaas.li>
Date: Tue Jan 27 18:52:36 2026 +0100
Include GTIN, UPC, EAN or ISBN on the admin text search (#60052)
* add GTIN to product admin search
* add gtin search on variations as well
---------
Co-authored-by: Brandon Kraft <public@brandonkraft.com>
diff --git a/plugins/woocommerce/changelog/pr-60052 b/plugins/woocommerce/changelog/pr-60052
new file mode 100644
index 00000000000..36a9cf1e940
--- /dev/null
+++ b/plugins/woocommerce/changelog/pr-60052
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Add global identifier (GTIN/UPC/EAN/ISBN) support to admin product search.
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
index 19e379759dd..6098c7b98e7 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
@@ -1957,13 +1957,14 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
// phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- an array of placeholders is a valid arg.
$term_query = $wpdb->prepare(
- '( posts.post_title LIKE %s ) OR ( posts.post_excerpt LIKE %s ) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s )',
- array_fill( 0, 4, $like )
+ '( posts.post_title LIKE %s ) OR ( posts.post_excerpt LIKE %s ) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s ) OR ( wc_product_meta_lookup.global_unique_id LIKE %s )',
+ array_fill( 0, 5, $like )
);
// Variations should also search the parent's meta table for fallback fields.
if ( $include_variations ) {
$term_query .= $wpdb->prepare( " OR ( wc_product_meta_lookup.sku = '' AND parent_wc_product_meta_lookup.sku LIKE %s )", $like );
+ $term_query .= $wpdb->prepare( " OR ( wc_product_meta_lookup.global_unique_id = '' AND parent_wc_product_meta_lookup.global_unique_id LIKE %s )", $like );
}
$term_group_query[] = "( {$term_query} )";
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php b/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
index 4f8d9798b1b..722fe5ebf41 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
@@ -900,6 +900,116 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$this->assertContains( $product4->get_id(), $results );
}
+ /**
+ * @testdox Test searching products by global_unique_id (EAN/barcode).
+ *
+ * @return void
+ */
+ public function test_search_products_by_global_unique_id() {
+ $product = new WC_Product();
+ $product->set_regular_price( 42 );
+ $product->set_name( 'Product A' );
+ $product->set_sku( 'PROD-A' );
+ $product->set_global_unique_id( '4521121209036' );
+ $product->save();
+
+ $product2 = new WC_Product();
+ $product2->set_regular_price( 42 );
+ $product2->set_name( 'Product B' );
+ $product2->set_sku( 'PROD-B' );
+ $product2->set_global_unique_id( '1234567890123' );
+ $product2->save();
+
+ $product3 = new WC_Product();
+ $product3->set_regular_price( 42 );
+ $product3->set_name( 'Product C' );
+ $product3->set_sku( 'PROD-C' );
+ $product3->set_global_unique_id( '9876543210987' );
+ $product3->save();
+
+ $data_store = WC_Data_Store::load( 'product' );
+
+ // Full barcode search.
+ $results = $data_store->search_products( '4521121209036', '', true, true );
+ $this->assertContains( $product->get_id(), $results );
+ $this->assertNotContains( $product2->get_id(), $results );
+ $this->assertNotContains( $product3->get_id(), $results );
+
+ // Partial barcode search.
+ $results = $data_store->search_products( '123456', '', true, true );
+ $this->assertNotContains( $product->get_id(), $results );
+ $this->assertContains( $product2->get_id(), $results );
+ $this->assertNotContains( $product3->get_id(), $results );
+
+ // Another barcode search.
+ $results = $data_store->search_products( '9876543210987', '', true, true );
+ $this->assertNotContains( $product->get_id(), $results );
+ $this->assertNotContains( $product2->get_id(), $results );
+ $this->assertContains( $product3->get_id(), $results );
+ }
+
+ /**
+ * @testdox Test searching variations by global_unique_id.
+ *
+ * @return void
+ */
+ public function test_search_products_by_global_unique_id_with_variations() {
+ $variable_product = new WC_Product_Variable();
+ $variable_product->set_name( 'Variable Product' );
+ $variable_product->set_global_unique_id( '1111111111111' );
+ $variable_product->save();
+
+ // Variation with its own barcode.
+ $variation1 = new WC_Product_Variation();
+ $variation1->set_parent_id( $variable_product->get_id() );
+ $variation1->set_global_unique_id( '2222222222222' );
+ $variation1->save();
+
+ // Variation without barcode - inherits from parent.
+ $variation2 = new WC_Product_Variation();
+ $variation2->set_parent_id( $variable_product->get_id() );
+ $variation2->set_global_unique_id( '' );
+ $variation2->save();
+
+ $data_store = WC_Data_Store::load( 'product' );
+
+ // Search parent barcode - finds parent and variation that inherits.
+ $results = $data_store->search_products( '1111111111111', '', true, true );
+ $this->assertContains( $variable_product->get_id(), $results );
+ $this->assertContains( $variation2->get_id(), $results );
+
+ // Search variation barcode - finds variation and parent (WC includes parents when variations match).
+ $results = $data_store->search_products( '2222222222222', '', true, true );
+ $this->assertContains( $variation1->get_id(), $results );
+ $this->assertContains( $variable_product->get_id(), $results );
+ $this->assertNotContains( $variation2->get_id(), $results );
+ }
+
+ /**
+ * @testdox Test that products with NULL global_unique_id do not interfere with search results.
+ *
+ * @return void
+ */
+ public function test_search_products_by_global_unique_id_with_null_value() {
+ $product_with_id = new WC_Product();
+ $product_with_id->set_regular_price( 42 );
+ $product_with_id->set_name( 'Product With ID' );
+ $product_with_id->set_global_unique_id( '5555555555555' );
+ $product_with_id->save();
+
+ $product_null_id = new WC_Product();
+ $product_null_id->set_regular_price( 42 );
+ $product_null_id->set_name( 'Product Null ID' );
+ $product_null_id->save();
+
+ $data_store = WC_Data_Store::load( 'product' );
+
+ // Search should find only the product with the matching global_unique_id.
+ $results = $data_store->search_products( '5555555555555', '', true, true );
+ $this->assertContains( $product_with_id->get_id(), $results );
+ $this->assertNotContains( $product_null_id->get_id(), $results );
+ }
+
/**
* Test WC_Product_Data_Store_CPT::create_all_product_variations
*/