diff --git a/composer.lock b/composer.lock index 02eed15e..8ed44b75 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "368f55c5f0ddcebd7fbe9b5e2269097e", + "content-hash": "52c0e5c7327026f3726997264931cab3", "packages": [ { "name": "firebase/php-jwt", @@ -1391,16 +1391,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -1438,7 +1438,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -1458,7 +1458,7 @@ "type": "tidelift" } ], - "time": "2026-04-13T15:52:40+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1961,16 +1961,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { @@ -2024,7 +2024,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { @@ -2044,7 +2044,7 @@ "type": "tidelift" } ], - "time": "2026-03-28T09:44:51+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { "name": "symfony/string", diff --git a/includes/class-type-registry.php b/includes/class-type-registry.php index d7c4e557..4e6b85ad 100644 --- a/includes/class-type-registry.php +++ b/includes/class-type-registry.php @@ -137,6 +137,7 @@ class Type_Registry { /** * Object fields. */ + Type\WPObject\Product_Brand_Type::register_fields(); Type\WPObject\Product_Category_Type::register_fields(); Type\WPObject\Root_Query::register_fields(); diff --git a/includes/class-wp-graphql-woocommerce.php b/includes/class-wp-graphql-woocommerce.php index 40c879e7..23935259 100644 --- a/includes/class-wp-graphql-woocommerce.php +++ b/includes/class-wp-graphql-woocommerce.php @@ -290,6 +290,7 @@ if ( ! class_exists( '\WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce' ) ) : require $include_directory_path . 'type/object/class-product-attribute-object-type.php'; require $include_directory_path . 'type/object/class-product-attribute-term-object-type.php'; require $include_directory_path . 'type/object/class-product-attribute-types.php'; + require $include_directory_path . 'type/object/class-product-brand-type.php'; require $include_directory_path . 'type/object/class-product-category-type.php'; require $include_directory_path . 'type/object/class-product-download-type.php'; require $include_directory_path . 'type/object/class-product-types.php'; diff --git a/includes/type/object/class-product-brand-type.php b/includes/type/object/class-product-brand-type.php new file mode 100644 index 00000000..7d78729d --- /dev/null +++ b/includes/type/object/class-product-brand-type.php @@ -0,0 +1,43 @@ + [ + 'type' => 'MediaItem', + 'description' => static function () { + return __( 'Product brand image', 'graphql-for-ecommerce' ); + }, + 'resolve' => static function ( $source, array $args, AppContext $context ) { + $thumbnail_id = get_term_meta( $source->term_id, 'thumbnail_id', true ); + return ! empty( $thumbnail_id ) + ? $context->get_loader( 'post' )->load_deferred( $thumbnail_id ) + : null; + }, + ], + ] + ); + } +} diff --git a/tests/wpunit/ProductBrandQueriesTest.php b/tests/wpunit/ProductBrandQueriesTest.php index 54019197..0f2a7f84 100644 --- a/tests/wpunit/ProductBrandQueriesTest.php +++ b/tests/wpunit/ProductBrandQueriesTest.php @@ -96,6 +96,58 @@ class ProductBrandQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooG $this->assertQuerySuccessful( $response, $expected ); } + /** + * Test that productBrand image resolves from WooCommerce term thumbnail meta. + */ + public function testProductBrandImage() { + $brand_id = $this->createProductBrand( 'brand-with-image' ); + $image_id = $this->factory->post->create( + [ + 'post_author' => $this->shop_manager, + 'post_status' => 'publish', + 'post_title' => 'Brand Image', + 'post_type' => 'attachment', + ] + ); + + update_term_meta( $brand_id, 'thumbnail_id', $image_id ); + + $query = ' + query ($id: ID!) { + productBrand(id: $id, idType: SLUG) { + databaseId + image { + id + } + } + productBrands(first: 100) { + nodes { + slug + image { + id + } + } + } + } + '; + + $variables = [ 'id' => 'brand-with-image' ]; + $response = $this->graphql( compact( 'query', 'variables' ) ); + $expected = [ + $this->expectedField( 'productBrand.databaseId', $brand_id ), + $this->expectedField( 'productBrand.image.id', $this->toRelayId( 'post', $image_id ) ), + $this->expectedNode( + 'productBrands.nodes', + [ + $this->expectedField( 'slug', 'brand-with-image' ), + $this->expectedField( 'image.id', $this->toRelayId( 'post', $image_id ) ), + ] + ), + ]; + + $this->assertQuerySuccessful( $response, $expected ); + } + /** * Test the connection from productBrand to products. */