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

wp-graphql / wp-graphql / 17562196906

08 Sep 2025 07:44PM UTC coverage: 84.575% (+0.4%) from 84.17%
17562196906

push

github

web-flow
Merge pull request #3389 from wp-graphql/develop

release: next version 📦

238 of 308 new or added lines in 13 files covered. (77.27%)

6 existing lines in 6 files now uncovered.

15884 of 18781 relevant lines covered (84.57%)

261.69 hits per line

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

87.27
/src/Deprecated.php
1
<?php
2
/**
3
 * Class for handling deprecated functionality.
4
 *
5
 * Entirely deprecated classes can be relocated to the `deprecated/` directory, but things still need to be hooked into WordPress.
6
 *
7
 * @package  WPGraphQL
8
 */
9

10
namespace WPGraphQL;
11

12
use GraphQL\Error\UserError;
13
use GraphQLRelay\Relay;
14
use WPGraphQL\Data\Connection\PostObjectConnectionResolver;
15
use WPGraphQL\Model\Post;
16
use WPGraphQL\Type\Union\MenuItemObjectUnion;
17
use WPGraphQL\Type\Union\PostObjectUnion;
18
use WPGraphQL\Type\Union\TermObjectUnion;
19
use WPGraphQL\Type\WPObjectType;
20

21
/**
22
 * Class - Deprecated
23
 */
