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

wp-graphql / wp-graphql-woocommerce / 23354837106

20 Mar 2026 05:31PM UTC coverage: 83.637% (+0.4%) from 83.244%
23354837106

push

github

web-flow
feat: Product and product attribute mutations (#851)

* devops: Product and product attribute mutation tests generated

* feat: Product Mutations implemented and tested

* chore: Linter and PHPStan compliance met

* fix: resolve test failures in product and variation mutation tests

- Removed duplicate requires for payment-method classes (rebase artifact)
- Wrapped bare `attributes` queries in `... on SimpleProduct` inline
  fragments (attributes field is on ProductWithAttributes interface,
  not the base Product type)
- Fixed attribute label expectations to match factory output (lowercase)
- Fixed relay ID prefix from 'product'/'product_variation' to 'post'
  to match WPGraphQL's actual encoding
- Cache WP_Post before force-deletion and re-cache after so the Product
  model's type resolver can still determine the GraphQL type in the
  response
- Added wp_cache_flush() before asserting product deletion to avoid
  false positives from the re-cached post

* chore: fix PHPCS lint errors in mutation files

* fix: resolve CI test failures for attribute and variation mutations

- Fixed ProductVariation type registration in schema filters so the
  variation mutation output field resolves correctly
- Renamed attribute slug from 'pattern' to 'fabric' in
  ProductAttributeMutationsTest to avoid collision with the pattern
  attribute created by ProductAttributeQueriesTest
- Fixed PHPStan errors for redundant is_wp_error checks
- Updated IntrospectionQueryTest

1251 of 1437 new or added lines in 23 files covered. (87.06%)

13995 of 16733 relevant lines covered (83.64%)

91.46 hits per line

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

98.21
/includes/mutation/class-product-attribute-create.php
1
<?php
2
/**
3
 * Mutation - createProductAttribute
4
 *
5
 * Registers mutation for creating a product attribute.
6
 *
7
 * @package WPGraphQL\WooCommerce\Mutation
8
 * @since TBD
9
 */
10

11
namespace WPGraphQL\WooCommerce\Mutation;
12

13
use GraphQL\Error\UserError;
14
use GraphQL\Type\Definition\ResolveInfo;
15
use WPGraphQL\AppContext;
16
use WPGraphQL\WooCommerce\Data\Mutation\Product_Mutation;
17

18
/**
19
 * Class Product_Attribute_Create
20
 */
21
class Product_Attribute_Create {
22
        /**
23
         * Registers mutation
24
         *
25
         * @return void
26
         */
27
        public static function register_mutation() {
28
                register_graphql_mutation(
182✔
29
                        'createProductAttribute',
182✔
30
                        [
182✔
31
                                'inputFields'         => self::get_input_fields(),
182✔
32
                                'outputFields'        => self::get_output_fields(),
182✔
33
                                'mutateAndGetPayload' => self::mutate_and_get_payload(),
182✔
34
                        ]
182✔
35
                );
182✔
36
        }
37

38
        /**
39
         * Defines the mutation input field configuration
40
         *
41
         * @return array
42
         */
43
        public static function get_input_fields() {
44
                return [
182✔
45
                        'name'        => [
182✔
46
                                'type'        => [ 'non_null' => 'String' ],
182✔
47
                                'description' => __( 'Name of the attribute.', 'wp-graphql-woocommerce' ),
182✔
48
                        ],
182✔
49
                        'slug'        => [
182✔
50
                                'type'        => 'String',
182✔
51
                                'description' => __( 'Slug of the attribute.', 'wp-graphql-woocommerce' ),
182✔
52
                        ],
182✔
53
                        'type'        => [
182✔
54
                                'type'        => 'String',
182✔
55
                                'description' => __( 'Type of the attribute.', 'wp-graphql-woocommerce' ),
182✔
56
                        ],
182✔
57
                        'orderBy'     => [
182✔
58
                                'type'        => 'String',
182✔
59
                                'description' => __( 'Order by which the attribute should be sorted.', 'wp-graphql-woocommerce' ),
182✔
60
                        ],
182✔
61
                        'hasArchives' => [
182✔
62
                                'type'        => 'Boolean',
182✔
63
                                'description' => __( 'Whether the attribute has archives.', 'wp-graphql-woocommerce' ),
182✔
64
                        ],
182✔
65
                ];
182✔
66
        }
67

68
        /**
69
         * Defines the mutation output field configuration
70
         *
71
         * @return array
72
         */
73
        public static function get_output_fields() {
74
                return [
182✔
75
                        'attribute' => [
182✔
76
                                'type'    => 'ProductAttributeObject',
182✔
77
                                'resolve' => static function ( $payload ) {
182✔
78
                                        return $payload['attribute'];
3✔
79
                                },
182✔
80
                        ],
182✔
81
                ];
182✔
82
        }
83

84
        /**
85
         * Defines the mutation data modification closure.
86
         *
87
         * @return callable
88
         */
89
        public static function mutate_and_get_payload() {
90
                return static function ( $input, AppContext $context, ResolveInfo $info ) {
182✔
91
                        if ( ! wc_rest_check_manager_permissions( 'attributes', 'create' ) ) {
3✔
92
                                throw new UserError( __( 'Sorry, you are not allowed to create attributes.', 'wp-graphql-woocommerce' ) );
1✔
93
                        }
94

95
                        $attribute_id = wc_create_attribute(
3✔
96
                                [
3✔
97
                                        'name'         => $input['name'],
3✔
98
                                        'slug'         => \wc_sanitize_taxonomy_name( stripslashes( $input['slug'] ) ),
3✔
99
                                        'type'         => ! empty( $input['type'] ) ? $input['type'] : 'select',
3✔
100
                                        'order_by'     => ! empty( $input['orderBy'] ) ? $input['orderBy'] : 'menu_order',
3✔
101
                                        'has_archives' => true === $input['hasArchives'],
3✔
102
                                ]
3✔
103
                        );
3✔
104

105
                        // Checks for errors.
106
                        if ( is_wp_error( $attribute_id ) ) {
3✔
NEW
107
                                throw new UserError( $attribute_id->get_error_message() );
×
108
                        }
109

110
                        $attribute = Product_Mutation::get_attribute( $attribute_id );
3✔
111

112
                        /**
113
                         * Fires after a single product attribute is created or updated via the REST API.
114
                         *
115
                         * @param object{'attribute_id': int} $attribute Inserted attribute object.
116
                         * @param array                       $input     Request object.
117
                         * @param boolean                     $creating  True when creating attribute, false when updating.
118
                         */
119
                        do_action( 'graphql_woocommerce_insert_product_attribute', $attribute, $input, true );
3✔
120

121
                        return [ 'attribute' => $attribute ];
3✔
122
                };
182✔
123
        }
124
}
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