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

wp-graphql / wp-graphql / 14605898576

22 Apr 2025 10:35PM UTC coverage: 82.671% (+0.06%) from 82.613%
14605898576

push

github

web-flow
Merge pull request #3363 from justlevine/fix/cleanup-model-resolvers

fix: cleanup Model fields for better source-of-truth and type-safety.

555 of 599 new or added lines in 27 files covered. (92.65%)

2 existing lines in 2 files now uncovered.

13916 of 16833 relevant lines covered (82.67%)

300.94 hits per line

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

96.16
/src/Registry/Utils/PostObject.php
1
<?php
2

3
namespace WPGraphQL\Registry\Utils;
4

5
use GraphQL\Type\Definition\ResolveInfo;
6
use WPGraphQL;
7
use WPGraphQL\AppContext;
8
use WPGraphQL\Data\Connection\CommentConnectionResolver;
9
use WPGraphQL\Data\Connection\PostObjectConnectionResolver;
10
use WPGraphQL\Data\Connection\TermObjectConnectionResolver;
11
use WPGraphQL\Model\Post;
12
use WPGraphQL\Type\Connection\Comments;
13
use WPGraphQL\Type\Connection\PostObjects;
14
use WPGraphQL\Type\Connection\TermObjects;
15
use WP_Post_Type;
16

17
/**
18
 * Class PostObject
19
 *
20
 * @package WPGraphQL\Data
21
 * @since   1.12.0
22
 */
