• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql-woocommerce / 6282474781

23 Sep 2023 07:50AM UTC coverage: 84.793% (+0.1%) from 84.684%
6282474781

push

github

web-flow
feat: `collectionStats` query added (#785)

* feat: collectionStats query added

* fix: `collectionStats` query completed and tested.

* chore: Linter and PHPStan compliance met

* chore: ProductTaxonomy values fixed.

* chore: CollectionStatsQueryTest tweaked for CI

563 of 563 new or added lines in 12 files covered. (100.0%)

11029 of 13007 relevant lines covered (84.79%)

58.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.56
/includes/type/object/class-root-query.php
1
<?php
2
/**
3
 * Registers WooCommerce fields on the RootQuery object.
4
 *
5
 * @package WPGraphQL\WooCommerce\Type\WPObject
6
 * @since   0.6.0
7
 */
8

9
namespace WPGraphQL\WooCommerce\Type\WPObject;
10

11
use Automattic\WooCommerce\StoreApi\Utilities\ProductQueryFilters;
12
use Automattic\WooCommerce\Utilities\OrderUtil;
13
use GraphQL\Error\UserError;
14
use GraphQL\Type\Definition\ResolveInfo;
15
use GraphQLRelay\Relay;
16
use WPGraphQL\AppContext;
17
use WPGraphQL\WooCommerce\Data\Factory;
18
use WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce as WooGraphQL;
19

20
/**
21
 * Class - Root_Query
22
 */
23
class Root_Query {
24
        /**
25
         * Registers WC-related root queries.
26
         *
27
         * @return void
28
         */
29
        public static function register_fields() {
30
                register_graphql_fields(
114✔
31
                        'RootQuery',
114✔
32
                        [
114✔
33
                                'cart'             => [
114✔
34
                                        'type'        => 'Cart',
114✔
35
                                        'args'        => [
114✔
36
                                                'recalculateTotals' => [
114✔
37
                                                        'type'        => 'Boolean',
114✔
38
                                                        'description' => __( 'Should cart totals be recalculated.', 'wp-graphql-woocommerce' ),
114✔
39
                                                ],
114✔
40
                                        ],
114✔
41
                                        'description' => __( 'The cart object', 'wp-graphql-woocommerce' ),
114✔
42
                                        'resolve'     => static function ( $_, $args ) {
114✔
43
                                                $token_invalid = apply_filters( 'graphql_woocommerce_session_token_errors', null );
9✔
44
                                                if ( $token_invalid ) {
9✔
45
                                                        throw new UserError( $token_invalid );
×
46
                                                }
47

48
                                                $cart = Factory::resolve_cart();
9✔
49
                                                if ( ! empty( $args['recalculateTotals'] ) ) {
9✔
50
                                                        $cart->calculate_totals();
×
51
                                                }
52

53
                                                return $cart;
9✔
54
                                        },
114✔
55
                                ],
114✔
56
                                'cartItem'         => [
114✔
57
                                        'type'        => 'CartItem',
114✔
58
                                        'args'        => [
114✔
59
                                                'key' => [
114✔
60
                                                        'type' => [ 'non_null' => 'ID' ],
114✔
61
                                                ],
114✔
62
                                        ],
114✔
63
                                        'description' => __( 'The cart object', 'wp-graphql-woocommerce' ),
114✔
64
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
65
                                                $item = Factory::resolve_cart()->get_cart_item( $args['key'] );
1✔
66
                                                if ( empty( $item ) || empty( $item['key'] ) ) {
1✔
67
                                                        throw new UserError( __( 'Failed to retrieve cart item.', 'wp-graphql-woocommerce' ) );
×
68
                                                }
69

70
                                                return $item;
1✔
71
                                        },
114✔
72
                                ],
114✔
73
                                'cartFee'          => [
114✔
74
                                        'type'        => 'CartFee',
114✔
75
                                        'args'        => [
114✔
76
                                                'id' => [
114✔
77
                                                        'type' => [ 'non_null' => 'ID' ],
114✔
78
                                                ],
114✔
79
                                        ],
114✔
80
                                        'description' => __( 'The cart object', 'wp-graphql-woocommerce' ),
114✔
81
                                        'resolve'     => static function ( $source, array $args ) {
114✔
82
                                                $fees   = Factory::resolve_cart()->get_fees();
1✔
83
                                                $fee_id = $args['id'];
1✔
84

85
                                                if ( empty( $fees[ $fee_id ] ) ) {
1✔
86
                                                        throw new UserError( __( 'The ID input is invalid', 'wp-graphql-woocommerce' ) );
×
87
                                                }
88

89
                                                return $fees[ $fee_id ];
1✔
90
                                        },
114✔
91
                                ],
114✔
92
                                'coupon'           => [
114✔
93
                                        'type'        => 'Coupon',
114✔
94
                                        'description' => __( 'A coupon object', 'wp-graphql-woocommerce' ),
114✔
95
                                        'args'        => [
114✔
96
                                                'id'     => [ 'type' => [ 'non_null' => 'ID' ] ],
114✔
97
                                                'idType' => [
114✔
98
                                                        'type'        => 'CouponIdTypeEnum',
114✔
99
                                                        'description' => __( 'Type of ID being used identify coupon', 'wp-graphql-woocommerce' ),
114✔
100
                                                ],
114✔
101
                                        ],
114✔
102
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
103
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
3✔
104
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
3✔
105

106
                                                $coupon_id = null;
3✔
107
                                                switch ( $id_type ) {
108
                                                        case 'code':
3✔
109
                                                                $coupon_id = \wc_get_coupon_id_by_code( $id );
1✔
110
                                                                break;
1✔
111
                                                        case 'database_id':
3✔
112
                                                                $coupon_id = absint( $id );
1✔
113
                                                                break;
1✔
114
                                                        case 'global_id':
3✔
115
                                                        default:
116
                                                                $id_components = Relay::fromGlobalId( $args['id'] );
3✔
117
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
3✔
118
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
119
                                                                }
120
                                                                $coupon_id = absint( $id_components['id'] );
3✔
121
                                                                break;
3✔
122
                                                }
123

124
                                                // Check if user authorized to view coupon.
125
                                                /**
126
                                                 * Get coupon post type.
127
                                                 *
128
                                                 * @var \WP_Post_Type $post_type
129
                                                 */
130
                                                $post_type     = get_post_type_object( 'shop_coupon' );
3✔
131
                                                $is_authorized = current_user_can( $post_type->cap->edit_others_posts );
3✔
132
                                                if ( ! $is_authorized ) {
3✔
133
                                                        return null;
1✔
134
                                                }
135

136
                                                if ( empty( $coupon_id ) ) {
3✔
137
                                                        /* translators: %1$s: ID type, %2$s: ID value */
138
                                                        throw new UserError( sprintf( __( 'No coupon ID was found corresponding to the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
139
                                                }
140

141
                                                $coupon = get_post( $coupon_id );
3✔
142
                                                if ( ! is_object( $coupon ) || 'shop_coupon' !== $coupon->post_type ) {
3✔
143
                                                        /* translators: %1$s: ID type, %2$s: ID value */
144
                                                        throw new UserError( sprintf( __( 'No coupon exists with the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
145
                                                }
146

147
                                                return Factory::resolve_crud_object( $coupon_id, $context );
3✔
148
                                        },
114✔
149
                                ],
114✔
150
                                'customer'         => [
114✔
151
                                        'type'        => 'Customer',
114✔
152
                                        'description' => __( 'A customer object', 'wp-graphql-woocommerce' ),
114✔
153
                                        'args'        => [
114✔
154
                                                'id'         => [
114✔
155
                                                        'type'        => 'ID',
114✔
156
                                                        'description' => __( 'Get the customer by their global ID', 'wp-graphql-woocommerce' ),
114✔
157
                                                ],
114✔
158
                                                'customerId' => [
114✔
159
                                                        'type'        => 'Int',
114✔
160
                                                        'description' => __( 'Get the customer by their database ID', 'wp-graphql-woocommerce' ),
114✔
161
                                                ],
114✔
162
                                        ],
114✔
163
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
164
                                                $current_user_id = get_current_user_id();
10✔
165

166
                                                // Default the customer to the current user.
167
                                                $customer_id = $current_user_id;
10✔
168

169
                                                // If a customer ID has been provided, resolve to that ID instead.
170
                                                if ( ! empty( $args['id'] ) ) {
10✔
171
                                                        $id_components = Relay::fromGlobalId( $args['id'] );
3✔
172
                                                        if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
3✔
173
                                                                throw new UserError( __( 'The ID input is invalid', 'wp-graphql-woocommerce' ) );
×
174
                                                        }
175

176
                                                        $customer_id = absint( $id_components['id'] );
3✔
177
                                                } elseif ( ! empty( $args['customerId'] ) ) {
10✔
178
                                                        $customer_id = absint( $args['customerId'] );
1✔
179
                                                }
180

181
                                                // If a user does not have the ability to list users, they can only view their own customer object.
182
                                                $unauthorized = ! empty( $customer_id )
10✔
183
                                                        && ! current_user_can( 'list_users' )
10✔
184
                                                        && $current_user_id !== $customer_id;
10✔
185
                                                if ( $unauthorized ) {
10✔
186
                                                        throw new UserError( __( 'Not authorized to access this customer', 'wp-graphql-woocommerce' ) );
2✔
187
                                                }
188

189
                                                // If we have a customer ID, resolve to that customer.
190
                                                if ( $customer_id ) {
10✔
191
                                                        return Factory::resolve_customer( $customer_id, $context );
10✔
192
                                                }
193

194
                                                // Resolve to the session customer.
195
                                                return Factory::resolve_session_customer();
1✔
196
                                        },
114✔
197
                                ],
114✔
198
                                'order'            => [
114✔
199
                                        'type'        => 'Order',
114✔
200
                                        'description' => __( 'A order object', 'wp-graphql-woocommerce' ),
114✔
201
                                        'args'        => [
114✔
202
                                                'id'     => [
114✔
203
                                                        'type'        => 'ID',
114✔
204
                                                        'description' => __( 'The ID for identifying the order', 'wp-graphql-woocommerce' ),
114✔
205
                                                ],
114✔
206
                                                'idType' => [
114✔
207
                                                        'type'        => 'OrderIdTypeEnum',
114✔
208
                                                        'description' => __( 'Type of ID being used identify order', 'wp-graphql-woocommerce' ),
114✔
209
                                                ],
114✔
210
                                        ],
114✔
211
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
212
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
10✔
213
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
10✔
214

215
                                                $order_id = null;
10✔
216
                                                switch ( $id_type ) {
217
                                                        case 'order_number':
10✔
218
                                                                $order_id = \wc_get_order_id_by_order_key( $id );
1✔
219
                                                                break;
1✔
220
                                                        case 'database_id':
10✔
221
                                                                $order_id = absint( $id );
2✔
222
                                                                break;
2✔
223
                                                        case 'global_id':
9✔
224
                                                        default:
225
                                                                $id_components = Relay::fromGlobalId( $id );
9✔
226
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
9✔
227
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
228
                                                                }
229
                                                                $order_id = absint( $id_components['id'] );
9✔
230
                                                                break;
9✔
231
                                                }
232

233
                                                if ( empty( $order_id ) ) {
10✔
234
                                                        /* translators: %1$s: ID type, %2$s: ID value */
235
                                                        throw new UserError( sprintf( __( 'No order ID was found corresponding to the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
236
                                                }
237

238
                                                if ( 'shop_order' !== OrderUtil::get_order_type( $order_id ) ) {
10✔
239
                                                        /* translators: %1$s: ID type, %2$s: ID value */
240
                                                        throw new UserError( sprintf( __( 'No order exists with the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
241
                                                }
242

243
                                                // Check if user authorized to view order.
244
                                                /**
245
                                                 * Get order post type.
246
                                                 *
247
                                                 * @var \WP_Post_Type $post_type
248
                                                 */
249
                                                $post_type     = get_post_type_object( 'shop_order' );
10✔
250
                                                $is_authorized = current_user_can( $post_type->cap->edit_others_posts );
10✔
251
                                                if ( ! $is_authorized && get_current_user_id() ) {
10✔
252
                                                        /** @var \WC_Order[] $orders */
253
                                                        $orders = wc_get_orders(
2✔
254
                                                                [
2✔
255
                                                                        'type'          => 'shop_order',
2✔
256
                                                                        'post__in'      => [ $order_id ],
2✔
257
                                                                        'customer_id'   => get_current_user_id(),
2✔
258
                                                                        'no_rows_found' => true,
2✔
259
                                                                        'return'        => 'ids',
2✔
260
                                                                ]
2✔
261
                                                        );
2✔
262

263
                                                        if ( in_array( $order_id, $orders, true ) ) {
2✔
264
                                                                $is_authorized = true;
1✔
265
                                                        }
266
                                                }
267

268
                                                // Throw if authorized to view order.
269
                                                if ( ! $is_authorized ) {
10✔
270
                                                        throw new UserError( __( 'Not authorized to access this order', 'wp-graphql-woocommerce' ) );
1✔
271
                                                }
272

273
                                                return Factory::resolve_crud_object( $order_id, $context );
10✔
274
                                        },
114✔
275
                                ],
114✔
276
                                'productVariation' => [
114✔
277
                                        'type'        => 'ProductVariation',
114✔
278
                                        'description' => __( 'A product variation object', 'wp-graphql-woocommerce' ),
114✔
279
                                        'args'        => [
114✔
280
                                                'id'     => [
114✔
281
                                                        'type'        => 'ID',
114✔
282
                                                        'description' => __( 'The ID for identifying the product variation', 'wp-graphql-woocommerce' ),
114✔
283
                                                ],
114✔
284
                                                'idType' => [
114✔
285
                                                        'type'        => 'ProductVariationIdTypeEnum',
114✔
286
                                                        'description' => __( 'Type of ID being used identify product variation', 'wp-graphql-woocommerce' ),
114✔
287
                                                ],
114✔
288
                                        ],
114✔
289
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
290
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
6✔
291
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
6✔
292

293
                                                $variation_id = null;
6✔
294
                                                switch ( $id_type ) {
295
                                                        case 'database_id':
6✔
296
                                                                $variation_id = absint( $id );
2✔
297
                                                                break;
2✔
298
                                                        case 'global_id':
5✔
299
                                                        default:
300
                                                                $id_components = Relay::fromGlobalId( $id );
5✔
301
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
5✔
302
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
303
                                                                }
304
                                                                $variation_id = absint( $id_components['id'] );
5✔
305
                                                                break;
5✔
306
                                                }
307

308
                                                if ( empty( $variation_id ) ) {
6✔
309
                                                        /* translators: %1$s: ID type, %2$s: ID value */
310
                                                        throw new UserError( sprintf( __( 'No product variation ID was found corresponding to the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
311
                                                }
312

313
                                                $variation = get_post( $variation_id );
6✔
314
                                                if ( ! is_object( $variation ) || 'product_variation' !== $variation->post_type ) {
6✔
315
                                                        /* translators: %1$s: ID type, %2$s: ID value */
316
                                                        throw new UserError( sprintf( __( 'No product variation exists with the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
317
                                                }
318

319
                                                return Factory::resolve_crud_object( $variation_id, $context );
6✔
320
                                        },
114✔
321
                                ],
114✔
322
                                'refund'           => [
114✔
323
                                        'type'        => 'Refund',
114✔
324
                                        'description' => __( 'A refund object', 'wp-graphql-woocommerce' ),
114✔
325
                                        'args'        => [
114✔
326
                                                'id'     => [
114✔
327
                                                        'type'        => [ 'non_null' => 'ID' ],
114✔
328
                                                        'description' => __( 'The ID for identifying the refund', 'wp-graphql-woocommerce' ),
114✔
329
                                                ],
114✔
330
                                                'idType' => [
114✔
331
                                                        'type'        => 'RefundIdTypeEnum',
114✔
332
                                                        'description' => __( 'Type of ID being used identify refund', 'wp-graphql-woocommerce' ),
114✔
333
                                                ],
114✔
334
                                        ],
114✔
335
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
336
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
3✔
337
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
3✔
338

339
                                                $refund_id = null;
3✔
340
                                                switch ( $id_type ) {
341
                                                        case 'database_id':
3✔
342
                                                                $refund_id = absint( $id );
1✔
343
                                                                break;
1✔
344
                                                        case 'global_id':
3✔
345
                                                        default:
346
                                                                $id_components = Relay::fromGlobalId( $id );
3✔
347
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
3✔
348
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
349
                                                                }
350
                                                                $refund_id = absint( $id_components['id'] );
3✔
351
                                                                break;
3✔
352
                                                }
353

354
                                                if ( empty( $refund_id ) ) {
3✔
355
                                                        /* translators: %1$s: ID type, %2$s: ID value */
356
                                                        throw new UserError( sprintf( __( 'No refund ID was found corresponding to the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
357
                                                }
358

359
                                                if ( 'shop_order_refund' !== OrderUtil::get_order_type( $refund_id ) ) {
3✔
360
                                                        /* translators: %1$s: ID type, %2$s: ID value */
361
                                                        throw new UserError( sprintf( __( 'No refund exists with the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $id ) );
×
362
                                                }
363

364
                                                // Check if user authorized to view order.
365
                                                /**
366
                                                 * Get refund post type.
367
                                                 *
368
                                                 * @var \WP_Post_Type $post_type
369
                                                 */
370
                                                $post_type     = get_post_type_object( 'shop_order_refund' );
3✔
371
                                                $is_authorized = current_user_can( $post_type->cap->edit_others_posts );
3✔
372
                                                if ( get_current_user_id() ) {
3✔
373
                                                        $refund = \wc_get_order( $refund_id );
3✔
374
                                                        if ( ! is_object( $refund ) || ! is_a( $refund, \WC_Order_Refund::class ) ) {
3✔
375
                                                                throw new UserError( __( 'Failed to retrieve refund', 'wp-graphql-woocommerce' ) );
×
376
                                                        }
377
                                                        $order_id = $refund->get_parent_id();
3✔
378

379
                                                        /** @var \WC_Order[] $orders */
380
                                                        $orders = wc_get_orders(
3✔
381
                                                                [
3✔
382
                                                                        'type'          => 'shop_order',
3✔
383
                                                                        'post__in'      => [ $order_id ],
3✔
384
                                                                        'customer_id'   => get_current_user_id(),
3✔
385
                                                                        'no_rows_found' => true,
3✔
386
                                                                        'return'        => 'ids',
3✔
387
                                                                ]
3✔
388
                                                        );
3✔
389

390
                                                        if ( in_array( $order_id, $orders, true ) ) {
3✔
391
                                                                $is_authorized = true;
3✔
392
                                                        }
393
                                                }//end if
394

395
                                                // Throw if authorized to view refund.
396
                                                if ( ! $is_authorized ) {
3✔
397
                                                        throw new UserError( __( 'Not authorized to access this refund', 'wp-graphql-woocommerce' ) );
1✔
398
                                                }
399

400
                                                return Factory::resolve_crud_object( $refund_id, $context );
3✔
401
                                        },
114✔
402
                                ],
114✔
403
                                'shippingMethod'   => [
114✔
404
                                        'type'        => 'ShippingMethod',
114✔
405
                                        'description' => __( 'A shipping method object', 'wp-graphql-woocommerce' ),
114✔
406
                                        'args'        => [
114✔
407
                                                'id'     => [
114✔
408
                                                        'type'        => 'ID',
114✔
409
                                                        'description' => __( 'The ID for identifying the shipping method', 'wp-graphql-woocommerce' ),
114✔
410
                                                ],
114✔
411
                                                'idType' => [
114✔
412
                                                        'type'        => 'ShippingMethodIdTypeEnum',
114✔
413
                                                        'description' => __( 'Type of ID being used identify product variation', 'wp-graphql-woocommerce' ),
114✔
414
                                                ],
114✔
415
                                        ],
114✔
416
                                        'resolve'     => static function ( $source, array $args ) {
114✔
417
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
1✔
418
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
1✔
419

420
                                                $method_id = null;
1✔
421
                                                switch ( $id_type ) {
422
                                                        case 'database_id':
1✔
423
                                                                $method_id = $id;
1✔
424
                                                                break;
1✔
425
                                                        case 'global_id':
1✔
426
                                                        default:
427
                                                                $id_components = Relay::fromGlobalId( $id );
1✔
428
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
1✔
429
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
430
                                                                }
431
                                                                $method_id = $id_components['id'];
1✔
432
                                                                break;
1✔
433
                                                }
434

435
                                                return Factory::resolve_shipping_method( $method_id );
1✔
436
                                        },
114✔
437
                                ],
114✔
438
                                'taxRate'          => [
114✔
439
                                        'type'        => 'TaxRate',
114✔
440
                                        'description' => __( 'A tax rate object', 'wp-graphql-woocommerce' ),
114✔
441
                                        'args'        => [
114✔
442
                                                'id'     => [
114✔
443
                                                        'type'        => 'ID',
114✔
444
                                                        'description' => __( 'The ID for identifying the tax rate', 'wp-graphql-woocommerce' ),
114✔
445
                                                ],
114✔
446
                                                'idType' => [
114✔
447
                                                        'type'        => 'TaxRateIdTypeEnum',
114✔
448
                                                        'description' => __( 'Type of ID being used identify tax rate', 'wp-graphql-woocommerce' ),
114✔
449
                                                ],
114✔
450
                                        ],
114✔
451
                                        'resolve'     => static function ( $source, array $args, AppContext $context ) {
114✔
452
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
1✔
453
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
1✔
454

455
                                                $rate_id = null;
1✔
456
                                                switch ( $id_type ) {
457
                                                        case 'database_id':
1✔
458
                                                                $rate_id = absint( $id );
1✔
459
                                                                break;
1✔
460
                                                        case 'global_id':
1✔
461
                                                        default:
462
                                                                $id_components = Relay::fromGlobalId( $id );
1✔
463
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
1✔
464
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
465
                                                                }
466
                                                                $rate_id = absint( $id_components['id'] );
1✔
467
                                                                break;
1✔
468
                                                }
469

470
                                                return Factory::resolve_tax_rate( $rate_id, $context );
1✔
471
                                        },
114✔
472
                                ],
114✔
473
                                'countries'        => [
114✔
474
                                        'type'        => [ 'list_of' => 'CountriesEnum' ],
114✔
475
                                        'description' => __( 'Countries', 'wp-graphql-woocommerce' ),
114✔
476
                                        'resolve'     => static function () {
114✔
477
                                                $wc_countries = new \WC_Countries();
1✔
478
                                                $countries    = $wc_countries->get_countries();
1✔
479

480
                                                return array_keys( $countries );
1✔
481
                                        },
114✔
482
                                ],
114✔
483
                                'allowedCountries' => [
114✔
484
                                        'type'        => [ 'list_of' => 'CountriesEnum' ],
114✔
485
                                        'description' => __( 'Countries that the store sells to', 'wp-graphql-woocommerce' ),
114✔
486
                                        'resolve'     => static function () {
114✔
487
                                                $wc_countries = new \WC_Countries();
1✔
488
                                                $countries    = $wc_countries->get_allowed_countries();
1✔
489

490
                                                return array_keys( $countries );
1✔
491
                                        },
114✔
492
                                ],
114✔
493
                                'countryStates'    => [
114✔
494
                                        'type'        => [ 'list_of' => 'CountryState' ],
114✔
495
                                        'args'        => [
114✔
496
                                                'country' => [
114✔
497
                                                        'type'        => [ 'non_null' => 'CountriesEnum' ],
114✔
498
                                                        'description' => __( 'Target country', 'wp-graphql-woocommerce' ),
114✔
499
                                                ],
114✔
500
                                        ],
114✔
501
                                        'description' => __( 'Countries that the store sells to', 'wp-graphql-woocommerce' ),
114✔
502
                                        'resolve'     => static function ( $_, $args ) {
114✔
503
                                                $country      = $args['country'];
1✔
504
                                                $wc_countries = new \WC_Countries();
1✔
505
                                                $states       = $wc_countries->get_shipping_country_states();
1✔
506

507
                                                if ( ! empty( $states ) && ! empty( $states[ $country ] ) ) {
1✔
508
                                                        $formatted_states = [];
1✔
509
                                                        foreach ( $states[ $country ] as $code => $name ) {
1✔
510
                                                                $formatted_states[] = compact( 'name', 'code' );
1✔
511
                                                        }
512

513
                                                        return $formatted_states;
1✔
514
                                                }
515

516
                                                return [];
×
517
                                        },
114✔
518
                                ],
114✔
519
                                'collectionStats'  => [
114✔
520
                                        'type'        => 'CollectionStats',
114✔
521
                                        'args'        => [
114✔
522
                                                'calculatePriceRange'        => [
114✔
523
                                                        'type'        => 'Boolean',
114✔
524
                                                        'description' => __( 'If true, calculates the minimum and maximum product prices for the collection.', 'wp-graphql-woocommerce' ),
114✔
525
                                                ],
114✔
526
                                                'calculateRatingCounts'      => [
114✔
527
                                                        'type'        => 'Boolean',
114✔
528
                                                        'description' => __( 'If true, calculates rating counts for products in the collection.', 'wp-graphql-woocommerce' ),
114✔
529
                                                ],
114✔
530
                                                'calculateStockStatusCounts' => [
114✔
531
                                                        'type'        => 'Boolean',
114✔
532
                                                        'description' => __( 'If true, calculates stock counts for products in the collection.', 'wp-graphql-woocommerce' ),
114✔
533
                                                ],
114✔
534
                                                'taxonomies'                 => [
114✔
535
                                                        'type' => [ 'list_of' => 'CollectionStatsQueryInput' ],
114✔
536
                                                ],
114✔
537
                                                'where'                      => [
114✔
538
                                                        'type' => 'CollectionStatsWhereArgs',
114✔
539
                                                ],
114✔
540
                                        ],
114✔
541
                                        'description' => __( 'Statistics for a product taxonomy query', 'wp-graphql-woocommerce' ),
114✔
542
                                        'resolve'     => static function ( $_, $args ) {
114✔
543
                                                $filters = new ProductQueryFilters(); // @phpstan-ignore-line
1✔
544
                                                $data    = [
1✔
545
                                                        'min_price'           => null,
1✔
546
                                                        'max_price'           => null,
1✔
547
                                                        'attribute_counts'    => null,
1✔
548
                                                        'stock_status_counts' => null,
1✔
549
                                                        'rating_counts'       => null,
1✔
550
                                                ];
1✔
551

552
                                                // Process client-side filters.
553
                                                $request = Collection_Stats_Type::prepare_rest_request( $args['where'] ?? [] );
1✔
554

555
                                                // Format taxonomies.
556
                                                if ( ! empty( $args['taxonomies'] ) ) {
1✔
557
                                                        $calculate_attribute_counts = [];
1✔
558
                                                        foreach ( $args['taxonomies'] as $attribute_to_count ) {
1✔
559
                                                                $calculate_attribute_counts[] = [
1✔
560
                                                                        'taxonomy'   => $attribute_to_count['taxonomy'],
1✔
561
                                                                        'query_type' => strtolower( $attribute_to_count['relation'] ),
1✔
562
                                                                ];
1✔
563
                                                        }
564
                                                        $request->set_param( 'calculate_attribute_counts', $calculate_attribute_counts );
1✔
565
                                                }
566
 
567
                                                $request->set_param( 'calculate_price_range', $args['calculatePriceRange'] ?? false );
1✔
568
                                                $request->set_param( 'calculate_stock_status_counts', $args['calculateStockStatusCounts'] ?? false );
1✔
569
                                                $request->set_param( 'calculate_rating_counts', $args['calculateRatingCounts'] ?? false );
1✔
570

571

572
                                                
573
                                                if ( ! empty( $request['calculate_price_range'] ) ) {
1✔
574
                                                        $filter_request = clone $request;
1✔
575
                                                        $filter_request->set_param( 'min_price', null );
1✔
576
                                                        $filter_request->set_param( 'max_price', null );
1✔
577

578
                                                        $price_results     = $filters->get_filtered_price( $filter_request ); // @phpstan-ignore-line
1✔
579
                                                        $data['min_price'] = $price_results->min_price;
1✔
580
                                                        $data['max_price'] = $price_results->max_price;
1✔
581
                                                }
582

583
                                                if ( ! empty( $request['calculate_stock_status_counts'] ) ) {
1✔
584
                                                        $filter_request = clone $request;
1✔
585
                                                        $counts         = $filters->get_stock_status_counts( $filter_request ); // @phpstan-ignore-line
1✔
586
                                
587
                                                        $data['stock_status_counts'] = [];
1✔
588
                                
589
                                                        foreach ( $counts as $key => $value ) {
1✔
590
                                                                $data['stock_status_counts'][] = (object) [
1✔
591
                                                                        'status' => $key,
1✔
592
                                                                        'count'  => $value,
1✔
593
                                                                ];
1✔
594
                                                        }
595
                                                }
596

597
                                                if ( ! empty( $request['calculate_attribute_counts'] ) ) {
1✔
598
                                                        foreach ( $request['calculate_attribute_counts'] as $attributes_to_count ) {
1✔
599
                                                                if ( ! isset( $attributes_to_count['taxonomy'] ) ) {
1✔
600
                                                                        continue;
×
601
                                                                }
602

603
                                                                $taxonomy = $attributes_to_count['taxonomy'];
1✔
604
                                                                $counts   = $filters->get_attribute_counts( $request, $taxonomy ); // @phpstan-ignore-line
1✔
605

606
                                                                $data['attribute_counts'][ $taxonomy ] = [];
1✔
607
                                                                foreach ( $counts as $key => $value ) {
1✔
608
                                                                        $data['attribute_counts'][ $taxonomy ][] = (object) [
1✔
609
                                                                                'taxonomy' => $taxonomy,
1✔
610
                                                                                'termId'   => $key,
1✔
611
                                                                                'count'    => $value,
1✔
612
                                                                        ];
1✔
613
                                                                }
614
                                                        }
615
                                                }
616

617
                                                if ( ! empty( $request['calculate_rating_counts'] ) ) {
1✔
618
                                                        $filter_request        = clone $request;
1✔
619
                                                        $counts                = $filters->get_rating_counts( $filter_request ); // @phpstan-ignore-line
1✔
620
                                                        $data['rating_counts'] = [];
1✔
621
                                
622
                                                        foreach ( $counts as $key => $value ) {
1✔
623
                                                                $data['rating_counts'][] = (object) [
×
624
                                                                        'rating' => $key,
×
625
                                                                        'count'  => $value,
×
626
                                                                ];
×
627
                                                        }
628
                                                }
629

630
                                                return $data;
1✔
631
                                        },
114✔
632
                                ],  
114✔
633
                        ]
114✔
634
                );
114✔
635

636
                // Product queries.
637
                $unsupported_type_enabled = woographql_setting( 'enable_unsupported_product_type', 'off' );
114✔
638

639
                $product_type_keys = array_keys( WooGraphQL::get_enabled_product_types() );
114✔
640
                if ( 'on' === $unsupported_type_enabled ) {
114✔
641
                        $product_type_keys[] = 'unsupported';
1✔
642
                }
643

644
                $product_type_keys = apply_filters( 'woographql_register_product_queries', $product_type_keys );
114✔
645

646
                $product_types = WooGraphQL::get_enabled_product_types();
114✔
647
                if ( 'on' === $unsupported_type_enabled ) {
114✔
648
                        $product_types['unsupported'] = WooGraphQL::get_supported_product_type();
1✔
649
                }
650

651
                foreach ( $product_type_keys as $type_key ) {
114✔
652
                        $field_name = "{$type_key}Product";
114✔
653
                        $type_name  = $product_types[ $type_key ] ?? null;
114✔
654

655
                        if ( empty( $type_name ) ) {
114✔
656
                                continue;
×
657
                        }
658

659
                        register_graphql_field(
114✔
660
                                'RootQuery',
114✔
661
                                $field_name,
114✔
662
                                [
114✔
663
                                        'type'              => $type_name,
114✔
664
                                        /* translators: Product type slug */
665
                                        'description'       => sprintf( __( 'A %s product object', 'wp-graphql-woocommerce' ), $type_key ),
114✔
666
                                        'deprecationReason' => 'Use "product" instead.',
114✔
667
                                        'args'              => [
114✔
668
                                                'id'     => [
114✔
669
                                                        'type'        => 'ID',
114✔
670
                                                        'description' => sprintf(
114✔
671
                                                                /* translators: %s: product type */
672
                                                                __( 'The ID for identifying the %s product', 'wp-graphql-woocommerce' ),
114✔
673
                                                                $type_name
114✔
674
                                                        ),
114✔
675
                                                ],
114✔
676
                                                'idType' => [
114✔
677
                                                        'type'        => 'ProductIdTypeEnum',
114✔
678
                                                        'description' => __( 'Type of ID being used identify product', 'wp-graphql-woocommerce' ),
114✔
679
                                                ],
114✔
680
                                        ],
114✔
681
                                        'resolve'           => static function ( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $type_key, $unsupported_type_enabled ) {
114✔
682
                                                $id      = isset( $args['id'] ) ? $args['id'] : null;
1✔
683
                                                $id_type = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
1✔
684

685
                                                $product_id = null;
1✔
686
                                                switch ( $id_type ) {
687
                                                        case 'sku':
1✔
688
                                                                $product_id = \wc_get_product_id_by_sku( $id );
×
689
                                                                break;
×
690
                                                        case 'slug':
1✔
691
                                                                $post       = get_page_by_path( $id, OBJECT, 'product' );
×
692
                                                                $product_id = ! empty( $post ) ? absint( $post->ID ) : 0;
×
693
                                                                break;
×
694
                                                        case 'database_id':
1✔
695
                                                                $product_id = absint( $id );
×
696
                                                                break;
×
697
                                                        case 'global_id':
1✔
698
                                                        default:
699
                                                                $id_components = Relay::fromGlobalId( $id );
1✔
700
                                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
1✔
701
                                                                        throw new UserError( __( 'The "id" is invalid', 'wp-graphql-woocommerce' ) );
×
702
                                                                }
703
                                                                $product_id = absint( $id_components['id'] );
1✔
704
                                                                break;
1✔
705
                                                }
706

707
                                                if ( empty( $product_id ) ) {
1✔
708
                                                        /* translators: %1$s: ID type, %2$s: ID value */
709
                                                        throw new UserError( sprintf( __( 'No product ID was found corresponding to the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $product_id ) );
×
710
                                                }
711

712
                                                if ( \WC()->product_factory->get_product_type( $product_id ) !== $type_key && 'off' === $unsupported_type_enabled ) {
1✔
713
                                                        /* translators: Invalid product type message %1$s: Product ID, %2$s: Product type */
714
                                                        throw new UserError( sprintf( __( 'This product of ID %1$s is not a %2$s product', 'wp-graphql-woocommerce' ), $product_id, $type_key ) );
×
715
                                                }
716

717
                                                $product = get_post( $product_id );
1✔
718
                                                if ( ! is_object( $product ) || 'product' !== $product->post_type ) {
1✔
719
                                                        /* translators: %1$s: ID type, %2$s: ID value */
720
                                                        throw new UserError( sprintf( __( 'No product exists with the %1$s: %2$s', 'wp-graphql-woocommerce' ), $id_type, $product_id ) );
×
721
                                                }
722

723
                                                return Factory::resolve_crud_object( $product_id, $context );
1✔
724
                                        },
114✔
725
                                ]
114✔
726
                        );
114✔
727
                }//end foreach
728
        }
729
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc