Skip to content

Commit 87d7a7b

Browse files
authored
fix: ProductVariation type made to inherit the Product interface. (#788)
* devops: WP theme during e2e locked to TwentyTwentyOne * fix: "Product Connection Resolver" restored * chore: Linter/PHPStan compliance met * devops: CustomerProceedsToCheckoutCept updated. * chore: Linter compliance met
1 parent 50e438f commit 87d7a7b

21 files changed

+663
-578
lines changed

bin/entrypoint.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ wp-graphql wp-graphql-jwt-authentication \
6666
wp-graphql-woocommerce \
6767
--allow-root
6868

69+
wp theme activate twentytwentyone --allow-root
70+
6971
if ! wp config has GRAPHQL_JWT_AUTH_SECRET_KEY --allow-root; then
7072
echo "Adding WPGraphQL-JWT-Authentication salt..."
7173
wp config set GRAPHQL_JWT_AUTH_SECRET_KEY 'test' --allow-root

codeception.dist.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ modules:
7272
domain: '%WORDPRESS_DOMAIN%'
7373
adminEmail: '%ADMIN_EMAIL%'
7474
title: 'WooGraphQL Tests'
75+
theme: 'twentytwentyone'
7576
plugins:
7677
- woocommerce/woocommerce.php
7778
- woocommerce-gateway-stripe/woocommerce-gateway-stripe.php

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ services:
7373
build:
7474
context: .
7575
args:
76-
PHP_VERSION: ${PHP_VERSION:-7.3}
76+
PHP_VERSION: ${PHP_VERSION:-8.0}
7777
volumes:
7878
- .:/var/www/html/wp-content/plugins/wp-graphql-woocommerce
7979
- ./codeception.dist.yml:/var/www/html/wp-content/plugins/wp-graphql-woocommerce/codeception.yml

includes/class-wp-graphql-woocommerce.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ public static function get_enabled_product_types() {
7474
return apply_filters(
7575
'graphql_woocommerce_product_types',
7676
[
77-
'simple' => 'SimpleProduct',
78-
'variable' => 'VariableProduct',
79-
'external' => 'ExternalProduct',
80-
'grouped' => 'GroupProduct',
77+
'simple' => 'SimpleProduct',
78+
'variable' => 'VariableProduct',
79+
'external' => 'ExternalProduct',
80+
'grouped' => 'GroupProduct',
81+
'variation' => 'ProductVariation',
8182
]
8283
);
8384
}

includes/connection/class-products.php

Lines changed: 36 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
use GraphQL\Type\Definition\ResolveInfo;
1313
use WPGraphQL\AppContext;
14-
use WPGraphQL\Data\Connection\PostObjectConnectionResolver;
14+
use WPGraphQL\WooCommerce\Data\Connection\Product_Connection_Resolver;
1515
use WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce;
1616

1717
/**
@@ -33,17 +33,9 @@ public static function register_connections() {
3333
[
3434
'fromType' => 'Coupon',
3535
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
36-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
37-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
38-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
39-
36+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
4037
$resolver->set_query_arg( 'post__in', $source->product_ids );
4138

42-
// Change default ordering.
43-
if ( ! in_array( 'orderby', array_keys( $resolver->get_query_args() ), true ) ) {
44-
$resolver->set_query_arg( 'orderby', 'post__in' );
45-
}
46-
4739
return $resolver->get_connection();
4840
},
4941
]
@@ -55,10 +47,7 @@ public static function register_connections() {
5547
'fromType' => 'Coupon',
5648
'fromFieldName' => 'excludedProducts',
5749
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
58-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
59-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
60-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
61-
50+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
6251
$resolver->set_query_arg( 'post__in', $source->excluded_product_ids );
6352

6453
// Change default ordering.
@@ -78,8 +67,7 @@ public static function register_connections() {
7867
[
7968
'fromType' => 'Product',
8069
'fromFieldName' => 'related',
81-
'connectionArgs' => array_merge(
82-
self::get_connection_args(),
70+
'connectionArgs' => self::get_connection_args(
8371
[
8472
'shuffle' => [
8573
'type' => 'Boolean',
@@ -88,9 +76,7 @@ public static function register_connections() {
8876
]
8977
),
9078
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
91-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
92-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
93-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
79+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
9480

9581
// Bypass randomization by default for pagination support.
9682
if ( empty( $args['where']['shuffle'] ) ) {
@@ -105,11 +91,6 @@ static function () {
10591
$related_ids = wc_get_related_products( $source->ID, $resolver->get_query_amount() );
10692
$resolver->set_query_arg( 'post__in', $related_ids );
10793

108-
// Change default ordering.
109-
if ( ! in_array( 'orderby', array_keys( $resolver->get_query_args() ), true ) ) {
110-
$resolver->set_query_arg( 'orderby', 'post__in' );
111-
}
112-
11394
return $resolver->get_connection();
11495
},
11596
]
@@ -121,17 +102,9 @@ static function () {
121102
'fromType' => 'Product',
122103
'fromFieldName' => 'upsell',
123104
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
124-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
125-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
126-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
127-
105+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
128106
$resolver->set_query_arg( 'post__in', $source->upsell_ids );
129107

130-
// Change default ordering.
131-
if ( ! in_array( 'orderby', array_keys( $resolver->get_query_args() ), true ) ) {
132-
$resolver->set_query_arg( 'orderby', 'post__in' );
133-
}
134-
135108
return $resolver->get_connection();
136109
},
137110
]
@@ -144,17 +117,9 @@ static function () {
144117
[
145118
'fromType' => 'GroupProduct',
146119
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
147-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
148-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
149-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
150-
120+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
151121
$resolver->set_query_arg( 'post__in', $source->grouped_ids );
152122

153-
// Change default ordering.
154-
if ( ! in_array( 'orderby', array_keys( $resolver->get_query_args() ), true ) ) {
155-
$resolver->set_query_arg( 'orderby', 'post__in' );
156-
}
157-
158123
return $resolver->get_connection();
159124
},
160125
]
@@ -165,17 +130,8 @@ static function () {
165130
$cross_sell_config = [
166131
'fromFieldName' => 'crossSell',
167132
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
168-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
169-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
170-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
171-
133+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
172134
$resolver->set_query_arg( 'post__in', $source->cross_sell_ids );
173-
174-
// Change default ordering.
175-
if ( ! in_array( 'orderby', array_keys( $resolver->get_query_args() ), true ) ) {
176-
$resolver->set_query_arg( 'orderby', 'post__in' );
177-
}
178-
179135
return $resolver->get_connection();
180136
},
181137
];
@@ -198,21 +154,12 @@ static function () {
198154
'toType' => 'ProductVariation',
199155
'fromFieldName' => 'variations',
200156
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
201-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
202-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
203-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
157+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
204158

205159
$resolver->set_query_arg( 'post_parent', $source->ID );
206160
$resolver->set_query_arg( 'post_type', 'product_variation' );
207161
$resolver->set_query_arg( 'post__in', $source->variation_ids );
208162

209-
// Change default ordering.
210-
if ( ! in_array( 'orderby', array_keys( $resolver->get_query_args() ), true ) ) {
211-
$resolver->set_query_arg( 'orderby', 'post__in' );
212-
}
213-
214-
$resolver = self::set_ordering_query_args( $resolver, $args );
215-
216163
return $resolver->get_connection();
217164
},
218165
]
@@ -232,14 +179,9 @@ static function () {
232179
return null;
233180
}
234181

235-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
236-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
237-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
238-
182+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
239183
$resolver->set_query_arg( 'p', $source->parent_id );
240184

241-
$resolver = self::set_ordering_query_args( $resolver, $args );
242-
243185
return $resolver->one_to_one()->get_connection();
244186
},
245187
]
@@ -255,30 +197,16 @@ static function () {
255197
'toType' => 'ProductVariation',
256198
'fromFieldName' => 'variations',
257199
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
258-
global $wpdb;
259-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
260-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product_variation' );
261-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
262-
263-
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
264-
$attribute_meta_key = 'attribute_' . strtolower( preg_replace( '/([A-Z])/', '_$1', $source->taxonomyName ) );
265-
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
266-
$variation_ids = $wpdb->get_col(
267-
$wpdb->prepare(
268-
"SELECT ID
269-
FROM {$wpdb->prefix}posts
270-
WHERE ID IN (SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = %s AND meta_value = %s)
271-
AND post_type = 'product_variation'",
272-
$attribute_meta_key,
273-
$source->slug
274-
)
275-
);
276-
277-
$variation_ids = ! empty( $variation_ids ) ? $variation_ids : [ '0' ];
278-
279-
$resolver->set_query_arg( 'post__in', $variation_ids );
200+
$attribute_meta_key = 'attribute_' . strtolower( preg_replace( '/([A-Z])/', '_$1', $source->taxonomyName ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
201+
$meta_query = [
202+
'key' => $attribute_meta_key,
203+
'value' => $source->slug,
204+
'compare' => '=',
205+
];
280206

281-
$resolver = self::set_ordering_query_args( $resolver, $args );
207+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
208+
$resolver->set_query_arg( 'post_type', 'product_variation' );
209+
$resolver->add_meta_query( $meta_query );
282210

283211
return $resolver->get_connection();
284212
},
@@ -319,26 +247,20 @@ public static function set_connection_config( $config ) {
319247
$from_type = $config['fromType'];
320248
if ( 'Product' === $to_type ) {
321249
$config['connectionArgs'] = self::get_connection_args();
322-
$config['queryClass'] = '\WC_Product_Query';
323250
}
324251

325252
$taxonomies = self::get_product_connected_taxonomies();
326253
if ( 'Product' === $to_type && in_array( $from_type, $taxonomies, true ) ) {
327254
$config['resolve'] = static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
328-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
329-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
330-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
331255
$tax_query = [
332256
[
333-
'taxonomy' => $source->taxonomyName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
334-
'terms' => [ $source->term_id ],
335-
'field' => 'term_id',
336-
'include_children' => false,
257+
'taxonomy' => $source->taxonomyName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
258+
'operator' => 'EXISTS',
337259
],
338260
];
339-
$resolver->set_query_arg( 'tax_query', $tax_query );
340261

341-
$resolver = self::set_ordering_query_args( $resolver, $args );
262+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
263+
$resolver->add_tax_query( $tax_query );
342264

343265
return $resolver->get_connection();
344266
};
@@ -359,14 +281,9 @@ public static function get_connection_config( $args = [] ): array {
359281
'fromType' => 'RootQuery',
360282
'toType' => 'Product',
361283
'fromFieldName' => 'products',
362-
'queryClass' => '\WC_Product_Query',
363284
'connectionArgs' => self::get_connection_args(),
364285
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
365-
add_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10, 3 );
366-
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
367-
remove_filter( 'graphql_post_object_connection_args', [ self::class, 'bypass_get_args_sanitization' ], 10 );
368-
369-
$resolver = self::set_ordering_query_args( $resolver, $args );
286+
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
370287

371288
return $resolver->get_connection();
372289
},
@@ -375,19 +292,6 @@ public static function get_connection_config( $args = [] ): array {
375292
);
376293
}
377294

378-
/**
379-
* Bypass arg sanization in Post Object Connection Resolver.
380-
*
381-
* @param array $args Sanitized GraphQL args passed to the resolver.
382-
* @param \WPGraphQL\Data\Connection\PostObjectConnectionResolver $connection_resolver Instance of the ConnectionResolver.
383-
* @param array $all_args array of arguments input in the field as part of the GraphQL query.
384-
385-
* @return array
386-
*/
387-
public static function bypass_get_args_sanitization( $args, $connection_resolver, $all_args ) {
388-
return $all_args;
389-
}
390-
391295
/**
392296
* Undocumented function
393297
*
@@ -432,10 +336,12 @@ public static function set_ordering_query_args( $resolver, $args ) {
432336

433337
/**
434338
* Returns array of where args.
339+
*
340+
* @param array $extra_args Extra connection args.
435341
*
436342
* @return array
437343
*/
438-
public static function get_connection_args(): array {
344+
public static function get_connection_args( $extra_args = [] ): array {
439345
$args = [
440346
'slugIn' => [
441347
'type' => [ 'list_of' => 'String' ],
@@ -561,6 +467,10 @@ public static function get_connection_args(): array {
561467
'type' => 'Boolean',
562468
'description' => __( 'Limit result types to types supported by WooGraphQL.', 'wp-graphql-woocommerce' ),
563469
],
470+
'includeVariations' => [
471+
'type' => 'Boolean',
472+
'description' => __( 'Include variations in the result set.', 'wp-graphql-woocommerce' ),
473+
],
564474
];
565475

566476
if ( wc_tax_enabled() ) {
@@ -570,7 +480,7 @@ public static function get_connection_args(): array {
570480
];
571481
}
572482

573-
return array_merge( get_wc_cpt_connection_args(), $args );
483+
return array_merge( get_wc_cpt_connection_args(), $args, $extra_args );
574484
}
575485

576486
/**
@@ -610,6 +520,10 @@ public static function map_input_fields_to_wp_query( $query_args, $args, $source
610520
'tag__not_in',
611521
];
612522

523+
if ( isset( $where_args['includeVariations'] ) && $where_args['includeVariations'] ) {
524+
$query_args['post_type'] = [ 'product', 'product_variation' ];
525+
}
526+
613527
$query_args = array_diff_key( $query_args, array_flip( $remove ) );
614528

615529
if ( ! empty( $where_args['slugIn'] ) ) {

includes/connection/class-variation-attributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public static function get_connection_config( $args = [] ): array {
5454
[
5555
'fromType' => 'ProductVariation',
5656
'toType' => 'VariationAttribute',
57-
'fromFieldName' => 'attributes',
57+
'fromFieldName' => 'variationAttributes',
5858
'connectionArgs' => [],
5959
'resolve' => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) {
6060
$resolver = new Variation_Attribute_Connection_Resolver();

includes/connection/class-wc-terms.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public static function register_connections() {
4141
// Loop through the allowed_taxonomies to register appropriate connections.
4242
foreach ( $allowed_taxonomies as $tax_object ) {
4343
foreach ( $wc_post_types as $post_type ) {
44-
if ( 'product' === $post_type ) {
44+
if ( 'product' === $post_type || 'product_variation' === $post_type ) {
4545
continue;
4646
}
4747

0 commit comments

Comments
 (0)