24
final class Deprecated {
25
        /**
26
         * The class constructor.
27
         */
28
        public function __construct() {}
29

30
        /**
31
         * Register the deprecated functionality.
32
         */
NEW
33
        public function register(): void {
×
NEW
34
                $this->filters();
×
35
                // We want to defer the action until after the schema is registered.
NEW
36
                add_action( 'graphql_register_types', [ $this, 'register_deprecated_types' ] );
×
37
        }
38

39
        /**
40
         * Handles deprecated filters.
41
         */
42
        private function filters(): void {
629✔
43
                /**
44
                 * The `graphql_object_type_interfaces` filter
45
                 *
46
                 * @deprecated 1.4.1
47
                 * @todo Remove in 3.0.0
48
                 */
NEW
49
                add_filter(
×
NEW
50
                        'graphql_type_interfaces',
×
NEW
51
                        static function ( $interfaces, $config, $type ) {
×
52
                                if ( ! $type instanceof WPObjectType || ! has_filter( 'graphql_object_type_interfaces' ) ) {
629✔
53
                                        return $interfaces;
629✔
54
                                }
55

56
                                /**
57
                                 * @deprecated
58
                                 *
59
                                 * @param string[]                                                     $interfaces List of interfaces applied to the Object Type
60
                                 * @param array<string,mixed>                                          $config     The config for the Object Type
61
                                 * @param \WPGraphQL\Type\WPInterfaceType|\WPGraphQL\Type\WPObjectType $type       The Type instance
62
                                 */
NEW
63
                                return apply_filters_deprecated( 'graphql_object_type_interfaces', [ $interfaces, $config, $type ], '1.4.1', 'graphql_type_interfaces', __( 'This will be removed in the next major release of WPGraphQL.', 'wp-graphql' ) );
×
NEW
64
                        },
×
NEW
65
                        10,
×
NEW
66
                        3
×
NEW
67
                );
×
68

69
                /**
70
                 * The `graphql_return_modeled_data` filter.
71
                 *
72
                 * @deprecated 1.7.0
73
                 * @todo Remove in 3.0.0
74
                 */
NEW
75
                add_filter(
×
NEW
76
                        'graphql_model_prepare_fields',
×
NEW
77
                        static function ( $fields, $model_name, $data, $visibility, $owner, $current_user ) {
×
78
                                if ( ! has_filter( 'graphql_return_modeled_data' ) ) {
513✔
79
                                        return $fields;
513✔
80
                                }
81

82
                                /**
83
                                 * @param array<string,mixed>    $fields       The array of fields for the model
84
                                 * @param string                 $model_name   Name of the model the filter is currently being executed in
85
                                 * @param string                 $visibility   The visibility setting for this piece of data
86
                                 * @param ?int                   $owner        The user ID for the owner of this piece of data
87
                                 * @param \WP_User               $current_user The current user for the session
88
                                 *
89
                                 * @deprecated 1.7.0 use "graphql_model_prepare_fields" filter instead, which passes additional context to the filter
90
                                 */
NEW
91
                                return apply_filters_deprecated(
×
NEW
92
                                        'graphql_return_modeled_data',
×
NEW
93
                                        [ $fields, $model_name, $visibility, $owner, $current_user ],
×
NEW
94
                                        '1.7.0',
×
NEW
95
                                        'graphql_model_prepare_fields',
×
NEW
96
                                        __( 'This will be removed in the next major release of WPGraphQL.', 'wp-graphql' )
×
NEW
97
                                );
×
NEW
98
                        },
×
NEW
99
                        10,
×
NEW
100
                        6
×
NEW
101
                );
×
102
        }
103

104
        /**
105
         * Registers deprecated graphql types.
106
         */
107
        public function register_deprecated_types(): void {
602✔
108
                MenuItemObjectUnion::register_type(); /* @phpstan-ignore staticMethod.deprecatedClass */
602✔
109
                PostObjectUnion::register_type(); /* @phpstan-ignore staticMethod.deprecatedClass */
602✔
110
                TermObjectUnion::register_type(); /* @phpstan-ignore staticMethod.deprecatedClass */
602✔
111

112
                $this->graphql_post_types();
602✔
113
                $this->menu_item_connected_object();
602✔
114
                $this->send_password_reset_email_user();
602✔
115
        }
116

117
        /**
118
         * The `MenuItem` connectedObject field.
119
         *
120
         * @todo remove in 3.0.0
121
         */
122
        private function menu_item_connected_object(): void {
602✔
123
                register_graphql_field(
602✔
124
                        'MenuItem',
602✔
125
                        'connectedObject',
602✔
126
                        [
602✔
127
                                'type'              => 'MenuItemObjectUnion',
602✔
128
                                'deprecationReason' => static function () {
602✔
129
                                        return __( 'Deprecated in favor of the connectedNode field', 'wp-graphql' );
17✔
130
                                },
602✔
131
                                'description'       => static function () {
602✔
132
                                        return __( 'The object connected to this menu item.', 'wp-graphql' );
17✔
133
                                },
602✔
134
                                'resolve'           => static function ( $menu_item, array $args, AppContext $context, $info ) {
602✔
135
                                        $object_id   = intval( get_post_meta( $menu_item->menuItemId, '_menu_item_object_id', true ) );
14✔
136
                                        $object_type = get_post_meta( $menu_item->menuItemId, '_menu_item_type', true );
14✔
137

138
                                        switch ( $object_type ) {
139
                                                // Post object
140
                                                case 'post_type':
14✔
141
                                                        $resolved_object = $context->get_loader( 'post' )->load_deferred( $object_id );
14✔
142
                                                        break;
14✔
143

144
                                                // Taxonomy term
NEW
145
                                                case 'taxonomy':
×
NEW
146
                                                        $resolved_object = $context->get_loader( 'term' )->load_deferred( $object_id );
×
NEW
147
                                                        break;
×
148
                                                default:
NEW
149
                                                        $resolved_object = null;
×
NEW
150
                                                        break;
×
151
                                        }
152

153
                                        /**
154
                                         * @todo Remove in 3.0.0.
155
                                         *
156
                                         * @param \WP_Post|\WP_Term                    $resolved_object Post or term connected to MenuItem
157
                                         * @param array<string,mixed>                  $args            Array of arguments input in the field as part of the GraphQL query
158
                                         * @param \WPGraphQL\AppContext                $context         Object containing app context that gets passed down the resolve tree
159
                                         * @param \GraphQL\Type\Definition\ResolveInfo $info            Info about fields passed down the resolve tree
160
                                         * @param int                                  $object_id       Post or term ID of connected object
161
                                         * @param string                               $object_type     Type of connected object ("post_type" or "taxonomy")
162
                                         *
163
                                         * @since 0.0.30
164
                                         */
165
                                        return apply_filters_deprecated(
14✔
166
                                                'graphql_resolve_menu_item',
14✔
167
                                                [
14✔
168
                                                        $resolved_object,
14✔
169
                                                        $args,
14✔
170
                                                        $context,
14✔
171
                                                        $info,
14✔
172
                                                        $object_id,
14✔
173
                                                        $object_type,
14✔
174
                                                ],
14✔
175
                                                '1.22.0',
14✔
176
                                                'graphql_pre_resolve_menu_item_connected_node',
14✔
177
                                                __( 'This will be removed in the next version of WPGraphQL. Use the `graphql_pre_resolve_menu_item_connected_node` filter on `connectedNode` instead.', 'wp-graphql' )
14✔
178
                                        );
14✔
179
                                },
602✔
180
                        ],
602✔
181
                );
602✔
182
        }
183

184
        /**
185
         * Registers deprecated Post Type data to the schema
186
         */
187
        private function graphql_post_types(): void {
602✔
188
                $allowed_post_types = \WPGraphQL::get_allowed_post_types( 'objects', [ 'graphql_register_root_field' => true ] );
602✔
189

190
                foreach ( $allowed_post_types as $post_type_object ) {
602✔
191
                        $this->post_type_by_field( $post_type_object );
602✔
192
                        $this->register_deprecated_post_type_parents( $post_type_object );
602✔
193
                        $this->register_deprecated_post_type_previews( $post_type_object );
602✔
194
                }
195
        }
196

197
        /**
198
         * Register deprecated {PostType}By fields
199
         *
200
         * @todo remove in 3.0.0
201
         *
202
         * @param \WP_Post_Type $post_type_object The post type object to register the field for.
203
         */
204
        private function post_type_by_field( $post_type_object ): void {
603✔
205
                $post_by_args = [
602✔
206
                        'id'                                          => [
602✔
207
                                'type'        => 'ID',
602✔
208
                                'description' => static function () use ( $post_type_object ) {
602✔
209
                                        return sprintf(
69✔
210
                                                // translators: %s is the post type's GraphQL name.
211
                                                __( 'Get the %s object by its global ID', 'wp-graphql' ),
69✔
212
                                                $post_type_object->graphql_single_name
69✔
213
                                        );
69✔
214
                                },
602✔
215
                        ],
602✔
216
                        $post_type_object->graphql_single_name . 'Id' => [
602✔
217
                                'type'        => 'Int',
602✔
218
                                'description' => static function () use ( $post_type_object ) {
602✔
219
                                        return sprintf(
69✔
220
                                                // translators: %s is the post type's GraphQL name.
221
                                                __( 'Get the %s by its database ID', 'wp-graphql' ),
69✔
222
                                                $post_type_object->graphql_single_name
69✔
223
                                        );
69✔
224
                                },
602✔
225
                        ],
602✔
226
                        'uri'                                         => [
602✔
227
                                'type'        => 'String',
602✔
228
                                'description' => static function () use ( $post_type_object ) {
602✔
229
                                        return sprintf(
69✔
230
                                                // translators: %s is the post type's GraphQL name.
231
                                                __( 'Get the %s by its uri', 'wp-graphql' ),
69✔
232
                                                $post_type_object->graphql_single_name
69✔
233
                                        );
69✔
234
                                },
602✔
235
                        ],
602✔
236
                ];
602✔
237

238
                if ( false === $post_type_object->hierarchical ) {
602✔
239
                        $post_by_args['slug'] = [
602✔
240
                                'type'        => 'String',
602✔
241
                                'description' => static function () use ( $post_type_object ) {
602✔
242
                                        return sprintf(
69✔
243
                                                // translators: %s is the post type's GraphQL name.
244
                                                __( 'Get the %s by its slug (only available for non-hierarchical types)', 'wp-graphql' ),
69✔
245
                                                $post_type_object->graphql_single_name
69✔
246
                                        );
69✔
247
                                },
602✔
248
                        ];
602✔
249
                }
250

251
                /**
252
                 * @deprecated Deprecated in favor of single node entry points
253
                 */
254
                register_graphql_field(
603✔
255
                        'RootQuery',
603✔
256
                        $post_type_object->graphql_single_name . 'By',
603✔
257
                        [
603✔
258
                                'type'              => $post_type_object->graphql_single_name,
603✔
259
                                'deprecationReason' => static function () {
603✔
260
                                        return __( 'Deprecated in favor of using the single entry point for this type with ID and IDType fields. For example, instead of postBy( id: "" ), use post(id: "" idType: "")', 'wp-graphql' );
69✔
261
                                },
603✔
262
                                'description'       => static function () use ( $post_type_object ) {
603✔
263
                                        return sprintf(
69✔
264
                                                // translators: %s is the post type's GraphQL name.
265
                                                __( 'A %s object', 'wp-graphql' ),
69✔
266
                                                $post_type_object->graphql_single_name
69✔
267
                                        );
69✔
268
                                },
603✔
269
                                'args'              => $post_by_args,
603✔
270
                                'resolve'           => static function ( $source, array $args, $context ) use ( $post_type_object ) {
603✔
271
                                        $post_id = 0;
15✔
272

273
                                        if ( ! empty( $args['id'] ) ) {
15✔
274
                                                $id_components = Relay::fromGlobalId( $args['id'] );
8✔
275
                                                if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
8✔
276
                                                        throw new UserError( esc_html__( 'The "id" is invalid', 'wp-graphql' ) );
1✔
277
                                                }
278
                                                $post_id = absint( $id_components['id'] );
7✔
279
                                        } elseif ( ! empty( $args[ lcfirst( $post_type_object->graphql_single_name . 'Id' ) ] ) ) {
10✔
280
                                                $id      = $args[ lcfirst( $post_type_object->graphql_single_name . 'Id' ) ];
8✔
281
                                                $post_id = absint( $id );
8✔
282
                                        } elseif ( ! empty( $args['uri'] ) ) {
5✔
283
                                                return $context->node_resolver->resolve_uri(
4✔
284
                                                        $args['uri'],
4✔
285
                                                        [
4✔
286
                                                                'post_type' => $post_type_object->name,
4✔
287
                                                                'archive'   => false,
4✔
288
                                                                'nodeType'  => 'ContentNode',
4✔
289
                                                        ]
4✔
290
                                                );
4✔
291
                                        } elseif ( ! empty( $args['slug'] ) ) {
2✔
292
                                                $slug = esc_html( $args['slug'] );
2✔
293

294
                                                return $context->node_resolver->resolve_uri(
2✔
295
                                                        $slug,
2✔
296
                                                        [
2✔
297
                                                                'name'      => $slug,
2✔
298
                                                                'post_type' => $post_type_object->name,
2✔
299
                                                                'nodeType'  => 'ContentNode',
2✔
300
                                                        ]
2✔
301
                                                );
2✔
302
                                        }
303

304
                                        return $context->get_loader( 'post' )->load_deferred( $post_id )->then(
12✔
305
                                                static function ( $post ) use ( $post_type_object ) {
12✔
306

307
                                                        // if the post type object isn't an instance of WP_Post_Type, return
308
                                                        if ( ! $post_type_object instanceof \WP_Post_Type ) {
12✔
NEW
309
                                                                return null;
×
310
                                                        }
311

312
                                                        // if the post isn't an instance of a Post model, return
313
                                                        if ( ! $post instanceof Post ) {
12✔
314
                                                                return null;
3✔
315
                                                        }
316

317
                                                        if ( ! isset( $post->post_type ) || ! in_array(
10✔
318
                                                                $post->post_type,
10✔
319
                                                                [
10✔
320
                                                                        'revision',
10✔
321
                                                                        $post_type_object->name,
10✔
322
                                                                ],
10✔
323
                                                                true
10✔
324
                                                        ) ) {
10✔
325
                                                                return null;
1✔
326
                                                        }
327

328
                                                        return $post;
9✔
329
                                                }
12✔
330
                                        );
12✔
331
                                },
603✔
332
                        ]
603✔
333
                );
603✔
334
        }
