2019-03-21 21:43:24 -04:00
|
|
|
<?php
|
2019-03-22 16:36:34 -04:00
|
|
|
/**
|
2026-03-27 16:32:30 -04:00
|
|
|
* Registers WooCommerce post types and related schema filters.
|
2019-03-22 16:36:34 -04:00
|
|
|
*
|
2019-10-25 19:13:36 -04:00
|
|
|
* @package \WPGraphQL\WooCommerce
|
2019-03-22 16:36:34 -04:00
|
|
|
* @since 0.0.1
|
|
|
|
|
*/
|
2019-03-21 21:43:24 -04:00
|
|
|
|
2019-10-25 19:13:36 -04:00
|
|
|
namespace WPGraphQL\WooCommerce;
|
2019-03-21 21:43:24 -04:00
|
|
|
|
2022-12-07 15:04:39 -07:00
|
|
|
use GraphQL\Error\UserError;
|
2019-12-09 18:49:19 -05:00
|
|
|
use WPGraphQL\WooCommerce\Data\Factory;
|
2023-07-19 23:01:50 +03:00
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_CPT_Loader;
|
2025-11-14 14:30:01 -05:00
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Cart_Item_Loader;
|
2023-07-19 23:01:50 +03:00
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Customer_Loader;
|
2025-11-14 14:30:01 -05:00
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Downloadable_Item_Loader;
|
|
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Order_Item_Loader;
|
|
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Shipping_Method_Loader;
|
|
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Shipping_Zone_Loader;
|
|
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Tax_Class_Loader;
|
|
|
|
|
use WPGraphQL\WooCommerce\Data\Loader\WC_Tax_Rate_Loader;
|
2023-04-18 23:48:08 -04:00
|
|
|
use WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce as WooGraphQL;
|
2019-03-24 20:22:05 -04:00
|
|
|
|
2019-03-21 21:43:24 -04:00
|
|
|
/**
|
2026-03-27 16:32:30 -04:00
|
|
|
* Class Post_Types
|
2019-03-21 21:43:24 -04:00
|
|
|
*/
|
2026-03-27 16:32:30 -04:00
|
|
|
class Post_Types {
|
2019-03-21 21:43:24 -04:00
|
|
|
/**
|
|
|
|
|
* Register filters
|
2023-06-13 23:17:02 +03:00
|
|
|
*
|
|
|
|
|
* @return void
|
2019-03-21 21:43:24 -04:00
|
|
|
*/
|
2026-03-27 16:32:30 -04:00
|
|
|
public static function init() {
|
2019-09-17 12:06:41 -04:00
|
|
|
// Registers WooCommerce CPTs.
|
2023-07-20 00:11:25 +03:00
|
|
|
add_filter( 'register_post_type_args', [ self::class, 'register_post_types' ], 10, 2 );
|
|
|
|
|
add_filter( 'graphql_post_entities_allowed_post_types', [ self::class, 'skip_type_registry' ], 10 );
|
2019-09-17 12:06:41 -04:00
|
|
|
|
2019-06-20 14:14:13 -04:00
|
|
|
// Add data-loaders to AppContext.
|
2025-11-14 14:30:01 -05:00
|
|
|
add_filter( 'graphql_data_loader_classes', [ self::class, 'graphql_data_loader_classes' ], 10 );
|
2019-06-20 14:14:13 -04:00
|
|
|
|
2019-10-24 13:56:12 -04:00
|
|
|
// Add node resolvers.
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_resolve_node',
|
2022-08-26 14:53:36 -04:00
|
|
|
[ '\WPGraphQL\WooCommerce\Data\Factory', 'resolve_node' ],
|
2019-10-24 13:56:12 -04:00
|
|
|
10,
|
|
|
|
|
4
|
|
|
|
|
);
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_resolve_node_type',
|
2022-08-26 14:53:36 -04:00
|
|
|
[ '\WPGraphQL\WooCommerce\Data\Factory', 'resolve_node_type' ],
|
2019-10-24 13:56:12 -04:00
|
|
|
10,
|
|
|
|
|
2
|
|
|
|
|
);
|
2019-12-09 18:49:19 -05:00
|
|
|
|
|
|
|
|
// Filter Unions.
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_wp_union_type_config',
|
2023-07-20 00:11:25 +03:00
|
|
|
[ self::class, 'inject_union_types' ],
|
2019-12-09 18:49:19 -05:00
|
|
|
10,
|
|
|
|
|
2
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_union_resolve_type',
|
2023-07-20 00:11:25 +03:00
|
|
|
[ self::class, 'inject_type_resolver' ],
|
2020-10-12 12:41:19 -04:00
|
|
|
10,
|
2023-06-13 23:17:02 +03:00
|
|
|
2
|
2020-10-12 12:41:19 -04:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_interface_resolve_type',
|
2023-07-20 00:11:25 +03:00
|
|
|
[ self::class, 'inject_type_resolver' ],
|
2019-12-09 18:49:19 -05:00
|
|
|
10,
|
2023-06-13 23:17:02 +03:00
|
|
|
2
|
2019-12-09 18:49:19 -05:00
|
|
|
);
|
2020-10-01 01:45:26 -04:00
|
|
|
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_dataloader_pre_get_model',
|
2022-08-26 14:53:36 -04:00
|
|
|
[ '\WPGraphQL\WooCommerce\Data\Loader\WC_CPT_Loader', 'inject_post_loader_models' ],
|
2020-10-01 01:45:26 -04:00
|
|
|
10,
|
|
|
|
|
3
|
|
|
|
|
);
|
2021-06-14 11:40:42 -04:00
|
|
|
|
2025-10-11 16:29:36 -04:00
|
|
|
// Filter to allow order notes to be visible in GraphQL queries.
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_data_is_private',
|
|
|
|
|
[ self::class, 'make_order_notes_visible' ],
|
|
|
|
|
10,
|
|
|
|
|
3
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Filter to set order notes visibility to public for authorized users.
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_object_visibility',
|
|
|
|
|
[ self::class, 'set_order_notes_visibility' ],
|
|
|
|
|
10,
|
|
|
|
|
5
|
|
|
|
|
);
|
|
|
|
|
|
2021-06-14 11:40:42 -04:00
|
|
|
add_filter(
|
|
|
|
|
'graphql_dataloader_get_model',
|
2022-08-26 14:53:36 -04:00
|
|
|
[ '\WPGraphQL\WooCommerce\Data\Loader\WC_Customer_Loader', 'inject_user_loader_models' ],
|
2021-06-14 11:40:42 -04:00
|
|
|
10,
|
|
|
|
|
3
|
|
|
|
|
);
|
|
|
|
|
|
2021-06-03 21:37:09 -04:00
|
|
|
add_filter(
|
|
|
|
|
'graphql_map_input_fields_to_wp_query',
|
2022-08-26 14:53:36 -04:00
|
|
|
[ '\WPGraphQL\WooCommerce\Connection\Coupons', 'map_input_fields_to_wp_query' ],
|
2021-06-03 21:37:09 -04:00
|
|
|
10,
|
|
|
|
|
7
|
|
|
|
|
);
|
|
|
|
|
|
2021-06-14 11:40:42 -04:00
|
|
|
add_filter(
|
|
|
|
|
'graphql_map_input_fields_to_wp_user_query',
|
2022-08-26 14:53:36 -04:00
|
|
|
[ '\WPGraphQL\WooCommerce\Connection\Customers', 'map_input_fields_to_wp_query' ],
|
2021-06-14 11:40:42 -04:00
|
|
|
10,
|
|
|
|
|
6
|
|
|
|
|
);
|
2022-08-26 22:43:09 -04:00
|
|
|
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_connection',
|
|
|
|
|
[ '\WPGraphQL\WooCommerce\Connection\Customers', 'upgrade_models' ],
|
|
|
|
|
10,
|
|
|
|
|
2
|
|
|
|
|
);
|
2023-02-02 16:47:19 -05:00
|
|
|
|
|
|
|
|
add_filter(
|
|
|
|
|
'graphql_wp_connection_type_config',
|
|
|
|
|
[ '\WPGraphQL\WooCommerce\Connection\Products', 'set_connection_config' ]
|
|
|
|
|
);
|
2019-03-24 20:22:05 -04:00
|
|
|
}
|
|
|
|
|
|
2019-09-17 12:06:41 -04:00
|
|
|
/**
|
|
|
|
|
* Registers WooCommerce post-types to be used in GraphQL schema
|
|
|
|
|
*
|
|
|
|
|
* @param array $args - allowed post-types.
|
|
|
|
|
* @param string $post_type - name of taxonomy being checked.
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function register_post_types( $args, $post_type ) {
|
|
|
|
|
if ( 'product' === $post_type ) {
|
2022-12-07 15:04:39 -07:00
|
|
|
$args['show_in_graphql'] = true;
|
2024-08-06 16:54:18 -06:00
|
|
|
$args['model'] = \WPGraphQL\WooCommerce\Model\Product::class;
|
2022-12-07 15:04:39 -07:00
|
|
|
$args['graphql_single_name'] = 'Product';
|
|
|
|
|
$args['graphql_plural_name'] = 'Products';
|
|
|
|
|
$args['graphql_kind'] = 'interface';
|
2025-05-01 14:46:55 -04:00
|
|
|
$args['graphql_interfaces'] = [ 'ContentNode', 'ProductUnion' ];
|
2022-12-07 15:04:39 -07:00
|
|
|
$args['graphql_register_root_field'] = false;
|
|
|
|
|
$args['graphql_register_root_connection'] = false;
|
2025-02-27 10:22:36 -05:00
|
|
|
$args['graphql_resolve_type'] = [ self::class, 'resolve_product_type' ];
|
2026-03-20 13:31:13 -04:00
|
|
|
$args['graphql_exclude_mutations'] = [ 'create', 'delete', 'update' ];
|
|
|
|
|
}//end if
|
2019-09-17 12:06:41 -04:00
|
|
|
if ( 'product_variation' === $post_type ) {
|
2025-02-27 10:22:36 -05:00
|
|
|
$args['show_in_graphql'] = true;
|
|
|
|
|
$args['model'] = \WPGraphQL\WooCommerce\Model\Product_Variation::class;
|
|
|
|
|
$args['graphql_single_name'] = 'ProductVariation';
|
|
|
|
|
$args['graphql_plural_name'] = 'ProductVariations';
|
|
|
|
|
$args['publicly_queryable'] = true;
|
|
|
|
|
$args['graphql_kind'] = 'interface';
|
|
|
|
|
$args['graphql_interfaces'] = [
|
|
|
|
|
'Node',
|
|
|
|
|
'NodeWithFeaturedImage',
|
|
|
|
|
'ContentNode',
|
|
|
|
|
'ProductUnion',
|
2025-05-01 14:46:55 -04:00
|
|
|
'UniformResourceIdentifiable',
|
2025-02-27 10:22:36 -05:00
|
|
|
'ProductWithPricing',
|
|
|
|
|
'ProductWithDimensions',
|
|
|
|
|
'InventoriedProduct',
|
|
|
|
|
'DownloadableProduct',
|
|
|
|
|
];
|
|
|
|
|
$args['graphql_register_root_field'] = false;
|
|
|
|
|
$args['graphql_register_root_connection'] = false;
|
|
|
|
|
$args['graphql_resolve_type'] = [ self::class, 'resolve_product_variation_type' ];
|
2026-03-20 13:31:13 -04:00
|
|
|
$args['graphql_exclude_mutations'] = [ 'create', 'delete', 'update' ];
|
2019-09-17 12:06:41 -04:00
|
|
|
}
|
|
|
|
|
if ( 'shop_coupon' === $post_type ) {
|
|
|
|
|
$args['show_in_graphql'] = true;
|
|
|
|
|
$args['graphql_single_name'] = 'Coupon';
|
|
|
|
|
$args['graphql_plural_name'] = 'Coupons';
|
2021-11-04 18:09:04 -04:00
|
|
|
$args['publicly_queryable'] = true;
|
2019-09-17 12:06:41 -04:00
|
|
|
$args['skip_graphql_type_registry'] = true;
|
|
|
|
|
}
|
|
|
|
|
if ( 'shop_order' === $post_type ) {
|
|
|
|
|
$args['show_in_graphql'] = true;
|
|
|
|
|
$args['graphql_single_name'] = 'Order';
|
|
|
|
|
$args['graphql_plural_name'] = 'Orders';
|
|
|
|
|
$args['skip_graphql_type_registry'] = true;
|
|
|
|
|
}
|
|
|
|
|
if ( 'shop_order_refund' === $post_type ) {
|
|
|
|
|
$args['show_in_graphql'] = true;
|
|
|
|
|
$args['graphql_single_name'] = 'Refund';
|
|
|
|
|
$args['graphql_plural_name'] = 'Refunds';
|
|
|
|
|
$args['skip_graphql_type_registry'] = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-09-17 16:26:51 -04:00
|
|
|
* Filters "allowed_post_types" and removed Woocommerce CPTs.
|
2019-09-17 12:06:41 -04:00
|
|
|
*
|
|
|
|
|
* @param array $post_types Post types registered in GraphQL schema.
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function skip_type_registry( $post_types ) {
|
|
|
|
|
return array_diff(
|
|
|
|
|
$post_types,
|
|
|
|
|
get_post_types(
|
2022-08-26 14:53:36 -04:00
|
|
|
[
|
2019-09-17 12:06:41 -04:00
|
|
|
'show_in_graphql' => true,
|
|
|
|
|
'skip_graphql_type_registry' => true,
|
2022-08-26 14:53:36 -04:00
|
|
|
]
|
2019-09-17 12:06:41 -04:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-24 20:22:05 -04:00
|
|
|
/**
|
2019-03-28 01:15:32 -04:00
|
|
|
* Registers data-loaders to be used when resolving WooCommerce-related GraphQL types
|
2019-03-26 01:35:55 -04:00
|
|
|
*
|
2025-11-14 14:30:01 -05:00
|
|
|
* @param array $loaders Assigned loaders.
|
2019-03-26 01:35:55 -04:00
|
|
|
*
|
|
|
|
|
* @return array
|
2019-03-24 20:22:05 -04:00
|
|
|
*/
|
2025-11-14 14:30:01 -05:00
|
|
|
public static function graphql_data_loader_classes( $loaders ) {
|
2019-03-28 01:15:32 -04:00
|
|
|
// WooCommerce customer loader.
|
2025-11-14 14:30:01 -05:00
|
|
|
$loaders['wc_customer'] = WC_Customer_Loader::class;
|
2019-03-28 01:15:32 -04:00
|
|
|
|
2020-03-20 22:59:47 -04:00
|
|
|
// WooCommerce CPT loader.
|
2025-11-14 14:30:01 -05:00
|
|
|
$loaders['wc_post'] = WC_CPT_Loader::class;
|
2020-03-20 22:59:47 -04:00
|
|
|
|
|
|
|
|
// WooCommerce DB loaders.
|
2025-11-14 14:30:01 -05:00
|
|
|
$loaders['cart_item'] = WC_Cart_Item_Loader::class;
|
|
|
|
|
$loaders['downloadable_item'] = WC_Downloadable_Item_Loader::class;
|
|
|
|
|
$loaders['tax_class'] = WC_Tax_Class_Loader::class;
|
|
|
|
|
$loaders['tax_rate'] = WC_Tax_Rate_Loader::class;
|
|
|
|
|
$loaders['order_item'] = WC_Order_Item_Loader::class;
|
|
|
|
|
$loaders['shipping_method'] = WC_Shipping_Method_Loader::class;
|
|
|
|
|
$loaders['shipping_zone'] = WC_Shipping_Zone_Loader::class;
|
2019-03-26 01:35:55 -04:00
|
|
|
return $loaders;
|
2019-03-21 21:43:24 -04:00
|
|
|
}
|
2019-12-09 18:49:19 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Inject Union types that resolve to Product with Product types
|
|
|
|
|
*
|
|
|
|
|
* @param array $config WPUnion config.
|
|
|
|
|
* @param \WPGraphQL\Type\WPUnionType $wp_union WPUnion object.
|
2023-06-13 23:17:02 +03:00
|
|
|
*
|
|
|
|
|
* @return array
|
2019-12-09 18:49:19 -05:00
|
|
|
*/
|
|
|
|
|
public static function inject_union_types( $config, $wp_union ) {
|
|
|
|
|
$refresh_callback = false;
|
|
|
|
|
if ( in_array( 'Product', $config['typeNames'], true ) ) {
|
|
|
|
|
// Strip 'Product' from config and child product types.
|
|
|
|
|
$config['typeNames'] = array_merge(
|
|
|
|
|
array_filter(
|
|
|
|
|
$config['typeNames'],
|
2023-07-20 00:11:25 +03:00
|
|
|
static function ( $type ) {
|
2019-12-09 18:49:19 -05:00
|
|
|
return 'Product' !== $type;
|
|
|
|
|
}
|
|
|
|
|
),
|
2023-04-18 23:48:08 -04:00
|
|
|
array_values( WooGraphQL::get_enabled_product_types() ),
|
|
|
|
|
[ WooGraphQL::get_supported_product_type() ]
|
2019-12-09 18:49:19 -05:00
|
|
|
);
|
|
|
|
|
$refresh_callback = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update 'types' callback.
|
|
|
|
|
if ( $refresh_callback ) {
|
2023-07-19 22:56:42 +03:00
|
|
|
$config['types'] = static function () use ( $config, $wp_union ) {
|
2022-08-26 14:53:36 -04:00
|
|
|
$prepared_types = [];
|
2023-06-13 23:17:02 +03:00
|
|
|
foreach ( $config['typeNames'] as $type_name ) {
|
|
|
|
|
$prepared_types[] = $wp_union->type_registry->get_type( $type_name );
|
2019-12-09 18:49:19 -05:00
|
|
|
}
|
|
|
|
|
return $prepared_types;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Inject Union type resolver that resolve to Product with Product types
|
|
|
|
|
*
|
|
|
|
|
* @param \WPGraphQL\Type\WPObjectType $type Type be resolve to.
|
|
|
|
|
* @param mixed $value Object for which the type is being resolve config.
|
|
|
|
|
* @param \WPGraphQL\Type\WPUnionType $wp_union WPUnion object.
|
2023-06-13 23:17:02 +03:00
|
|
|
*
|
|
|
|
|
* @return \WPGraphQL\Type\WPObjectType
|
2019-12-09 18:49:19 -05:00
|
|
|
*/
|
|
|
|
|
public static function inject_union_type_resolver( $type, $value, $wp_union ) {
|
2019-12-10 16:17:35 -05:00
|
|
|
switch ( get_class( $value ) ) {
|
|
|
|
|
case 'WPGraphQL\WooCommerce\Model\Product':
|
|
|
|
|
case 'WPGraphQL\WooCommerce\Model\Coupon':
|
|
|
|
|
case 'WPGraphQL\WooCommerce\Model\Order':
|
|
|
|
|
$new_type = Factory::resolve_node_type( $type, $value );
|
|
|
|
|
if ( $new_type ) {
|
|
|
|
|
$type = $wp_union->type_registry->get_type( $new_type );
|
|
|
|
|
}
|
|
|
|
|
break;
|
2019-12-09 18:49:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $type;
|
|
|
|
|
}
|
2020-10-12 12:41:19 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Inject Union type resolver that resolve to Product with Product types
|
|
|
|
|
*
|
2023-04-18 23:48:08 -04:00
|
|
|
* @param \WPGraphQL\Type\WPObjectType|null $type Type be resolve to.
|
|
|
|
|
* @param mixed $value Object for which the type is being resolve config.
|
|
|
|
|
*
|
2023-07-19 23:01:50 +03:00
|
|
|
* @throws \GraphQL\Error\UserError Invalid product type received.
|
2023-04-18 23:48:08 -04:00
|
|
|
*
|
|
|
|
|
* @return \WPGraphQL\Type\WPObjectType|null
|
2020-10-12 12:41:19 -04:00
|
|
|
*/
|
2023-04-18 23:48:08 -04:00
|
|
|
public static function inject_type_resolver( $type, $value ) {
|
2024-08-06 16:54:18 -06:00
|
|
|
|
2023-04-18 23:48:08 -04:00
|
|
|
$type_registry = \WPGraphQL::get_type_registry();
|
2020-10-12 12:41:19 -04:00
|
|
|
switch ( $type ) {
|
|
|
|
|
case 'Coupon':
|
|
|
|
|
case 'Order':
|
|
|
|
|
$new_type = Factory::resolve_node_type( $type, $value );
|
|
|
|
|
if ( $new_type ) {
|
2023-04-18 23:48:08 -04:00
|
|
|
$type = $type_registry->get_type( $new_type );
|
2020-10-12 12:41:19 -04:00
|
|
|
}
|
|
|
|
|
break;
|
2024-08-06 16:54:18 -06:00
|
|
|
case 'ProductVariation':
|
|
|
|
|
$type = self::resolve_product_variation_type( $value );
|
|
|
|
|
break;
|
2023-04-18 23:48:08 -04:00
|
|
|
case 'Product':
|
2025-02-27 10:22:36 -05:00
|
|
|
$type = self::resolve_product_type( $value );
|
2023-04-18 23:48:08 -04:00
|
|
|
}//end switch
|
2020-10-12 12:41:19 -04:00
|
|
|
|
|
|
|
|
return $type;
|
|
|
|
|
}
|
2023-09-15 00:21:00 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolves GraphQL type for provided product model.
|
|
|
|
|
*
|
2025-02-27 10:22:36 -05:00
|
|
|
* @param \WPGraphQL\WooCommerce\Model\Product|\WPGraphQL\WooCommerce\Model\Product_Variation $value Product model.
|
2024-05-16 21:27:54 -04:00
|
|
|
*
|
2023-09-15 00:21:00 -04:00
|
|
|
* @throws \GraphQL\Error\UserError Invalid product type requested.
|
|
|
|
|
*
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public static function resolve_product_type( $value ) {
|
|
|
|
|
$type_registry = \WPGraphQL::get_type_registry();
|
|
|
|
|
$possible_types = WooGraphQL::get_enabled_product_types();
|
2025-11-14 14:30:01 -05:00
|
|
|
|
|
|
|
|
if ( $value instanceof \WPGraphQL\Model\Post && ( 'product' === $value->post_type || 'product_variation' === $value->post_type ) ) {
|
|
|
|
|
$product_model = \WPGraphQL::get_app_context()
|
|
|
|
|
->get_loader( 'wc_post' )
|
|
|
|
|
->load( $value->ID );
|
|
|
|
|
} elseif ( $value instanceof \WPGraphQL\Model\Post && ( 'product' !== $value->post_type && 'product_variation' !== $value->post_type ) ) {
|
|
|
|
|
throw new UserError(
|
|
|
|
|
sprintf(
|
|
|
|
|
/* translators: %s: Post type slug */
|
2026-06-30 10:16:21 -04:00
|
|
|
__( 'The "%s" post type is not a valid product type.', 'graphql-for-ecommerce' ),
|
2025-11-14 14:30:01 -05:00
|
|
|
$value->post_type
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
$product_model = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$product_type = $product_model->get_type();
|
2023-09-15 00:21:00 -04:00
|
|
|
if ( isset( $possible_types[ $product_type ] ) ) {
|
|
|
|
|
return $type_registry->get_type( $possible_types[ $product_type ] );
|
2025-11-14 14:30:01 -05:00
|
|
|
} elseif ( $product_model instanceof \WPGraphQL\WooCommerce\Model\Product_Variation ) {
|
|
|
|
|
return self::resolve_product_variation_type( $product_model );
|
2023-09-15 00:21:00 -04:00
|
|
|
} elseif ( 'on' === woographql_setting( 'enable_unsupported_product_type', 'off' ) ) {
|
|
|
|
|
$unsupported_type = WooGraphQL::get_supported_product_type();
|
|
|
|
|
return $type_registry->get_type( $unsupported_type );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new UserError(
|
|
|
|
|
sprintf(
|
|
|
|
|
/* translators: %s: Product type */
|
2026-06-30 10:16:21 -04:00
|
|
|
__( 'The "%s" product type is not supported by the core GraphQL for eCommerce schema.', 'graphql-for-ecommerce' ),
|
2025-11-14 14:30:01 -05:00
|
|
|
$product_model->type
|
2023-09-15 00:21:00 -04:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-09-15 21:03:55 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolves GraphQL type for provided product variation model.
|
|
|
|
|
*
|
2025-02-27 10:22:36 -05:00
|
|
|
* @param \WPGraphQL\WooCommerce\Model\Product_Variation $value Product model.
|
2024-05-16 21:27:54 -04:00
|
|
|
*
|
2023-09-15 21:03:55 -04:00
|
|
|
* @throws \GraphQL\Error\UserError Invalid product type requested.
|
|
|
|
|
*
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public static function resolve_product_variation_type( $value ) {
|
|
|
|
|
$type_registry = \WPGraphQL::get_type_registry();
|
|
|
|
|
$possible_types = WooGraphQL::get_enabled_product_variation_types();
|
2024-08-06 16:54:18 -06:00
|
|
|
|
2026-06-30 10:03:48 -04:00
|
|
|
// Normally $value is a Product_Variation model. In some setups the
|
|
|
|
|
// variation node is loaded through the generic post loader (e.g. a cart
|
|
|
|
|
// item's variation under Polylang, which doesn't manage the
|
|
|
|
|
// product_variation post-type) and arrives as a base
|
|
|
|
|
// \WPGraphQL\Model\Post with no get_type(). Fall back to resolving the
|
|
|
|
|
// variation's product type from its ID.
|
|
|
|
|
if ( is_callable( [ $value, 'get_type' ] ) ) {
|
|
|
|
|
$product_type = $value->get_type();
|
|
|
|
|
} else {
|
|
|
|
|
$variation_id = $value->databaseId ?? ( $value->ID ?? 0 );
|
|
|
|
|
$product = $variation_id ? wc_get_product( $variation_id ) : false;
|
|
|
|
|
$product_type = $product ? $product->get_type() : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $product_type && isset( $possible_types[ $product_type ] ) ) {
|
2023-09-15 21:03:55 -04:00
|
|
|
return $type_registry->get_type( $possible_types[ $product_type ] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new UserError(
|
|
|
|
|
sprintf(
|
|
|
|
|
/* translators: %s: Product type */
|
2026-06-30 10:16:21 -04:00
|
|
|
__( 'The "%s" product variation type is not supported by the core GraphQL for eCommerce schema.', 'graphql-for-ecommerce' ),
|
2026-06-30 10:03:48 -04:00
|
|
|
$product_type ?? ''
|
2023-09-15 21:03:55 -04:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-11 16:29:36 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Filter to make order notes visible in GraphQL queries for authorized users.
|
|
|
|
|
*
|
|
|
|
|
* @param bool $is_private Whether the data is private.
|
|
|
|
|
* @param string $model_name The name of the model being checked.
|
|
|
|
|
* @param mixed $data The data being checked.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public static function make_order_notes_visible( $is_private, $model_name, $data ) {
|
|
|
|
|
// Only apply to Comment models.
|
|
|
|
|
if ( 'CommentObject' !== $model_name ) {
|
|
|
|
|
return $is_private;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if this is an order note.
|
|
|
|
|
if ( $data instanceof \WP_Comment && 'order_note' === $data->comment_type ) {
|
|
|
|
|
// Get the parent order.
|
|
|
|
|
$order_id = absint( $data->comment_post_ID );
|
2025-11-14 14:30:01 -05:00
|
|
|
$order = wc_get_order( $order_id );
|
|
|
|
|
|
2025-10-11 16:29:36 -04:00
|
|
|
if ( ! $order ) {
|
|
|
|
|
return true; // Keep it private if order not found.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allow shop managers and admins to see all order notes.
|
|
|
|
|
if ( current_user_can( 'edit_shop_orders' ) ) {
|
|
|
|
|
return false; // Not private.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allow customers to see customer notes on their own orders.
|
2025-11-14 14:30:01 -05:00
|
|
|
$comment_id = absint( $data->comment_ID );
|
|
|
|
|
$is_customer_note = get_comment_meta( $comment_id, 'is_customer_note', true );
|
|
|
|
|
if ( $is_customer_note && ! is_bool( $order ) && is_a( $order, \WC_Order::class ) ) {
|
|
|
|
|
if ( get_current_user_id() === $order->get_customer_id() ) {
|
|
|
|
|
return false; // Not private.
|
|
|
|
|
}
|
|
|
|
|
} elseif ( $is_customer_note && ! is_bool( $order ) && is_a( $order, \WC_Order_Refund::class ) ) {
|
|
|
|
|
/** @var \WC_Order|false $parent */
|
|
|
|
|
$parent = wc_get_order( $order->get_parent_id() );
|
|
|
|
|
if ( $parent && get_current_user_id() === $parent->get_customer_id() ) {
|
|
|
|
|
return false; // Not private.
|
|
|
|
|
}
|
2025-10-11 16:29:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise keep it private.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $is_private;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Filter to set order notes visibility to public for authorized users.
|
|
|
|
|
*
|
2025-11-14 14:30:01 -05:00
|
|
|
* @param string $visibility The visibility of the object.
|
|
|
|
|
* @param string $model_name The name of the model being checked.
|
|
|
|
|
* @param mixed $data The data being checked.
|
|
|
|
|
* @param int|null $owner The owner of the object.
|
|
|
|
|
* @param \WP_User $current_user The current user.
|
2025-10-11 16:29:36 -04:00
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public static function set_order_notes_visibility( $visibility, $model_name, $data, $owner, $current_user ) {
|
|
|
|
|
// Only apply to Comment models.
|
|
|
|
|
if ( 'CommentObject' !== $model_name ) {
|
|
|
|
|
return $visibility;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if this is an order note and if user owns the order.
|
|
|
|
|
if ( $data instanceof \WP_Comment && 'order_note' === $data->comment_type ) {
|
|
|
|
|
$order = wc_get_order( $data->comment_post_ID );
|
2025-11-14 14:30:01 -05:00
|
|
|
|
2025-10-11 16:29:36 -04:00
|
|
|
// If user is the order owner, make it public.
|
2025-11-14 14:30:01 -05:00
|
|
|
if ( $order && ! is_bool( $order ) && is_a( $order, \WC_Order::class ) ) {
|
|
|
|
|
return get_current_user_id() === $order->get_customer_id() ? 'public' : $visibility;
|
|
|
|
|
} elseif ( $order && ! is_bool( $order ) && is_a( $order, \WC_Order_Refund::class ) ) {
|
|
|
|
|
/** @var \WC_Order|false $parent */
|
|
|
|
|
$parent = wc_get_order( $order->get_parent_id() );
|
|
|
|
|
return $parent && get_current_user_id() === $parent->get_customer_id() ? 'public' : $visibility;
|
2025-10-11 16:29:36 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $visibility;
|
|
|
|
|
}
|
2019-03-21 21:43:24 -04:00
|
|
|
}
|