23
class PostObject {
24

25
        /**
26
         * Registers a post_type type to the schema as either a GraphQL object, interface, or union.
27
         *
28
         * @param \WP_Post_Type $post_type_object Post type.
29
         *
30
         * @return void
31
         * @throws \Exception
32
         */
33
        public static function register_types( WP_Post_Type $post_type_object ) {
593✔
34
                $single_name = $post_type_object->graphql_single_name;
593✔
35

36
                $config = [
593✔
37
                        'description' => ! empty( $post_type_object->graphql_description )
593✔
38
                                ? $post_type_object->graphql_description
593✔
39
                                : ( ! empty( $post_type_object->description )
306✔
40
                                        ? $post_type_object->description
9✔
41
                                        /* translators: post object singular name w/ description */
306✔
42
                                        : sprintf( __( 'The %s type', 'wp-graphql' ), $single_name )
593✔
43
                                ),
593✔
44
                        'connections' => static::get_connections( $post_type_object ),
593✔
45
                        'interfaces'  => static::get_interfaces( $post_type_object ),
593✔
46
                        'fields'      => static::get_fields( $post_type_object ),
593✔
47
                        'model'       => Post::class,
593✔
48
                ];
593✔
49

50
                // Register as GraphQL objects.
51
                if ( 'object' === $post_type_object->graphql_kind ) {
593✔
52
                        register_graphql_object_type( $single_name, $config );
593✔
53

54
                        // Register fields to the Type used for attachments (MediaItem)
55
                        if ( 'attachment' === $post_type_object->name && true === $post_type_object->show_in_graphql && isset( $post_type_object->graphql_single_name ) ) {
593✔
56
                                self::register_attachment_fields( $post_type_object );
592✔
57
                        }
58

59
                        return;
593✔
60
                }
61

62
                /**
63
                 * Register as GraphQL interfaces or unions.
64
                 *
65
                 * It's assumed that the types used in `resolveType` have already been registered to the schema.
66
                 */
67

68
                // Bail early if graphql_resolve_type isnt a vallable callback.
69
                if ( empty( $post_type_object->graphql_resolve_type ) || ! is_callable( $post_type_object->graphql_resolve_type ) ) {
3✔
70
                        graphql_debug(
1✔
71
                                sprintf(
1✔
72
                                        // translators: %1$s is the post type name, %2$s is the graphql kind.
73
                                        __( '%1$s is registered as a GraphQL %2$s, but has no way to resolve the type. Ensure "graphql_resolve_type" is a valid callback function', 'wp-graphql' ),
1✔
74
                                        $single_name,
1✔
75
                                        $post_type_object->graphql_kind
1✔
76
                                ),
1✔
77
                                [ 'registered_post_type_object' => $post_type_object ]
1✔
78
                        );
1✔
79

80
                        return;
1✔
81
                }
82

83
                $config['resolveType'] = $post_type_object->graphql_resolve_type;
3✔
84

85
                if ( 'interface' === $post_type_object->graphql_kind ) {
3✔
86
                        register_graphql_interface_type( $single_name, $config );
1✔
87

88
                        return;
1✔
89
                } elseif ( 'union' === $post_type_object->graphql_kind ) {
2✔
90

91
                        // Bail early if graphql_union_types is not defined.
92
                        if ( empty( $post_type_object->graphql_union_types ) || ! is_array( $post_type_object->graphql_union_types ) ) {
2✔
93
                                graphql_debug(
1✔
94
                                        __( 'Registering a post type with "graphql_kind" => "union" requires "graphql_union_types" to be a valid array of possible GraphQL type names.', 'wp-graphql' ),
1✔
95
                                        [ 'registered_post_type_object' => $post_type_object ]
1✔
96
                                );
1✔
97

98
                                return;
1✔
99
                        }
100

101
                        // Set the possible types for the union.
102
                        $config['typeNames'] = $post_type_object->graphql_union_types;
1✔
103

104
                        register_graphql_union_type( $single_name, $config );
1✔
105
                }
106
        }
107

108
        /**
109
         * Gets all the connections for the given post type.
110
         *
111
         * @param \WP_Post_Type $post_type_object
112
         *
113
         * @return array<string,array<string,mixed>>
114
         */
115
        protected static function get_connections( WP_Post_Type $post_type_object ) {
593✔
116
                $connections = [];
593✔
117

118
                // Comments.
119
                if ( post_type_supports( $post_type_object->name, 'comments' ) ) {
593✔
120
                        $connections['comments'] = [
593✔
121
                                'toType'         => 'Comment',
593✔
122
                                'connectionArgs' => Comments::get_connection_args(),
593✔
123
                                'resolve'        => static function ( Post $post, $args, $context, $info ) {
593✔
124
                                        if ( $post->isRevision ) {
3✔
125
                                                $id = $post->parentDatabaseId;
×
126
                                        } else {
127
                                                $id = $post->databaseId;
3✔
128
                                        }
129

130
                                        $resolver = new CommentConnectionResolver( $post, $args, $context, $info );
3✔
131

132
                                        return $resolver->set_query_arg( 'post_id', absint( $id ) )->get_connection();
3✔
133
                                },
593✔
134
                        ];
593✔
135
                }
136

137
                // Previews.
138
                if ( ! in_array( $post_type_object->name, [ 'attachment', 'revision' ], true ) ) {
593✔
139
                        $connections['preview'] = [
593✔
140
                                'toType'             => $post_type_object->graphql_single_name,
593✔
141
                                'connectionTypeName' => ucfirst( $post_type_object->graphql_single_name ) . 'ToPreviewConnection',
593✔
142
                                'oneToOne'           => true,
593✔
143
                                'deprecationReason'  => ( true === $post_type_object->publicly_queryable || true === $post_type_object->public ) ? null
593✔
144
                                        : sprintf(
593✔
145
                                                // translators: %s is the post type's GraphQL name.
146
                                                __( 'The "%s" Type is not publicly queryable and does not support previews. This field will be removed in the future.', 'wp-graphql' ),
593✔
147
                                                WPGraphQL\Utils\Utils::format_type_name( $post_type_object->graphql_single_name )
593✔
148
                                        ),
593✔
149
                                'resolve'            => static function ( Post $post, $args, AppContext $context, ResolveInfo $info ) {
593✔
150
                                        if ( $post->isRevision ) {
10✔
151
                                                return null;
×
152
                                        }
153

154
                                        if ( empty( $post->previewRevisionDatabaseId ) ) {
10✔
155
                                                return null;
1✔
156
                                        }
157

158
                                        $resolver = new PostObjectConnectionResolver( $post, $args, $context, $info, 'revision' );
9✔
159
                                        $resolver->set_query_arg( 'p', $post->previewRevisionDatabaseId );
9✔
160

161
                                        return $resolver->one_to_one()->get_connection();
9✔
162
                                },
593✔
163
                        ];
593✔
164
                }
165

166
                // Revisions.
167
                if ( true === post_type_supports( $post_type_object->name, 'revisions' ) ) {
593✔
168
                        $connections['revisions'] = [
593✔
169
                                'connectionTypeName' => ucfirst( $post_type_object->graphql_single_name ) . 'ToRevisionConnection',
593✔
170
                                'toType'             => $post_type_object->graphql_single_name,
593✔
171
                                'queryClass'         => 'WP_Query',
593✔
172
                                'connectionArgs'     => PostObjects::get_connection_args( [], $post_type_object ),
593✔
173
                                'resolve'            => static function ( Post $post, $args, $context, $info ) {
593✔
174
                                        $resolver = new PostObjectConnectionResolver( $post, $args, $context, $info, 'revision' );
3✔
175
                                        $resolver->set_query_arg( 'post_parent', $post->databaseId );
3✔
176

177
                                        return $resolver->get_connection();
3✔
178
                                },
593✔
179
                        ];
593✔
180
                }
181

182
                // Used to ensure TermNode connection doesn't get registered multiple times.
183
                $already_registered = false;
593✔
184
                $allowed_taxonomies = WPGraphQL::get_allowed_taxonomies( 'objects' );
593✔
185

186
                foreach ( $allowed_taxonomies as $tax_object ) {
593✔
187
                        if ( ! in_array( $post_type_object->name, $tax_object->object_type, true ) ) {
593✔
188
                                continue;
592✔
189
                        }
190

191
                        // TermNode.
192
                        if ( ! $already_registered ) {
593✔
193
                                $connections['terms'] = [
593✔
194
                                        'toType'         => 'TermNode',
593✔
195
                                        'queryClass'     => 'WP_Term_Query',
593✔
196
                                        'connectionArgs' => TermObjects::get_connection_args(
593✔
197
                                                [
593✔
198
                                                        'taxonomies' => [
593✔
199
                                                                'type'        => [ 'list_of' => 'TaxonomyEnum' ],
593✔
200
                                                                'description' => __( 'The Taxonomy to filter terms by', 'wp-graphql' ),
593✔
201
                                                        ],
593✔
202
                                                ]
593✔
203
                                        ),
593✔
204
                                        'resolve'        => static function ( Post $post, $args, AppContext $context, ResolveInfo $info ) {
593✔
205
                                                $taxonomies = \WPGraphQL::get_allowed_taxonomies();
×
NEW
206
                                                $object_id  = true === $post->isPreview && ! empty( $post->parentDatabaseId ) ? $post->parentDatabaseId : $post->databaseId;
×
207

208
                                                if ( empty( $object_id ) ) {
×
209
                                                        return null;
×
210
                                                }
211

212
                                                $resolver = new TermObjectConnectionResolver( $post, $args, $context, $info, $taxonomies );
×
213
                                                $resolver->set_query_arg( 'object_ids', absint( $object_id ) );
×
214
                                                return $resolver->get_connection();
×
215
                                        },
593✔
216
                                ];
593✔
217

218
                                // We won't need to register this connection again.
219
                                $already_registered = true;
593✔
220
                        }
221

222
                        // TermObjects.
223
                        $connections[ $tax_object->graphql_plural_name ] = [
593✔
224
                                'toType'         => $tax_object->graphql_single_name,
593✔
225
                                'queryClass'     => 'WP_Term_Query',
593✔
226
                                'connectionArgs' => TermObjects::get_connection_args(),
593✔
227
                                'resolve'        => static function ( Post $post, $args, AppContext $context, $info ) use ( $tax_object ) {
593✔
228
                                        $object_id = true === $post->isPreview && ! empty( $post->parentDatabaseId ) ? $post->parentDatabaseId : $post->databaseId;
15✔
229

230
                                        if ( empty( $object_id ) || ! absint( $object_id ) ) {
15✔
231
                                                return null;
×
232
                                        }
233

234
                                        $resolver = new TermObjectConnectionResolver( $post, $args, $context, $info, $tax_object->name );
15✔
235
                                        $resolver->set_query_arg( 'object_ids', absint( $object_id ) );
15✔
236

237
                                        return $resolver->get_connection();
15✔
238
                                },
593✔
239
                        ];
593✔
240
                }
241

242
                // Deprecated connections.
243
                if ( ! $post_type_object->hierarchical &&
593✔
244
                        ! in_array(
593✔
245
                                $post_type_object->name,
593✔
246
                                [
593✔
247
                                        'attachment',
593✔
248
                                        'revision',
593✔
249
                                ],
593✔
250
                                true
593✔
251
                        ) ) {
593✔
252
                        $connections['ancestors'] = [
593✔
253
                                'toType'            => $post_type_object->graphql_single_name,
593✔
254
                                'description'       => __( 'The ancestors of the content node.', 'wp-graphql' ),
593✔
255
                                'deprecationReason' => __( 'This content type is not hierarchical and typically will not have ancestors', 'wp-graphql' ),
593✔
256
                                'resolve'           => static function () {
593✔
257
                                        return null;
×
258
                                },
593✔
259
                        ];
593✔
260
                        $connections['parent']    = [
593✔
261
                                'toType'            => $post_type_object->graphql_single_name,
593✔
262
                                'oneToOne'          => true,
593✔
263
                                'description'       => __( 'The parent of the content node.', 'wp-graphql' ),
593✔
264
                                'deprecationReason' => __( 'This content type is not hierarchical and typically will not have a parent', 'wp-graphql' ),
593✔
265
                                'resolve'           => static function () {
593✔
266
                                        return null;
×
267
                                },
593✔
268
                        ];
593✔
269
                }
270

271
                // Merge with connections set in register_post_type.
272
                if ( ! empty( $post_type_object->graphql_connections ) ) {
593✔
273
                        $connections = array_merge( $connections, $post_type_object->graphql_connections );
1✔
274
                }
275

276
                // Remove excluded connections.
277
                if ( ! empty( $post_type_object->graphql_exclude_connections ) ) {
593✔
278
                        foreach ( $post_type_object->graphql_exclude_connections as $connection_name ) {
1✔
279
                                unset( $connections[ lcfirst( $connection_name ) ] );
1✔
280
                        }
281
                }
282

283
                return $connections;
593✔
284
        }
285

286
        /**
287
         * Gets all the interfaces for the given post type.
288
         *
289
         * @param \WP_Post_Type $post_type_object Post type.
290
         *
291
         * @return string[]
292
         */
293
        protected static function get_interfaces( WP_Post_Type $post_type_object ) {
593✔
294
                $interfaces = [ 'Node', 'ContentNode', 'DatabaseIdentifier', 'NodeWithTemplate' ];
593✔
295

296
                if ( true === $post_type_object->public ) {
593✔
297
                        $interfaces[] = 'UniformResourceIdentifiable';
593✔
298
                }
299

300
                // Only post types that are publicly_queryable are previewable
301
                if ( 'attachment' !== $post_type_object->name && ( true === $post_type_object->publicly_queryable || true === $post_type_object->public ) ) {
593✔
302
                        $interfaces[] = 'Previewable';
593✔
303
                }
304

305
                if ( post_type_supports( $post_type_object->name, 'title' ) ) {
593✔
306
                        $interfaces[] = 'NodeWithTitle';
593✔
307
                }
308

309
                if ( post_type_supports( $post_type_object->name, 'editor' ) ) {
593✔
310
                        $interfaces[] = 'NodeWithContentEditor';
593✔
311
                }
312

313
                if ( post_type_supports( $post_type_object->name, 'author' ) ) {
593✔
314
                        $interfaces[] = 'NodeWithAuthor';
593✔
315
                }
316

317
                if ( post_type_supports( $post_type_object->name, 'thumbnail' ) ) {
593✔
318
                        $interfaces[] = 'NodeWithFeaturedImage';
593✔
319
                }
320

321
                if ( post_type_supports( $post_type_object->name, 'excerpt' ) ) {
593✔
322
                        $interfaces[] = 'NodeWithExcerpt';
593✔
323
                }
324

325
                if ( post_type_supports( $post_type_object->name, 'comments' ) ) {
593✔
326
                        $interfaces[] = 'NodeWithComments';
593✔
327
                }
328

329
                if ( post_type_supports( $post_type_object->name, 'trackbacks' ) ) {
593✔
330
                        $interfaces[] = 'NodeWithTrackbacks';
593✔
331
                }
332

333
                if ( post_type_supports( $post_type_object->name, 'revisions' ) ) {
593✔
334
                        $interfaces[] = 'NodeWithRevisions';
593✔
335
                }
336

337
                if ( post_type_supports( $post_type_object->name, 'page-attributes' ) ) {
593✔
338
                        $interfaces[] = 'NodeWithPageAttributes';
592✔
339
                }
340

341
                if ( $post_type_object->hierarchical || in_array(
593✔
342
                        $post_type_object->name,
593✔
343
                        [
593✔
344
                                'attachment',
593✔
345
                                'revision',
593✔
346
                        ],
593✔
347
                        true
593✔
348
                ) ) {
593✔
349
                        $interfaces[] = 'HierarchicalContentNode';
592✔
350
                }
351

352
                if ( true === $post_type_object->show_in_nav_menus ) {
593✔
353
                        $interfaces[] = 'MenuItemLinkable';
593✔
354
                }
355

356
                // Merge with interfaces set in register_post_type.
357
                if ( ! empty( $post_type_object->graphql_interfaces ) ) {
593✔
358
                        $interfaces = array_merge( $interfaces, $post_type_object->graphql_interfaces );
1✔
359
                }
360

361
                // Remove excluded interfaces.
362
                if ( ! empty( $post_type_object->graphql_exclude_interfaces ) ) {
593✔
363
                        $interfaces = array_diff( $interfaces, $post_type_object->graphql_exclude_interfaces );
1✔
364
                }
365

366
                return $interfaces;
593✔
367
        }
368

369
        /**
370
         * Registers common post type fields on schema type corresponding to provided post type object.
371
         *
372
         * @param \WP_Post_Type $post_type_object Post type.
373
         *
374
         * @return array<string,array<string,mixed>>
375
         * @todo make protected after \Type\ObjectType\PostObject::get_fields() is removed.
376
         */
377
        public static function get_fields( WP_Post_Type $post_type_object ) {
593✔
378
                $single_name = $post_type_object->graphql_single_name;
593✔
379
                $fields      = [
593✔
380
                        'id'                => [
593✔
381
                                'description' => sprintf(
593✔
382
                                /* translators: %s: custom post-type name */
383
                                        __( 'The globally unique identifier of the %s object.', 'wp-graphql' ),
593✔
384
                                        $post_type_object->name
593✔
385
                                ),
593✔
386
                        ],
593✔
387
                        $single_name . 'Id' => [
593✔
388
                                'type'              => [
593✔
389
                                        'non_null' => 'Int',
593✔
390
                                ],
593✔
391
                                'deprecationReason' => __( 'Deprecated in favor of the databaseId field', 'wp-graphql' ),
593✔
392
                                'description'       => __( 'The id field matches the WP_Post->ID field.', 'wp-graphql' ),
593✔
393
                                'resolve'           => static function ( Post $post ) {
593✔
394
                                        return absint( $post->databaseId );
50✔
395
                                },
593✔
396
                        ],
593✔
397
                        'hasPassword'       => [
593✔
398
                                'type'        => 'Boolean',
593✔
399
                                'description' => sprintf(
593✔
400
                                        // translators: %s: custom post-type name.
401
                                        __( 'Whether the %s object is password protected.', 'wp-graphql' ),
593✔
402
                                        $post_type_object->name
593✔
403
                                ),
593✔
404
                        ],
593✔
405
                        'password'          => [
593✔
406
                                'type'        => 'String',
593✔
407
                                'description' => sprintf(
593✔
408
                                        // translators: %s: custom post-type name.
409
                                        __( 'The password for the %s object.', 'wp-graphql' ),
593✔
410
                                        $post_type_object->name
593✔
411
                                ),
593✔
412
                        ],
593✔
413
                ];
593✔
414

415
                if ( 'page' === $post_type_object->name ) {
593✔
416
                        $fields['isFrontPage'] = [
592✔
417
                                'type'        => [ 'non_null' => 'Bool' ],
592✔
418
                                'description' => __( 'Whether this page is set to the static front page.', 'wp-graphql' ),
592✔
419
                        ];
592✔
420

421
                        $fields['isPostsPage'] = [
592✔
422
                                'type'        => [ 'non_null' => 'Bool' ],
592✔
423
                                'description' => __( 'Whether this page is set to the blog posts page.', 'wp-graphql' ),
592✔
424
                        ];
592✔
425

426
                        $fields['isPrivacyPage'] = [
592✔
427
                                'type'        => [ 'non_null' => 'Bool' ],
592✔
428
                                'description' => __( 'Whether this page is set to the privacy page.', 'wp-graphql' ),
592✔
429
                        ];
592✔
430
                }
431

432
                if ( 'post' === $post_type_object->name ) {
593✔
433
                        $fields['isSticky'] = [
593✔
434
                                'type'        => [ 'non_null' => 'Bool' ],
593✔
435
                                'description' => __( 'Whether this page is sticky', 'wp-graphql' ),
593✔
436
                        ];
593✔
437
                }
438

439
                // Merge with fields set in register_post_type.
440
                if ( ! empty( $post_type_object->graphql_fields ) ) {
593✔
441
                        $fields = array_merge( $fields, $post_type_object->graphql_fields );
2✔
442
                }
443

444
                // Remove excluded fields.
445
                if ( ! empty( $post_type_object->graphql_exclude_fields ) ) {
593✔
446
                        foreach ( $post_type_object->graphql_exclude_fields as $field_name ) {
1✔
447
                                unset( $fields[ $field_name ] );
1✔
448
                        }
449
                }
450

451
                return $fields;
593✔
452
        }
453

454
        /**
455
         * Register fields to the Type used for attachments (MediaItem).
456
         *
457
         * @param \WP_Post_Type $post_type_object Post type.
458
         */
459
        private static function register_attachment_fields( WP_Post_Type $post_type_object ): void {
592✔
460
                /**
461
                 * Register fields custom to the MediaItem Type
462
                 */
463
                register_graphql_fields(
592✔
464
                        $post_type_object->graphql_single_name,
592✔
465
                        [
592✔
466
                                'caption'      => [
592✔
467
                                        'type'        => 'String',
592✔
468
                                        'description' => __( 'The caption for the resource', 'wp-graphql' ),
592✔
469
                                        'args'        => [
592✔
470
                                                'format' => [
592✔
471
                                                        'type'        => 'PostObjectFieldFormatEnum',
592✔
472
                                                        'description' => __( 'Format of the field output', 'wp-graphql' ),
592✔
473
                                                ],
592✔
474
                                        ],
592✔
475
                                        'resolve'     => static function ( $source, $args ) {
592✔
476
                                                if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
7✔
477
                                                        // @codingStandardsIgnoreLine.
478
                                                        return $source->captionRaw;
×
479
                                                }
480

481
                                                // @codingStandardsIgnoreLine.
482
                                                return $source->captionRendered;
7✔
483
                                        },
592✔
484
                                ],
592✔
485
                                'altText'      => [
592✔
486
                                        'type'        => 'String',
592✔
487
                                        'description' => __( 'Alternative text to display when resource is not displayed', 'wp-graphql' ),
592✔
488
                                ],
592✔
489
                                'srcSet'       => [
592✔
490
                                        'type'        => 'string',
592✔
491
                                        'args'        => [
592✔
492
                                                'size' => [
592✔
493
                                                        'type'        => 'MediaItemSizeEnum',
592✔
494
                                                        'description' => __( 'Size of the MediaItem to calculate srcSet with', 'wp-graphql' ),
592✔
495
                                                ],
592✔
496
                                        ],
592✔
497
                                        'description' => __( 'The srcset attribute specifies the URL of the image to use in different situations. It is a comma separated string of urls and their widths.', 'wp-graphql' ),
592✔
498
                                        'resolve'     => static function ( $source, $args ) {
592✔
499
                                                $size = 'medium';
3✔
500
                                                if ( ! empty( $args['size'] ) ) {
3✔
501
                                                        $size = $args['size'];
1✔
502
                                                }
503

504
                                                $src_set = wp_get_attachment_image_srcset( $source->ID, $size );
3✔
505

506
                                                return ! empty( $src_set ) ? $src_set : null;
3✔
507
                                        },
592✔
508
                                ],
592✔
509
                                'sizes'        => [
592✔
510
                                        'type'        => 'string',
592✔
511
                                        'args'        => [
592✔
512
                                                'size' => [
592✔
513
                                                        'type'        => 'MediaItemSizeEnum',
592✔
514
                                                        'description' => __( 'Size of the MediaItem to calculate sizes with', 'wp-graphql' ),
592✔
515
                                                ],
592✔
516
                                        ],
592✔
517
                                        'description' => __( 'The sizes attribute value for an image.', 'wp-graphql' ),
592✔
518
                                        'resolve'     => static function ( $source, $args ) {
592✔
519
                                                $size = 'medium';
1✔
520
                                                if ( ! empty( $args['size'] ) ) {
1✔
521
                                                        $size = $args['size'];
1✔
522
                                                }
523

524
                                                $image = wp_get_attachment_image_src( $source->ID, $size );
1✔
525
                                                if ( $image ) {
1✔
526
                                                        list( $src, $width, $height ) = $image;
1✔
527
                                                        $sizes                        = wp_calculate_image_sizes(
1✔
528
                                                                [
1✔
529
                                                                        absint( $width ),
1✔
530
                                                                        absint( $height ),
1✔
531
                                                                ],
1✔
532
                                                                $src,
1✔
533
                                                                null,
1✔
534
                                                                $source->ID
1✔
535
                                                        );
1✔
536

537
                                                        return ! empty( $sizes ) ? $sizes : null;
1✔
538
                                                }
539

540
                                                return null;
×
541
                                        },
592✔
542
                                ],
592✔
543
                                'description'  => [
592✔
544
                                        'type'        => 'String',
592✔
545
                                        'description' => __( 'Description of the image (stored as post_content)', 'wp-graphql' ),
592✔
546
                                        'args'        => [
592✔
547
                                                'format' => [
592✔
548
                                                        'type'        => 'PostObjectFieldFormatEnum',
592✔
549
                                                        'description' => __( 'Format of the field output', 'wp-graphql' ),
592✔
550
                                                ],
592✔
551
                                        ],
592✔
552
                                        'resolve'     => static function ( $source, $args ) {
592✔
553
                                                if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
8✔
554
                                                        // @codingStandardsIgnoreLine.
555
                                                        return $source->descriptionRaw;
×
556
                                                }
557

558
                                                // @codingStandardsIgnoreLine.
559
                                                return $source->descriptionRendered;
8✔
560
                                        },
592✔
561
                                ],
592✔
562
                                'mediaItemUrl' => [
592✔
563
                                        'type'        => 'String',
592✔
564
                                        'description' => __( 'Url of the mediaItem', 'wp-graphql' ),
592✔
565
                                ],
592✔
566
                                'mediaType'    => [
592✔
567
                                        'type'        => 'String',
592✔
568
                                        'description' => __( 'Type of resource', 'wp-graphql' ),
592✔
569
                                ],
592✔
570
                                'sourceUrl'    => [
592✔
571
                                        'type'        => 'String',
592✔
572
                                        'description' => __( 'Url of the mediaItem', 'wp-graphql' ),
592✔
573
                                        'args'        => [
592✔
574
                                                'size' => [
592✔
575
                                                        'type'        => 'MediaItemSizeEnum',
592✔
576
                                                        'description' => __( 'Size of the MediaItem to return', 'wp-graphql' ),
592✔
577
                                                ],
592✔
578
                                        ],
592✔
579
                                        'resolve'     => static function ( $image, $args ) {
592✔
580
                                                if ( empty( $args['size'] ) ) {
8✔
581
                                                        return $image->sourceUrl;
7✔
582
                                                }
583

584
                                                // @todo why do we coerce full to large?
585
                                                $size = 'full' === $args['size'] ? 'large' : $args['size'];
2✔
586

587
                                                /** @var \WPGraphQL\Model\Post $image */
588
                                                return $image->get_source_url_by_size( $size );
2✔
589
                                        },
592✔
590
                                ],
592✔
591
                                'file'         => [
592✔
592
                                        'type'        => 'String',
592✔
593
                                        'description' => __( 'The filename of the mediaItem for the specified size (default size is full)', 'wp-graphql' ),
592✔
594
                                        'args'        => [
592✔
595
                                                'size' => [
592✔
596
                                                        'type'        => 'MediaItemSizeEnum',
592✔
597
                                                        'description' => __( 'Size of the MediaItem to return', 'wp-graphql' ),
592✔
598
                                                ],
592✔
599
                                        ],
592✔
600
                                        'resolve'     => static function ( $source, $args ) {
592✔
601
                                                // If a size is specified, get the size-specific filename
602
                                                if ( ! empty( $args['size'] ) ) {
1✔
603
                                                        $size = 'full' === $args['size'] ? 'large' : $args['size'];
1✔
604

605
                                                        // Get the metadata which contains size information
606
                                                        $metadata = wp_get_attachment_metadata( $source->databaseId );
1✔
607

608
                                                        if ( ! empty( $metadata['sizes'][ $size ]['file'] ) ) {
1✔
609
                                                                return $metadata['sizes'][ $size ]['file'];
1✔
610
                                                        }
611
                                                }
612

613
                                                // Default to original file
614
                                                $attached_file = get_post_meta( $source->databaseId, '_wp_attached_file', true );
1✔
615
                                                return ! empty( $attached_file ) ? basename( $attached_file ) : null;
1✔
616
                                        },
592✔
617
                                ],
592✔
618
                                'filePath'     => [
592✔
619
                                        'type'        => 'String',
592✔
620
                                        'description' => __( 'The path to the original file relative to the uploads directory', 'wp-graphql' ),
592✔
621
                                        'args'        => [
592✔
622
                                                'size' => [
592✔
623
                                                        'type'        => 'MediaItemSizeEnum',
592✔
624
                                                        'description' => __( 'Size of the MediaItem to return', 'wp-graphql' ),
592✔
625
                                                ],
592✔
626
                                        ],
592✔
627
                                        'resolve'     => static function ( $source, $args ) {
592✔
628
                                                // Get the upload directory info
629
                                                $upload_dir           = wp_upload_dir();
1✔
630
                                                $relative_upload_path = wp_make_link_relative( $upload_dir['baseurl'] );
1✔
631

632
                                                // If a size is specified, get the size-specific path
633
                                                if ( ! empty( $args['size'] ) ) {
1✔
634
                                                        $size = 'full' === $args['size'] ? 'large' : $args['size'];
1✔
635

636
                                                        // Get the metadata which contains size information
637
                                                        $metadata = wp_get_attachment_metadata( $source->databaseId );
1✔
638

639
                                                        if ( ! empty( $metadata['sizes'][ $size ]['file'] ) ) {
1✔
640
                                                                $file_path = $metadata['file'];
1✔
641
                                                                return path_join( $relative_upload_path, dirname( $file_path ) . '/' . $metadata['sizes'][ $size ]['file'] );
1✔
642
                                                        }
643
                                                }
644

645
                                                // Default to original file path
646
                                                $attached_file = get_post_meta( $source->databaseId, '_wp_attached_file', true );
1✔
647

648
                                                if ( empty( $attached_file ) ) {
1✔
649
                                                        return null;
×
650
                                                }
651

652
                                                return path_join( $relative_upload_path, $attached_file );
1✔
653
                                        },
592✔
654
                                ],
592✔
655
                                'fileSize'     => [
592✔
656
                                        'type'        => 'Int',
592✔
657
                                        'description' => __( 'The filesize in bytes of the resource', 'wp-graphql' ),
592✔
658
                                        'args'        => [
592✔
659
                                                'size' => [
592✔
660
                                                        'type'        => 'MediaItemSizeEnum',
592✔
661
                                                        'description' => __( 'Size of the MediaItem to return', 'wp-graphql' ),
592✔
662
                                                ],
592✔
663
                                        ],
592✔
664
                                        'resolve'     => static function ( $image, $args ) {
592✔
665
                                                /**
666
                                                 * By default, use the mediaItemUrl.
667
                                                 *
668
                                                 * @var \WPGraphQL\Model\Post $image
669
                                                 */
670
                                                $source_url = $image->mediaItemUrl;
3✔
671

672
                                                // If there's a url for the provided size, use that instead.
673
                                                if ( ! empty( $args['size'] ) ) {
3✔
674
                                                        $size = ( 'full' === $args['size'] ) ? 'large' : $args['size'];
1✔
675

676
                                                        $source_url = $image->get_source_url_by_size( $size ) ?: $source_url;
1✔
677
                                                }
678

679
                                                // If there's no source_url, return null.
680
                                                if ( empty( $source_url ) ) {
3✔
681
                                                        return null;
×
682
                                                }
683

684
                                                $path_parts    = pathinfo( $source_url );
3✔
685
                                                $original_file = get_attached_file( absint( $image->databaseId ) );
3✔
686
                                                $filesize_path = ! empty( $original_file ) ? path_join( dirname( $original_file ), $path_parts['basename'] ) : null;
3✔
687

688
                                                return ! empty( $filesize_path ) ? filesize( $filesize_path ) : null;
3✔
689
                                        },
592✔
690
                                ],
592✔
691
                                'mimeType'     => [
592✔
692
                                        'type'        => 'String',
592✔
693
                                        'description' => __( 'The mime type of the mediaItem', 'wp-graphql' ),
592✔
694
                                ],
592✔
695
                                'mediaDetails' => [
592✔
696
                                        'type'        => 'MediaDetails',
592✔
697
                                        'description' => __( 'Details about the mediaItem', 'wp-graphql' ),
592✔
698
                                ],
592✔
699
                        ]
592✔
700
                );
592✔
701
        }
702
}
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