335

336
        /**
337
         * Register deprecated Post Type connections.
338
         *
339
         * @todo remove in 3.0.0
340
         *
341
         * @param \WP_Post_Type $post_type_object The post type object to register the connection for.
342
         */
343
        private function register_deprecated_post_type_parents( \WP_Post_Type $post_type_object ): void {
602✔
344
                if ( $post_type_object->hierarchical || in_array( $post_type_object->name, [ 'attachment', 'revision' ], true ) ) {
602✔
345
                        return;
601✔
346
                }
347

348
                // Ancestors
349
                register_graphql_connection(
602✔
350
                        [
602✔
351
                                'fromType'          => $post_type_object->graphql_single_name,
602✔
352
                                'toType'            => $post_type_object->graphql_single_name,
602✔
353
                                'fromFieldName'     => 'ancestors',
602✔
354
                                'description'       => static function () {
602✔
355
                                                return __( 'The ancestors of the content node.', 'wp-graphql' );
19✔
356
                                },
602✔
357
                                'deprecationReason' => static function () {
602✔
358
                                        return __( 'This content type is not hierarchical and typically will not have ancestors', 'wp-graphql' );
19✔
359
                                },
602✔
360
                                'resolve'           => static function () {
602✔
NEW
361
                                        return null;
×
362
                                },
602✔
363
                        ]
602✔
364
                );
602✔
365

366
                // Parent
367
                register_graphql_connection(
602✔
368
                        [
602✔
369
                                'fromType'          => $post_type_object->graphql_single_name,
602✔
370
                                'toType'            => $post_type_object->graphql_single_name,
602✔
371
                                'fromFieldName'     => 'parent',
602✔
372
                                'oneToOne'          => true,
602✔
373
                                'description'       => static function () {
602✔
374
                                        return __( 'The parent of the content node.', 'wp-graphql' );
19✔
375
                                },
602✔
376
                                'deprecationReason' => static function () {
602✔
377
                                        return __( 'This content type is not hierarchical and typically will not have a parent', 'wp-graphql' );
19✔
378
                                },
602✔
379
                                'resolve'           => static function () {
602✔
NEW
380
                                        return null;
×
381
                                },
602✔
382
                        ]
602✔
383
                );
602✔
384
        }
