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

wp-graphql / wp-graphql-woocommerce / 9121529590

17 May 2024 01:27AM UTC coverage: 84.681% (+0.1%) from 84.581%
9121529590

push

github

web-flow
`collectionStats` query fully implemented (#849)

* fix: WPGraphQL v1.24.x support added

* fix: WPGraphQL v1.24.x support added

* chore: Linter and PHPStan compliance met

* chore: composer.json fixed

* fix: Unnecessary type check restored

* chore: Linter and PHPStan compliance met

* devops: CollectionStatsQueryTest patched

138 of 168 new or added lines in 10 files covered. (82.14%)

1 existing line in 1 file now uncovered.

11199 of 13225 relevant lines covered (84.68%)

60.39 hits per line

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

88.48
/includes/type/object/class-collection-stats-type.php
1
<?php
2
/**
3
 * WPObject Type - Collection_Stats_Type
4
 *
5
 * @package WPGraphQL\WooCommerce\Type\WPObject
6
 * @since   0.18.0
7
 */
8

9
namespace WPGraphQL\WooCommerce\Type\WPObject;
10

11
/**
12
 * Class Collection_Stats_Type
13
 */
14
class Collection_Stats_Type {
15
        /**
16
         * Register CollectionStats type to the WPGraphQL schema
17
         *
18
         * @return void
19
         */
20
        public static function register() {
21
                register_graphql_object_type(
118✔
22
                        'PriceRange',
118✔
23
                        [
118✔
24
                                'eagerlyLoadType' => true,
118✔
25
                                'description'     => __( 'Price range', 'wp-graphql-woocommerce' ),
118✔
26
                                'fields'          => [
118✔
27
                                        'minPrice' => [
118✔
28
                                                'type'        => 'String',
118✔
29
                                                'args'        => [
118✔
30
                                                        'format' => [
118✔
31
                                                                'type'        => 'PricingFieldFormatEnum',
118✔
32
                                                                'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
118✔
33
                                                        ],
118✔
34
                                                ],
118✔
35
                                                'description' => __( 'Minimum price', 'wp-graphql-woocommerce' ),
118✔
36
                                                'resolve'     => static function ( $source, array $args ) {
118✔
37
                                                        if ( empty( $source['min_price'] ) ) {
×
38
                                                                return null;
×
39
                                                        }
40

41
                                                        if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
×
42
                                                                return $source['min_price'];
×
43
                                                        }
44

45
                                                        return wc_graphql_price( $source['min_price'] );
×
46
                                                },
118✔
47
                                        ],
118✔
48
                                        'maxPrice' => [
118✔
49
                                                'type'        => 'String',
118✔
50
                                                'args'        => [
118✔
51
                                                        'format' => [
118✔
52
                                                                'type'        => 'PricingFieldFormatEnum',
118✔
53
                                                                'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
118✔
54
                                                        ],
118✔
55
                                                ],
118✔
56
                                                'description' => __( 'Maximum price', 'wp-graphql-woocommerce' ),
118✔
57
                                                'resolve'     => static function ( $source, array $args ) {
118✔
58
                                                        if ( empty( $source['max_price'] ) ) {
×
59
                                                                return null;
×
60
                                                        }
61

62
                                                        if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
×
63
                                                                return $source['max_price'];
×
64
                                                        }
65

66
                                                        return wc_graphql_price( $source['max_price'] );
×
67
                                                },
118✔
68
                                        ],
118✔
69
                                ],
118✔
70
                        ]
118✔
71
                );
118✔
72

73
                register_graphql_object_type(
118✔
74
                        'AttributeCount',
118✔
75
                        [
118✔
76
                                'eagerlyLoadType' => true,
118✔
77
                                'description'     => __( 'Product attribute terms count', 'wp-graphql-woocommerce' ),
118✔
78
                                'fields'          => [
118✔
79
                                        'slug'  => [
118✔
80
                                                'type'        => [ 'non_null' => 'ProductAttributeEnum' ],
118✔
81
                                                'description' => __( 'Attribute name', 'wp-graphql-woocommerce' ),
118✔
82
                                                'resolve'     => static function ( $source ) {
118✔
83
                                                        return $source->name;
4✔
84
                                                },
118✔
85
                                        ],
118✔
86
                                        'label' => [
118✔
87
                                                'type'        => [ 'non_null' => 'String' ],
118✔
88
                                                'description' => __( 'Attribute taxonomy', 'wp-graphql-woocommerce' ),
118✔
89
                                                'resolve'     => static function ( $source ) {
118✔
90
                                                        $taxonomy = get_taxonomy( $source->name );
3✔
91

92
                                                        if ( ! $taxonomy instanceof \WP_Taxonomy ) {
3✔
93
                                                                return null;
×
94
                                                        }
95
                                                        return $taxonomy->label;
3✔
96
                                                },
118✔
97
                                        ],
118✔
98
                                        'name'  => [
118✔
99
                                                'type'        => [ 'non_null' => 'String' ],
118✔
100
                                                'description' => __( 'Attribute name', 'wp-graphql-woocommerce' ),
118✔
101
                                                'resolve'     => static function ( $source ) {
118✔
102
                                                        $taxonomy = get_taxonomy( $source->name );
3✔
103

104
                                                        if ( ! $taxonomy instanceof \WP_Taxonomy ) {
3✔
105
                                                                return null;
×
106
                                                        }
107
                                                        return $taxonomy->labels->singular_name;
3✔
108
                                                },
118✔
109
                                        ],
118✔
110
                                        'terms' => [
118✔
111
                                                'type'        => [ 'list_of' => 'SingleAttributeCount' ],
118✔
112
                                                'description' => __( 'Attribute terms', 'wp-graphql-woocommerce' ),
118✔
113
                                        ],
118✔
114
                                ],
118✔
115
                        ]
118✔
116
                );
118✔
117

118
                register_graphql_object_type(
118✔
119
                        'SingleAttributeCount',
118✔
120
                        [
118✔
121
                                'eagerlyLoadType' => true,
118✔
122
                                'description'     => __( 'Single attribute term count', 'wp-graphql-woocommerce' ),
118✔
123
                                'fields'          => [
118✔
124
                                        'termId' => [
118✔
125
                                                'type'        => [ 'non_null' => 'ID' ],
118✔
126
                                                'description' => __( 'Term ID', 'wp-graphql-woocommerce' ),
118✔
127
                                        ],
118✔
128
                                        'count'  => [
118✔
129
                                                'type'        => 'Int',
118✔
130
                                                'description' => __( 'Number of products.', 'wp-graphql-woocommerce' ),
118✔
131
                                        ],
118✔
132
                                        'node'   => [
118✔
133
                                                'type'        => 'TermNode',
118✔
134
                                                'description' => __( 'Term object.', 'wp-graphql-woocommerce' ),
118✔
135
                                                'resolve'     => static function ( $source ) {
118✔
136
                                                        if ( empty( $source->termId ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
4✔
137
                                                                return null;
×
138
                                                        }
139

140
                                                        /**
141
                                                         * Term object.
142
                                                         *
143
                                                         * @var \WP_Term $term
144
                                                         */
145
                                                        $term = get_term( $source->termId ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
4✔
146
                                                        if ( ! $term instanceof \WP_Term ) {
4✔
147
                                                                return null;
×
148
                                                        }
149
                                                        return new \WPGraphQL\Model\Term( $term );
4✔
150
                                                },
118✔
151
                                        ],
118✔
152
                                ],
118✔
153
                        ]
118✔
154
                );
118✔
155

156
                register_graphql_object_type(
118✔
157
                        'RatingCount',
118✔
158
                        [
118✔
159
                                'eagerlyLoadType' => true,
118✔
160
                                'description'     => __( 'Single rating count', 'wp-graphql-woocommerce' ),
118✔
161
                                'fields'          => [
118✔
162
                                        'rating' => [
118✔
163
                                                'type'        => [ 'non_null' => 'Int' ],
118✔
164
                                                'description' => __( 'Average rating', 'wp-graphql-woocommerce' ),
118✔
165
                                        ],
118✔
166
                                        'count'  => [
118✔
167
                                                'type'        => 'Int',
118✔
168
                                                'description' => __( 'Number of products', 'wp-graphql-woocommerce' ),
118✔
169
                                        ],
118✔
170
                                ],
118✔
171
                        ]
118✔
172
                );
118✔
173

174
                register_graphql_object_type(
118✔
175
                        'StockStatusCount',
118✔
176
                        [
118✔
177
                                'eagerlyLoadType' => true,
118✔
178
                                'description'     => __( 'Single stock status count', 'wp-graphql-woocommerce' ),
118✔
179
                                'fields'          => [
118✔
180
                                        'status' => [
118✔
181
                                                'type'        => [ 'non_null' => 'StockStatusEnum' ],
118✔
182
                                                'description' => __( 'Status', 'wp-graphql-woocommerce' ),
118✔
183
                                        ],
118✔
184
                                        'count'  => [
118✔
185
                                                'type'        => 'Int',
118✔
186
                                                'description' => __( 'Number of products.', 'wp-graphql-woocommerce' ),
118✔
187
                                        ],
118✔
188
                                ],
118✔
189
                        ]
118✔
190
                );
118✔
191

192
                register_graphql_object_type(
118✔
193
                        'CollectionStats',
118✔
194
                        [
118✔
195
                                'description' => __( 'Data about a collection of products', 'wp-graphql-woocommerce' ),
118✔
196
                                'fields'      => [
118✔
197
                                        'priceRange'        => [
118✔
198
                                                'type'        => 'PriceRange',
118✔
199
                                                'description' => __( 'Min and max prices found in collection of products, provided using the smallest unit of the currency', 'wp-graphql-woocommerce' ),
118✔
200
                                                'resolve'     => static function ( $source ) {
118✔
201
                                                        $min_price = ! empty( $source['min_price'] ) ? $source['min_price'] : null;
×
202
                                                        $max_price = ! empty( $source['max_price'] ) ? $source['max_price'] : null;
×
203
                                                        return compact( 'min_price', 'max_price' );
×
204
                                                },
118✔
205
                                        ],
118✔
206
                                        'attributeCounts'   => [
118✔
207
                                                'type'        => [ 'list_of' => 'AttributeCount' ],
118✔
208
                                                'args'        => [
118✔
209
                                                        'page'    => [
118✔
210
                                                                'type'        => 'Int',
118✔
211
                                                                'description' => __( 'Page of results to return', 'wp-graphql-woocommerce' ),
118✔
212
                                                        ],
118✔
213
                                                        'perPage' => [
118✔
214
                                                                'type'        => 'Int',
118✔
215
                                                                'description' => __( 'Number of results to return per page', 'wp-graphql-woocommerce' ),
118✔
216
                                                        ],
118✔
217
                                                ],
118✔
218
                                                'description' => __( 'Returns number of products within attribute terms', 'wp-graphql-woocommerce' ),
118✔
219
                                                'resolve'     => static function ( $source, $args ) {
118✔
220
                                                        $page             = ! empty( $args['page'] ) ? $args['page'] : 1;
4✔
221
                                                        $per_page         = ! empty( $args['perPage'] ) ? $args['perPage'] : 0;
4✔
222
                                                        $attribute_counts = ! empty( $source['attribute_counts'] ) ? $source['attribute_counts'] : [];
4✔
223
                                                        $attribute_counts = array_slice(
4✔
224
                                                                $attribute_counts,
4✔
225
                                                                ( $page - 1 ) * $per_page,
4✔
226
                                                                0 < $per_page ? $per_page : null
4✔
227
                                                        );
4✔
228

229
                                                        return array_map(
4✔
230
                                                                static function ( $name, $terms ) {
4✔
231
                                                                        return (object) compact( 'name', 'terms' );
4✔
232
                                                                },
4✔
233
                                                                array_keys( $attribute_counts ),
4✔
234
                                                                array_values( $attribute_counts )
4✔
235
                                                        );
4✔
236
                                                },
118✔
237
                                        ],
118✔
238
                                        'ratingCounts'      => [
118✔
239
                                                'type'        => [ 'list_of' => 'RatingCount' ],
118✔
240
                                                'args'        => [
118✔
241
                                                        'page'    => [
118✔
242
                                                                'type'        => 'Int',
118✔
243
                                                                'description' => __( 'Page of results to return', 'wp-graphql-woocommerce' ),
118✔
244
                                                        ],
118✔
245
                                                        'perPage' => [
118✔
246
                                                                'type'        => 'Int',
118✔
247
                                                                'description' => __( 'Number of results to return per page', 'wp-graphql-woocommerce' ),
118✔
248
                                                        ],
118✔
249
                                                ],
118✔
250
                                                'description' => __( 'Returns number of products with each average rating', 'wp-graphql-woocommerce' ),
118✔
251
                                                'resolve'     => static function ( $source, $args ) {
118✔
252
                                                        $page          = ! empty( $args['page'] ) ? $args['page'] : 1;
×
253
                                                        $per_page      = ! empty( $args['perPage'] ) ? $args['perPage'] : 0;
×
254
                                                        $rating_counts = ! empty( $source['rating_counts'] ) ? $source['rating_counts'] : [];
×
255
                                                        $rating_counts = array_slice(
×
256
                                                                $rating_counts,
×
257
                                                                ( $page - 1 ) * $per_page,
×
258
                                                                0 < $per_page ? $per_page : null
×
259
                                                        );
×
260

261
                                                        return $rating_counts;
×
262
                                                },
118✔
263
                                        ],
118✔
264
                                        'stockStatusCounts' => [
118✔
265
                                                'type'        => [ 'list_of' => 'StockStatusCount' ],
118✔
266
                                                'args'        => [
118✔
267
                                                        'page'    => [
118✔
268
                                                                'type'        => 'Int',
118✔
269
                                                                'description' => __( 'Page of results to return', 'wp-graphql-woocommerce' ),
118✔
270
                                                        ],
118✔
271
                                                        'perPage' => [
118✔
272
                                                                'type'        => 'Int',
118✔
273
                                                                'description' => __( 'Number of results to return per page', 'wp-graphql-woocommerce' ),
118✔
274
                                                        ],
118✔
275
                                                ],
118✔
276
                                                'description' => __( 'Returns number of products with each stock status', 'wp-graphql-woocommerce' ),
118✔
277
                                                'resolve'     => static function ( $source, $args ) {
118✔
278
                                                        $page                = ! empty( $args['page'] ) ? $args['page'] : 1;
1✔
279
                                                        $per_page            = ! empty( $args['perPage'] ) ? $args['perPage'] : 0;
1✔
280
                                                        $stock_status_counts = ! empty( $source['stock_status_counts'] ) ? $source['stock_status_counts'] : [];
1✔
281
                                                        $stock_status_counts = array_slice(
1✔
282
                                                                $stock_status_counts,
1✔
283
                                                                ( $page - 1 ) * $per_page,
1✔
284
                                                                0 < $per_page ? $per_page : null
1✔
285
                                                        );
1✔
286

287
                                                        return $stock_status_counts;
1✔
288
                                                },
118✔
289
                                        ],
118✔
290
                                ],
118✔
291
                        ]
118✔
292
                );
118✔
293
        }
294

295
        /**
296
         * Prepare the WP_Rest_Request instance used for the resolution of a
297
         * statistics for a product connection.
298
         *
299
         * @param array $where_args  Arguments used to filter the connection results.
300
         *
301
         * @return \WP_REST_Request
302
         */
303
        public static function prepare_rest_request( array $where_args = [] ) {
304
                $request = new \WP_REST_Request();
4✔
305
                if ( empty( $where_args ) ) {
4✔
306
                        return $request;
1✔
307
                }
308

309
                $request->set_param( 'paged', 0 );
3✔
310
                $request->set_param( 'ignore_sticky_posts', true );
3✔
311

312
                $key_mapping = [
3✔
313
                        'slugIn'       => 'slug',
3✔
314
                        'typeIn'       => 'type',
3✔
315
                        'categoryIdIn' => 'category',
3✔
316
                        'tagIdIn'      => 'tag',
3✔
317
                        'onSale'       => 'on_sale',
3✔
318
                        'stockStatus'  => 'stock_status',
3✔
319
                        'visibility'   => 'catalog_visibility',
3✔
320
                        'minPrice'     => 'min_price',
3✔
321
                        'maxPrice'     => 'max_price',
3✔
322
                ];
3✔
323

324
                $needs_formatting = [ 'attributes', 'categoryIn', 'tagIn' ];
3✔
325
                foreach ( $where_args as $key => $value ) {
3✔
326
                        if ( in_array( $key, $needs_formatting, true ) ) {
3✔
327
                                continue;
3✔
328
                        }
329

330
                        $request->set_param( $key_mapping[ $key ] ?? $key, $value );
1✔
331
                }
332

333
                if ( ! empty( $where_args['categoryIn'] ) ) {
3✔
334
                        $category_ids = array_map(
2✔
335
                                static function ( $category ) {
2✔
336
                                        $term = get_term_by( 'slug', $category, 'product_cat' );
2✔
337
                                        if ( $term && ! is_wp_error( $term ) ) {
2✔
338
                                                return $term->term_id;
2✔
339
                                        }
340
                                        return 0;
×
341
                                },
2✔
342
                                $where_args['categoryIn']
2✔
343
                        );
2✔
344
                        $set_category = $request->get_param( 'category' );
2✔
345
                        if ( ! empty( $set_category ) ) {
2✔
346
                                $category_ids[] = $set_category;
×
347
                                $request->set_param( 'category', $category_ids );
×
348
                        } else {
349
                                $request->set_param( 'category', $category_ids );
2✔
350
                        }
351
                }
352

353
                if ( ! empty( $where_args['tagIn'] ) ) {
3✔
354
                        $tag_ids = array_map(
1✔
355
                                static function ( $tag ) {
1✔
356
                                        $term = get_term_by( 'slug', $tag, 'product_tag' );
1✔
357
                                        if ( $term && ! is_wp_error( $term ) ) {
1✔
358
                                                return $term->term_id;
1✔
359
                                        }
NEW
360
                                        return 0;
×
361
                                },
1✔
362
                                $where_args['tagIn']
1✔
363
                        );
1✔
364
                        $set_tag = $request->get_param( 'tag' );
1✔
365
                        if ( ! empty( $set_tag ) ) {
1✔
NEW
366
                                $tag_ids[] = $set_tag;
×
NEW
367
                                $request->set_param( 'tag', $tag_ids );
×
368
                        } else {
369
                                $request->set_param( 'tag', $tag_ids );
1✔
370
                        }
371
                }
372

373
                if ( ! empty( $where_args['attributes'] ) && ! empty( $where_args['attributes']['queries'] ) ) {
3✔
374
                        $attributes       = $where_args['attributes']['queries'];
2✔
375
                        $att_queries      = [];
2✔
376
                        $operator_mapping = [
2✔
377
                                'IN'     => 'in',
2✔
378
                                'NOT IN' => 'not_in',
2✔
379
                                'AND'    => 'and',
2✔
380
                        ];
2✔
381

382
                        foreach ( $attributes as $filter ) {
2✔
383
                                if ( empty( $filter['terms'] ) && empty( $filter['ids'] ) ) {
2✔
NEW
384
                                        continue;
×
385
                                }
386

387
                                $operator = ! empty( $filter['operator'] ) ? $operator_mapping[ $filter['operator'] ] : 'in';
2✔
388
                                if ( ! empty( $filter['terms'] ) ) {
2✔
389
                                        foreach ( $filter['terms'] as $term ) {
1✔
390
                                                $att_queries[] = [
1✔
391
                                                        'attribute' => $filter['taxonomy'],
1✔
392
                                                        'operator'  => $operator,
1✔
393
                                                        'slug'      => $term,
1✔
394
                                                ];
1✔
395
                                        }
396
                                }
397

398
                                if ( ! empty( $filter['ids'] ) ) {
2✔
399
                                        foreach ( $filter['ids'] as $term_id ) {
1✔
400
                                                $att_queries[] = [
1✔
401
                                                        'attribute' => $filter['taxonomy'],
1✔
402
                                                        'operator'  => $operator,
1✔
403
                                                        'term_id'   => $term_id,
1✔
404
                                                ];
1✔
405
                                        }
406
                                }
407
                        }
408

409
                        if ( ! empty( $att_queries ) ) {
2✔
410
                                $request->set_param( 'attributes', $att_queries );
2✔
411
                        }
412

413
                        if ( ! empty( $where_args['attributes']['relation'] ) ) {
2✔
NEW
414
                                $relation = $where_args['attributes']['relation'];
×
NEW
415
                                if ( 'NOT_IN' === $relation ) {
×
NEW
416
                                        graphql_debug( __( 'NOT_IN relation is not supported for attributes queries top-level "relation" field. Use "IN" or "AND" instead.', 'wp-graphql-woocommerce' ) );
×
NEW
417
                                        $relation = 'IN';
×
418
                                }
NEW
419
                                $request->set_param( 'attributes_relation', $where_args['attributes']['relation'] );
×
420
                        }
421
                }//end if
422

423
                return $request;
3✔
424
        }
425
}
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

© 2026 Coveralls, Inc