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

wp-graphql / wp-graphql / 13316763745

13 Feb 2025 08:45PM UTC coverage: 82.712% (-0.3%) from 83.023%
13316763745

push

github

web-flow
Merge pull request #3307 from wp-graphql/release/v2.0.0

release: v2.0.0

195 of 270 new or added lines in 20 files covered. (72.22%)

180 existing lines in 42 files now uncovered.

13836 of 16728 relevant lines covered (82.71%)

299.8 hits per line

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

84.25
/src/Type/WPMutationType.php
1
<?php
2
namespace WPGraphQL\Type;
3

4
use GraphQL\Type\Definition\ResolveInfo;
5
use WPGraphQL\AppContext;
6
use WPGraphQL\Registry\TypeRegistry;
7

8
/**
9
 * Class WPMutationType
10
 *
11
 * @package WPGraphQL\Type
12
 */
13
class WPMutationType {
14
        /**
15
         * Configuration for how auth should be handled on the connection field
16
         *
17
         * @var array<string,mixed>
18
         */
19
        protected $auth;
20

21
        /**
22
         * The config for the connection
23
         *
24
         * @var array<string,mixed>
25
         */
26
        protected $config;
27

28
        /**
29
         * The name of the mutation field
30
         *
31
         * @var string
32
         */
33
        protected $mutation_name;
34

35
        /**
36
         * Whether the user must be authenticated to use the mutation.
37
         *
38
         * @var bool
39
         */
40
        protected $is_private;
41

42
        /**
43
         * The mutation input field config.
44
         *
45
         * @var array<string,array<string,mixed>>
46
         */
47
        protected $input_fields;
48

49
        /**
50
         * The mutation output field config.
51
         *
52
         * @var array<string,array<string,mixed>>
53
         */
54
        protected $output_fields;
55

56
        /**
57
         * The resolver function to resolve the mutation
58
         *
59
         * @var callable(mixed $root,array<string,mixed> $args,\WPGraphQL\AppContext $context,\GraphQL\Type\Definition\ResolveInfo $info): array<string,mixed>
60
         */
61
        protected $resolve_mutation;
62

63
        /**
64
         * The WPGraphQL TypeRegistry
65
         *
66
         * @var \WPGraphQL\Registry\TypeRegistry
67
         */
68
        protected $type_registry;
69

70
        /**
71
         * WPMutationType constructor.
72
         *
73
         * @param array<string,mixed>              $config        The config array for the mutation
74
         * @param \WPGraphQL\Registry\TypeRegistry $type_registry Instance of the WPGraphQL Type Registry
75
         *
76
         * @throws \Exception
77
         */
78
        public function __construct( array $config, TypeRegistry $type_registry ) {
593✔
79

80
                /**
81
                 * Filter the config of WPMutationType
82
                 *
83
                 * @param array<string,mixed>            $config           Array of configuration options passed to the WPMutationType when instantiating a new type
84
                 * @param \WPGraphQL\Type\WPMutationType $wp_mutation_type The instance of the WPMutationType class
85
                 *
86
                 * @since 1.13.0
87
                 */
88
                $config = apply_filters( 'graphql_wp_mutation_type_config', $config, $this );
593✔
89

90
                if ( ! $this->is_config_valid( $config ) ) {
593✔
91
                        return;
×
92
                }
93

94
                $this->config        = $config;
593✔
95
                $this->type_registry = $type_registry;
593✔
96
                $this->mutation_name = $config['name'];
593✔
97

98
                // Bail if the mutation should be excluded from the schema.
99
                if ( ! $this->should_register() ) {
593✔
100
                        return;
×
101
                }
102

103
                $this->auth             = array_key_exists( 'auth', $config ) && is_array( $config['auth'] ) ? $config['auth'] : [];
593✔
104
                $this->is_private       = array_key_exists( 'isPrivate', $config ) ? $config['isPrivate'] : false;
593✔
105
                $this->input_fields     = $this->get_input_fields();
593✔
106
                $this->output_fields    = $this->get_output_fields();
593✔
107
                $this->resolve_mutation = $this->get_resolver();
593✔
108

109
                /**
110
                 * Run an action when the WPMutationType is instantiating.
111
                 *
112
                 * @param array<string,mixed>            $config           Array of configuration options passed to the WPObjectType when instantiating a new type
113
                 * @param \WPGraphQL\Type\WPMutationType $wp_mutation_type The instance of the WPMutationType class
114
                 *
115
                 * @since 1.13.0
116
                 */
117
                do_action( 'graphql_wp_mutation_type', $config, $this );
593✔
118

119
                $this->register_mutation();
593✔
120
        }
121

122
        /**
123
         * Validates that essential key/value pairs are passed to the connection config.
124
         *
125
         * @param array<string,mixed> $config The config array for the mutation
126
         */
127
        protected function is_config_valid( array $config ): bool {
593✔
128
                $is_valid = true;
593✔
129

130
                if ( ! array_key_exists( 'name', $config ) || ! is_string( $config['name'] ) ) {
593✔
131
                        graphql_debug(
×
132
                                __( 'Mutation config needs to have a valid name.', 'wp-graphql' ),
×
133
                                [
×
134
                                        'config' => $config,
×
135
                                ]
×
136
                        );
×
137
                        $is_valid = false;
×
138
                }
139

140
                if ( ! array_key_exists( 'mutateAndGetPayload', $config ) || ! is_callable( $config['mutateAndGetPayload'] ) ) {
593✔
141
                        graphql_debug(
×
142
                                __( 'Mutation config needs to have "mutateAndGetPayload" defined as a callable.', 'wp-graphql' ),
×
143
                                [
×
144
                                        'config' => $config,
×
145
                                ]
×
146
                        );
×
147
                        $is_valid = false;
×
148
                }
149

150
                return $is_valid;
593✔
151
        }
152

153
        /**
154
         * Gets the mutation input fields.
155
         *
156
         * @return array<string,array<string,mixed>>
157
         */
158
        protected function get_input_fields(): array {
593✔
159
                $input_fields = [
593✔
160
                        'clientMutationId' => [
593✔
161
                                'type'        => 'String',
593✔
162
                                'description' => __( 'This is an ID that can be passed to a mutation by the client to track the progress of mutations and catch possible duplicate mutation submissions.', 'wp-graphql' ),
593✔
163
                        ],
593✔
164
                ];
593✔
165

166
                if ( ! empty( $this->config['inputFields'] ) && is_array( $this->config['inputFields'] ) ) {
593✔
167
                        $input_fields = array_merge( $input_fields, $this->config['inputFields'] );
593✔
168
                }
169

170
                return $input_fields;
593✔
171
        }
172

173
        /**
174
         * Gets the mutation output fields.
175
         *
176
         * @return array<string,array<string,mixed>>
177
         */
178
        protected function get_output_fields(): array {
593✔
179
                $output_fields = [
593✔
180
                        'clientMutationId' => [
593✔
181
                                'type'        => 'String',
593✔
182
                                'description' => __( 'If a \'clientMutationId\' input is provided to the mutation, it will be returned as output on the mutation. This ID can be used by the client to track the progress of mutations and catch possible duplicate mutation submissions.', 'wp-graphql' ),
593✔
183
                        ],
593✔
184
                ];
593✔
185

186
                if ( ! empty( $this->config['outputFields'] ) && is_array( $this->config['outputFields'] ) ) {
593✔
187
                        $output_fields = array_merge( $output_fields, $this->config['outputFields'] );
593✔
188
                }
189

190
                return $output_fields;
593✔
191
        }
192

193
        /**
194
         * Gets the resolver callable for the mutation.
195
         *
196
         * @return callable(mixed $root,array<string,mixed> $args,\WPGraphQL\AppContext $context,\GraphQL\Type\Definition\ResolveInfo $info): array<string,mixed>
197
         */
198
        protected function get_resolver(): callable {
593✔
199
                return function ( $root, array $args, AppContext $context, ResolveInfo $info ) {
593✔
200
                        $unfiltered_input = $args['input'];
107✔
201

202
                        $unfiltered_input = $args['input'];
107✔
203

204
                        /**
205
                         * Filters the mutation input before it's passed to the `mutateAndGetPayload` callback.
206
                         *
207
                         * @param array<string,mixed>                  $input         The mutation input args.
208
                         * @param \WPGraphQL\AppContext                $context       The AppContext object.
209
                         * @param \GraphQL\Type\Definition\ResolveInfo $info          The ResolveInfo object.
210
                         * @param string                               $mutation_name The name of the mutation field.
211
                         */
212
                        $input = apply_filters( 'graphql_mutation_input', $unfiltered_input, $context, $info, $this->mutation_name );
107✔
213

214
                        /**
215
                         * Filter to short circuit the mutateAndGetPayload callback.
216
                         * Returning anything other than null will stop the callback for the mutation from executing,
217
                         * and will return your data or execute your callback instead.
218
                         *
219
                         * @param array<string,mixed>|callable|null   $payload.            The payload returned from the callback. Null by default.
220
                         * @param string                $mutation_name       The name of the mutation field.
221
                         * @param callable|\Closure     $mutateAndGetPayload The callback for the mutation.
222
                         * @param array<string,mixed>   $input               The mutation input args.
223
                         * @param \WPGraphQL\AppContext $context             The AppContext object.
224
                         * @param \GraphQL\Type\Definition\ResolveInfo $info The ResolveInfo object.
225
                         */
226
                        $pre = apply_filters( 'graphql_pre_mutate_and_get_payload', null, $this->mutation_name, $this->config['mutateAndGetPayload'], $input, $context, $info );
107✔
227

228
                        if ( ! is_null( $pre ) ) {
107✔
229
                                $payload = is_callable( $pre ) ? $pre( $input, $context, $info ) : $pre;
×
230
                        } else {
231
                                $payload = $this->config['mutateAndGetPayload']( $input, $context, $info );
107✔
232

233
                                /**
234
                                 * Filters the payload returned from the default mutateAndGetPayload callback.
235
                                 *
236
                                 * @param array<string,mixed>   $payload The payload returned from the callback.
237
                                 * @param string                $mutation_name The name of the mutation field.
238
                                 * @param array<string,mixed>   $input The mutation input args.
239
                                 * @param \WPGraphQL\AppContext $context The AppContext object.
240
                                 * @param \GraphQL\Type\Definition\ResolveInfo $info The ResolveInfo object.
241
                                 */
242
                                $payload = apply_filters( 'graphql_mutation_payload', $payload, $this->mutation_name, $input, $context, $info );
68✔
243
                        }
244

245
                        /**
246
                         * Fires after the mutation payload has been returned from the `mutateAndGetPayload` callback.
247
                         *
248
                         * @param array<string,mixed>                  $payload          The Payload returned from the mutation.
249
                         * @param array<string,mixed>                  $input            The mutation input args, after being filtered by 'graphql_mutation_input'.
250
                         * @param array<string,mixed>                  $unfiltered_input The unfiltered input args of the mutation
251
                         * @param \WPGraphQL\AppContext                $context          The AppContext object.
252
                         * @param \GraphQL\Type\Definition\ResolveInfo $info             The ResolveInfo object.
253
                         * @param string                               $mutation_name    The name of the mutation field.
254
                         */
255
                        do_action( 'graphql_mutation_response', $payload, $input, $unfiltered_input, $context, $info, $this->mutation_name );
68✔
256

257
                        // Add the client mutation ID to the payload
258
                        if ( ! empty( $input['clientMutationId'] ) ) {
68✔
259
                                $payload['clientMutationId'] = $input['clientMutationId'];
10✔
260
                        }
261

262
                        return $payload;
68✔
263
                };
593✔
264
        }
265

266
        /**
267
         * Registers the input args for the mutation.
268
         */
269
        protected function register_mutation_input(): void {
593✔
270
                $input_name = $this->mutation_name . 'Input';
593✔
271

272
                if ( $this->type_registry->has_type( $input_name ) ) {
593✔
UNCOV
273
                        return;
×
274
                }
275

276
                $this->type_registry->register_input_type(
593✔
277
                        $input_name,
593✔
278
                        [
593✔
279
                                // translators: %s is the name of the mutation.
280
                                'description'       => sprintf( __( 'Input for the %1$s mutation.', 'wp-graphql' ), $this->mutation_name ),
593✔
281
                                'fields'            => $this->input_fields,
593✔
282
                                'deprecationReason' => ! empty( $this->config['deprecationReason'] ) ? $this->config['deprecationReason'] : null,
593✔
283
                        ]
593✔
284
                );
593✔
285
        }
286

287
        /**
288
         * Registers the payload type to the Schema.
289
         */
290
        protected function register_mutation_payload(): void {
593✔
291
                $object_name = $this->mutation_name . 'Payload';
593✔
292

293
                if ( $this->type_registry->has_type( $object_name ) ) {
593✔
UNCOV
294
                        return;
×
295
                }
296

297
                $this->type_registry->register_object_type(
593✔
298
                        $object_name,
593✔
299
                        [
593✔
300
                                // translators: %s is the name of the mutation.
301
                                'description'       => sprintf( __( 'The payload for the %s mutation.', 'wp-graphql' ), $this->mutation_name ),
593✔
302
                                'fields'            => $this->output_fields,
593✔
303
                                'deprecationReason' => ! empty( $this->config['deprecationReason'] ) ? $this->config['deprecationReason'] : null,
593✔
304
                        ]
593✔
305
                );
593✔
306
        }
307

308
        /**
309
         * Registers the mutation in the Graph.
310
         *
311
         * @throws \Exception
312
         */
313
        protected function register_mutation_field(): void {
593✔
314
                $field_config = array_merge(
593✔
315
                        $this->config,
593✔
316
                        [
593✔
317
                                'args'        => [
593✔
318
                                        'input' => [
593✔
319
                                                'type'              => [ 'non_null' => $this->mutation_name . 'Input' ],
593✔
320
                                                // translators: %s is the name of the mutation.
321
                                                'description'       => sprintf( __( 'Input for the %s mutation', 'wp-graphql' ), $this->mutation_name ),
593✔
322
                                                'deprecationReason' => ! empty( $this->config['deprecationReason'] ) ? $this->config['deprecationReason'] : null,
593✔
323
                                        ],
593✔
324
                                ],
593✔
325
                                'auth'        => $this->auth,
593✔
326
                                // translators: %s is the name of the mutation.
327
                                'description' => ! empty( $this->config['description'] ) ? $this->config['description'] : sprintf( __( 'The %s mutation', 'wp-graphql' ), $this->mutation_name ),
593✔
328
                                'isPrivate'   => $this->is_private,
593✔
329
                                'type'        => $this->mutation_name . 'Payload',
593✔
330
                                'resolve'     => $this->resolve_mutation,
593✔
331
                                'name'        => lcfirst( $this->mutation_name ),
593✔
332
                        ]
593✔
333
                );
593✔
334

335
                $this->type_registry->register_field(
593✔
336
                        'RootMutation',
593✔
337
                        lcfirst( $this->mutation_name ),
593✔
338
                        $field_config
593✔
339
                );
593✔
340
        }
341

342
        /**
343
         * Registers the Mutation Types and field to the Schema.
344
         *
345
         * @throws \Exception
346
         */
347
        protected function register_mutation(): void {
593✔
348
                $this->register_mutation_payload();
593✔
349
                $this->register_mutation_input();
593✔
350
                $this->register_mutation_field();
593✔
351
        }
352

353
        /**
354
         * Checks whether the mutation should be registered to the schema.
355
         */
356
        protected function should_register(): bool {
593✔
357
                // Dont register mutations if they have been excluded from the schema.
358
                $excluded_mutations = $this->type_registry->get_excluded_mutations();
593✔
359
                if ( in_array( strtolower( $this->mutation_name ), $excluded_mutations, true ) ) {
593✔
360
                        return false;
×
361
                }
362

363
                return true;
593✔
364
        }
365
}
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