385

386
        /**
387
         * Register deprecated Post Type previews.
388
         *
389
         * @todo remove in 3.0.0
390
         * @param \WP_Post_Type $post_type_object The post type object to register the preview for.
391
         */
392
        private function register_deprecated_post_type_previews( \WP_Post_Type $post_type_object ): void {
602✔
393
                if ( in_array( $post_type_object->name, [ 'attachment', 'revision' ], true ) ) {
602✔
394
                        return;
601✔
395
                }
396

397
                register_graphql_connection(
602✔
398
                        [
602✔
399
                                'fromType'           => $post_type_object->graphql_single_name,
602✔
400
                                'toType'             => $post_type_object->graphql_single_name,
602✔
401
                                'fromFieldName'      => 'preview',
602✔
402
                                'connectionTypeName' => ucfirst( $post_type_object->graphql_single_name ) . 'ToPreviewConnection',
602✔
403
                                'oneToOne'           => true,
602✔
404
                                'deprecationReason'  => ( true === $post_type_object->publicly_queryable || true === $post_type_object->public ) ? null
602✔
405
                                        : sprintf(
602✔
406
                                                // translators: %s is the post type's GraphQL name.
407
                                                __( 'The "%s" Type is not publicly queryable and does not support previews. This field will be removed in the future.', 'wp-graphql' ),
602✔
408
                                                \WPGraphQL\Utils\Utils::format_type_name( $post_type_object->graphql_single_name )
602✔
409
                                        ),
602✔
410
                                'resolve'            => static function ( Post $post, $args, $context, $info ) {
602✔
411
                                        if ( $post->isRevision ) {
10✔
NEW
412
                                                return null;
×
413
                                        }
414

415
                                        if ( empty( $post->previewRevisionDatabaseId ) ) {
10✔
416
                                                return null;
1✔
417
                                        }
418

419
                                        $resolver = new PostObjectConnectionResolver( $post, $args, $context, $info, 'revision' );
9✔
420
                                        $resolver->set_query_arg( 'p', $post->previewRevisionDatabaseId );
9✔
421

422
                                        return $resolver->one_to_one()->get_connection();
9✔
423
                                },
602✔
424
                        ]
602✔
425
                );
602✔
426
        }
427

428
        /**
429
         * SendPasswordResetEmail.user output field\
430
         *
431
         * @todo remove in 3.0.0
432
         */
433
        public function send_password_reset_email_user(): void {
602✔
434
                register_graphql_field(
602✔
435
                        'SendPasswordResetEmailPayload',
602✔
436
                        'user',
602✔
437
                        [
602✔
438
                                'type'              => 'User',
602✔
439
                                'description'       => static function () {
602✔
440
                                        return __( 'The user that the password reset email was sent to', 'wp-graphql' );
15✔
441
                                },
602✔
442
                                'deprecationReason' => static function () {
602✔
443
                                        return __( 'This field will be removed in a future version of WPGraphQL', 'wp-graphql' );
15✔
444
                                },
602✔
445
                                'resolve'           => static function ( $payload, $args, AppContext $context ) {
602✔
446
                                        return ! empty( $payload['id'] ) ? $context->get_loader( 'user' )->load_deferred( $payload['id'] ) : null;
7✔
447
                                },
602✔
448
                        ],
602✔
449
                );
602✔
450
        }
451
}
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