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

wp-graphql / wp-graphql-woocommerce / 11714183904

07 Nov 2024 12:23AM UTC coverage: 83.665% (-0.8%) from 84.451%
11714183904

push

github

web-flow
devops: Name officially changed to "WPGraphQL for WooCommerce" (#900)

* devops: Name officially changed to "WPGraphQL for WooCommerce"

* devops: GA workflow environment updated

* devops: GA workflow environment updated

* devops: composer-git-hooks downgraded to "2.8.5"

* devops: Unstable session manager tests skipped

* chore: cleanup applied

* devops: Docker configurations updated

* devops: Docker configurations updated

* devops: Docker configurations updated

* devops: Docker configurations updated

* devops: Docker configurations updated

2 of 8 new or added lines in 5 files covered. (25.0%)

268 existing lines in 20 files now uncovered.

12431 of 14858 relevant lines covered (83.67%)

71.79 hits per line

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

64.96
/includes/data/mutation/class-cart-mutation.php
1
<?php
2
/**
3
 * Defines helper functions for executing mutations related to the cart.
4
 *
5
 * @package WPGraphQL\WooCommerce\Data\Mutation
6
 * @since 0.1.0
7
 */
8

9
namespace WPGraphQL\WooCommerce\Data\Mutation;
10

11
use GraphQL\Error\UserError;
12
use WPGraphQL\WooCommerce\Data\Factory;
13

14
/**
15
 * Class - Cart_Mutation
16
 */
17
class Cart_Mutation {
18
        /**
19
         * Retrieve `cart` output field defintion
20
         *
21
         * @param bool $fallback  Should cart be retrieved, if not provided in payload.
22
         * @return array
23
         */
24
        public static function get_cart_field( $fallback = false ) {
25
                return [
141✔
26
                        'type'    => 'Cart',
141✔
27
                        'resolve' => static function ( $payload ) use ( $fallback ) {
141✔
28
                                $cart = ! empty( $payload['cart'] ) ? $payload['cart'] : null;
4✔
29

30
                                if ( is_null( $cart ) && $fallback ) {
4✔
31
                                        $cart = Factory::resolve_cart();
2✔
32
                                }
33
                                return $cart;
4✔
34
                        },
141✔
35
                ];
141✔
36
        }
37

38
        /**
39
         * Returns a cart item.
40
         *
41
         * @param array                                $input   Input data describing cart item.
42
         * @param \WPGraphQL\AppContext                $context AppContext instance.
43
         * @param \GraphQL\Type\Definition\ResolveInfo $info    Query info.
44
         *
45
         * @throws \GraphQL\Error\UserError Missing/Invalid input.
46
         *
47
         * @return array
48
         */
49
        public static function prepare_cart_item( $input, $context, $info ) {
50
                if ( empty( $input['productId'] ) ) {
6✔
51
                        throw new UserError( __( 'No product ID provided', 'wp-graphql-woocommerce' ) );
×
52
                }
53

54
                if ( ! \wc_get_product( $input['productId'] ) ) {
6✔
55
                        throw new UserError( __( 'No product found matching the ID provided', 'wp-graphql-woocommerce' ) );
2✔
56
                }
57

58
                $cart_item_args   = [ $input['productId'] ];
6✔
59
                $cart_item_args[] = ! empty( $input['quantity'] ) ? $input['quantity'] : 1;
6✔
60
                $cart_item_args[] = ! empty( $input['variationId'] ) ? $input['variationId'] : 0;
6✔
61
                $cart_item_args[] = ! empty( $input['variation'] ) ? self::prepare_attributes( $input['productId'], $input['variation'] ) : [];
6✔
62
                $cart_item_args[] = ! empty( $input['extraData'] )
6✔
63
                        ? json_decode( $input['extraData'], true )
×
64
                        : [];
6✔
65

66
                return apply_filters( 'graphql_woocommerce_new_cart_item_data', $cart_item_args, $input, $context, $info );
6✔
67
        }
68

69
        /**
70
         * Processes the provided variation attributes data for the cart.
71
         *
72
         * @param int   $product_id      Product ID.
73
         * @param array $variation_data  Variation data.
74
         *
75
         * @return array
76
         *
77
         * @throws \GraphQL\Error\UserError  Invalid cart attribute provided.
78
         */
79
        public static function prepare_attributes( $product_id, array $variation_data = [] ) {
80
                $product = wc_get_product( $product_id );
3✔
81

82
                // Bail if bad product ID.
83
                if ( ! $product ) {
3✔
84
                        throw new UserError(
×
85
                                sprintf(
×
86
                                        /* translators: %s: product ID */
87
                                        __( 'No product found matching the ID provided: %s', 'wp-graphql-woocommerce' ),
×
88
                                        $product_id
×
89
                                )
×
90
                        );
×
91
                }
92

93
                $attribute_names = array_keys( $product->get_attributes() );
3✔
94

95
                $attributes = [];
3✔
96
                foreach ( $variation_data as $attribute ) {
3✔
97
                        $attribute_name = $attribute['attributeName'];
3✔
98
                        if ( in_array( "pa_{$attribute_name}", $attribute_names, true ) ) {
3✔
99
                                $attribute_name = "pa_{$attribute_name}";
3✔
100
                        } elseif ( ! in_array( $attribute_name, $attribute_names, true ) ) {
2✔
101
                                throw new UserError(
×
102
                                        sprintf(
×
103
                                                /* translators: %1$s: attribute name, %2$s: product name */
104
                                                __( '%1$s is not a valid attribute of the product: %2$s.', 'wp-graphql-woocommerce' ),
×
105
                                                $attribute_name,
×
106
                                                $product->get_name()
×
107
                                        )
×
108
                                );
×
109
                        }
110

111
                        $attribute_value = ! empty( $attribute['attributeValue'] ) ? $attribute['attributeValue'] : '';
3✔
112
                        $attribute_key   = "attribute_{$attribute_name}";
3✔
113

114
                        $attributes[ $attribute_key ] = $attribute_value;
3✔
115
                }
116

117
                return $attributes;
3✔
118
        }
119

120
        /**
121
         * Returns an array of cart items.
122
         *
123
         * @param array                                $input    Input data describing cart items.
124
         * @param \WPGraphQL\AppContext                $context  AppContext instance.
125
         * @param \GraphQL\Type\Definition\ResolveInfo $info     Query info.
126
         * @param string                               $mutation Mutation type.
127
         *
128
         * @return array
129
         * @throws \GraphQL\Error\UserError Cart item not found message.
130
         */
131
        public static function retrieve_cart_items( $input, $context, $info, $mutation = '' ) {
132
                $items = null;
2✔
133
                // If "all" flag provided, retrieve all cart items.
134
                if ( ! empty( $input['all'] ) ) {
2✔
135
                        $items = array_values( \WC()->cart->get_cart() );
1✔
136
                }
137

138
                // If keys are provided and cart items haven't been retrieve yet,
139
                // retrieve the cart items by key.
140
                if ( ! empty( $input['keys'] ) && null === $items ) {
2✔
141
                        $items = [];
2✔
142
                        foreach ( $input['keys'] as $key ) {
2✔
143
                                $item = \WC()->cart->get_cart_item( $key );
2✔
144
                                if ( empty( $item ) ) {
2✔
145
                                        /* translators: Cart item not found message */
146
                                        throw new UserError( sprintf( __( 'No cart item found with the key: %s', 'wp-graphql-woocommerce' ), $key ) );
×
147
                                }
148
                                $items[] = $item;
2✔
149
                        }
150
                }
151

152
                return apply_filters( 'graphql_woocommerce_retrieve_cart_items', $items, $input, $context, $info, $mutation );
2✔
153
        }
154

155
        /**
156
         * Return array of data to be when defining a cart fee.
157
         *
158
         * @param array                                $input   input data describing cart item.
159
         * @param \WPGraphQL\AppContext                $context AppContext instance.
160
         * @param \GraphQL\Type\Definition\ResolveInfo $info    query info.
161
         *
162
         * @return array
163
         */
164
        public static function prepare_cart_fee( $input, $context, $info ) {
165
                $cart_item_args = [
1✔
166
                        $input['name'],
1✔
167
                        $input['amount'],
1✔
168
                        ! empty( $input['taxable'] ) ? $input['taxable'] : false,
1✔
169
                        ! empty( $input['taxClass'] ) ? $input['taxClass'] : '',
1✔
170
                ];
1✔
171

172
                return apply_filters( 'graphql_woocommerce_new_cart_fee_data', $cart_item_args, $input, $context, $info );
1✔
173
        }
174

175
        /**
176
         * Validates coupon and checks if application is possible
177
         *
178
         * @param string $code    Coupon code.
179
         * @param string $reason  Reason for failure.
180
         *
181
         * @return bool
182
         */
183
        public static function validate_coupon( $code, &$reason = '' ) {
184
                // Get the coupon.
185
                $the_coupon = new \WC_Coupon( $code );
3✔
186

187
                // Prevent adding coupons by post ID.
188
                if ( strtoupper( $the_coupon->get_code() ) !== strtoupper( $code ) ) {
3✔
189
                        $reason = __( 'No coupon found with the code provided', 'wp-graphql-woocommerce' );
×
190
                        return false;
×
191
                }
192

193
                // Check it can be used with cart.
194
                if ( ! $the_coupon->is_valid() ) {
3✔
195
                        $reason = $the_coupon->get_error_message();
2✔
196
                        return false;
2✔
197
                }
198

199
                // Check if applied.
200
                if ( \WC()->cart->has_discount( $code ) ) {
3✔
201
                        $reason = __( 'This coupon has already been applied to the cart', 'wp-graphql-woocommerce' );
1✔
202
                        return false;
1✔
203
                }
204

205
                return true;
2✔
206
        }
207

208
        /**
209
         * Validates shipping method by checking comparing against shipping package.
210
         *
211
         * @param string  $shipping_method  Shipping method being validated.
212
         * @param integer $index            Index of the shipping package.
213
         * @param string  $reason           Reason for failure.
214
         *
215
         * @return bool
216
         */
217
        public static function validate_shipping_method( $shipping_method, $index, &$reason = '' ) {
218
                // Get available shipping packages.
219
                $available_packages = \WC()->cart->needs_shipping()
1✔
220
                        ? \WC()->shipping()->calculate_shipping( \WC()->cart->get_shipping_packages() )
1✔
UNCOV
221
                        : [];
×
222

223
                if ( ! isset( $available_packages[ $index ] ) ) {
1✔
224
                        $reason = sprintf(
1✔
225
                                /* translators: %d: Package index */
226
                                __( 'No shipping packages available for corresponding index %d', 'wp-graphql-woocommerce' ),
1✔
227
                                $index
1✔
228
                        );
1✔
229

230
                        return false;
1✔
231
                }
232

233
                $package           = $available_packages[ $index ];
1✔
234
                $chosen_rate_index = array_search( $shipping_method, wp_list_pluck( $package['rates'], 'id' ), true );
1✔
235

236
                if ( false !== $chosen_rate_index ) {
1✔
237
                        return true;
1✔
238
                }
239

240
                $product_names = [];
×
241
                foreach ( $package['contents'] as $item_id => $values ) {
×
242
                        $product_names[ $item_id ] = \html_entity_decode( $values['data']->get_name() . ' &times;' . $values['quantity'] );
×
243
                }
244

245
                // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
246
                $product_names = apply_filters( 'woocommerce_shipping_package_details_array', $product_names, $package );
×
247

248
                $reason = sprintf(
×
249
                        /* translators: %1$s: shipping method ID, %2$s: package contents */
250
                        __( '"%1$s" is not an available shipping method for shipping package "%2$s"', 'wp-graphql-woocommerce' ),
×
251
                        $shipping_method,
×
252
                        implode( ', ', $product_names )
×
253
                );
×
254

255
                return false;
×
256
        }
257

258
        /**
259
         * Validates and prepares posted shipping methods for the user session.
260
         *
261
         * @param array $posted_shipping_methods  Chosen shipping methods.
262
         *
263
         * @throws \GraphQL\Error\UserError  Invalid shipping method.
264
         *
265
         * @return array<string,string>
266
         */
267
        public static function prepare_shipping_methods( $posted_shipping_methods ) {
268
                /**
269
                 * Get current shipping methods.
270
                 *
271
                 * @var array<string,string> $chosen_shipping_methods
272
                 */
273
                $chosen_shipping_methods = \WC()->session->get( 'chosen_shipping_methods' );
×
274

275
                // Update current shipping methods.
276
                foreach ( $posted_shipping_methods as $package => $chosen_method ) {
×
277
                        if ( empty( $chosen_method ) ) {
×
278
                                continue;
×
279
                        }
280

281
                        $reason = '';
×
282
                        if ( self::validate_shipping_method( $chosen_method, $package, $reason ) ) {
×
283
                                $chosen_shipping_methods[ $package ] = $chosen_method;
×
284
                        } else {
285
                                throw new UserError( $reason );
×
286
                        }
287
                }
288

289
                return $chosen_shipping_methods;
×
290
        }
291

292
        /**
293
         * Validate CartItemQuantityInput item.
294
         *
295
         * @param array $item  CartItemQuantityInput object.
296
         *
297
         * @return boolean
298
         */
299
        public static function item_is_valid( array $item ) {
300
                if ( empty( $item['key'] ) ) {
1✔
301
                        return false;
×
302
                }
303
                if ( ! isset( $item['quantity'] ) || ! is_numeric( $item['quantity'] ) ) {
1✔
304
                        return false;
×
305
                }
306
                return true;
1✔
307
        }
308

309
        /**
310
         * Checks for errors thrown by the QL_Session_Handler during session token validation.
311
         *
312
         * @throws \GraphQL\Error\UserError If GRAPHQL_DEBUG is set to true and errors found.
313
         *
314
         * @return void
315
         */
316
        public static function check_session_token() {
317
                $token_invalid = apply_filters( 'graphql_woocommerce_session_token_errors', null );
15✔
318
                if ( $token_invalid ) {
15✔
319
                        throw new UserError( $token_invalid );
×
320
                }
321

322
                \WC()->cart->get_cart_from_session();
15✔
323
        }
324
}
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