PluginProbe
WPGraphQL / 2.22.0
WPGraphQL v2.22.0
2.22.3 2.22.2 2.22.1 2.22.0 2.21.1 2.21.0 2.20.0 2.19.0 2.18.0 2.17.0 2.16.0 2.15.1 2.15.0 2.14.1 2.14.0 2.13.0 2.2.0 2.3.0 2.3.3 2.3.6 2.3.8 2.5.0 2.5.1 2.5.2 2.5.3 All 177 releases
wp-graphql / src / Type / ObjectType / RootQuery.php

RootQuery.php in WPGraphQL 2.22.0, at src/Type/ObjectType/RootQuery.php

982 lines 34.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPGraphQL\Type\ObjectType;
4
5 use GraphQL\Error\UserError;
6 use GraphQL\Type\Definition\ResolveInfo;
7 use GraphQLRelay\Relay;
8 use WPGraphQL\AppContext;
9 use WPGraphQL\Data\Connection\ContentTypeConnectionResolver;
10 use WPGraphQL\Data\Connection\EnqueuedScriptsConnectionResolver;
11 use WPGraphQL\Data\Connection\EnqueuedStylesheetConnectionResolver;
12 use WPGraphQL\Data\Connection\MenuConnectionResolver;
13 use WPGraphQL\Data\Connection\PostObjectConnectionResolver;
14 use WPGraphQL\Data\Connection\ThemeConnectionResolver;
15 use WPGraphQL\Data\Connection\UserRoleConnectionResolver;
16 use WPGraphQL\Data\DataSource;
17 use WPGraphQL\Model\Post;
18 use WPGraphQL\Type\Connection\EnqueuedAssets;
19 use WPGraphQL\Type\Connection\PostObjects;
20 use WPGraphQL\Utils\Preview;
21 use WPGraphQL\Utils\Utils;
22
23 /**
24 * Class RootQuery
25 *
26 * @package WPGraphQL\Type\Object
27 */
28 class RootQuery {
29
30 /**
31 * Register the RootQuery type
32 *
33 * @return void
34 */
35 public static function register_type() {
36 register_graphql_object_type(
37 'RootQuery',
38 [
39 'description' => static function () {
40 return __( 'The root entry point into the Graph', 'wp-graphql' );
41 },
42 'connections' => [
43 'contentTypes' => [
44 'toType' => 'ContentType',
45 'resolve' => static function ( $source, $args, $context, $info ) {
46 $resolver = new ContentTypeConnectionResolver( $source, $args, $context, $info );
47
48 return $resolver->get_connection();
49 },
50 ],
51 'menus' => [
52 'toType' => 'Menu',
53 'connectionArgs' => [
54 'id' => [
55 'type' => 'Int',
56 'description' => static function () {
57 return __( 'The database ID of the object', 'wp-graphql' );
58 },
59 ],
60 'location' => [
61 'type' => 'MenuLocationEnum',
62 'description' => static function () {
63 return __( 'The menu location for the menu being queried', 'wp-graphql' );
64 },
65 ],
66 'slug' => [
67 'type' => 'String',
68 'description' => static function () {
69 return __( 'The slug of the menu to query items for', 'wp-graphql' );
70 },
71 ],
72 ],
73 'resolve' => static function ( $source, $args, $context, $info ) {
74 $resolver = new MenuConnectionResolver( $source, $args, $context, $info, 'nav_menu' );
75
76 return $resolver->get_connection();
77 },
78 ],
79 'plugins' => [
80 'toType' => 'Plugin',
81 'connectionArgs' => [
82 'search' => [
83 'name' => 'search',
84 'type' => 'String',
85 'description' => static function () {
86 return __( 'Show plugin based on a keyword search.', 'wp-graphql' );
87 },
88 ],
89 'status' => [
90 'type' => 'PluginStatusEnum',
91 'description' => static function () {
92 return __( 'Show plugins with a specific status.', 'wp-graphql' );
93 },
94 ],
95 'stati' => [
96 'type' => [ 'list_of' => 'PluginStatusEnum' ],
97 'description' => static function () {
98 return __( 'Retrieve plugins where plugin status is in an array.', 'wp-graphql' );
99 },
100 ],
101 ],
102 'resolve' => static function ( $root, $args, $context, $info ) {
103 return DataSource::resolve_plugins_connection( $root, $args, $context, $info );
104 },
105 ],
106 'registeredScripts' => [
107 'toType' => 'EnqueuedScript',
108 'connectionArgs' => EnqueuedAssets::get_connection_args(),
109 'resolve' => static function ( $source, $args, $context, $info ) {
110
111 // The connection resolver expects the source to include
112 // enqueuedScriptsQueue
113 $source = new \stdClass();
114 $source->enqueuedScriptsQueue = [];
115 global $wp_scripts;
116 do_action( 'wp_enqueue_scripts' );
117 $source->enqueuedScriptsQueue = array_keys( $wp_scripts->registered );
118 $resolver = new EnqueuedScriptsConnectionResolver( $source, $args, $context, $info );
119
120 return $resolver->get_connection();
121 },
122 ],
123 'registeredStylesheets' => [
124 'toType' => 'EnqueuedStylesheet',
125 'connectionArgs' => EnqueuedAssets::get_connection_args(),
126 'resolve' => static function ( $source, $args, $context, $info ) {
127
128 // The connection resolver expects the source to include
129 // enqueuedStylesheetsQueue
130 $source = new \stdClass();
131 $source->enqueuedStylesheetsQueue = [];
132 global $wp_styles;
133 do_action( 'wp_enqueue_scripts' );
134 $source->enqueuedStylesheetsQueue = array_keys( $wp_styles->registered );
135 $resolver = new EnqueuedStylesheetConnectionResolver( $source, $args, $context, $info );
136
137 return $resolver->get_connection();
138 },
139 ],
140 'themes' => [
141 'toType' => 'Theme',
142 'resolve' => static function ( $root, $args, $context, $info ) {
143 $resolver = new ThemeConnectionResolver( $root, $args, $context, $info );
144
145 return $resolver->get_connection();
146 },
147 ],
148 'revisions' => [
149 'toType' => 'ContentNode',
150 'queryClass' => 'WP_Query',
151 'connectionArgs' => PostObjects::get_connection_args(),
152 'resolve' => static function ( $root, $args, $context, $info ) {
153 $resolver = new PostObjectConnectionResolver( $root, $args, $context, $info, 'revision' );
154
155 return $resolver->get_connection();
156 },
157 ],
158 'userRoles' => [
159 'toType' => 'UserRole',
160 'fromFieldName' => 'userRoles',
161 'resolve' => static function ( $user, $args, $context, $info ) {
162 $resolver = new UserRoleConnectionResolver( $user, $args, $context, $info );
163
164 return $resolver->get_connection();
165 },
166 ],
167 ],
168 'fields' => static function () {
169 $fields = [
170 'allSettings' => [
171 'type' => 'Settings',
172 'description' => static function () {
173 return __( 'Entry point to get all settings for the site', 'wp-graphql' );
174 },
175 'resolve' => static function () {
176 return true;
177 },
178 ],
179 'comment' => [
180 'type' => 'Comment',
181 'description' => static function () {
182 return __( 'Returns a Comment', 'wp-graphql' );
183 },
184 'args' => [
185 'id' => [
186 'type' => [
187 'non_null' => 'ID',
188 ],
189 'description' => static function () {
190 return __( 'Unique identifier for the comment node.', 'wp-graphql' );
191 },
192 ],
193 'idType' => [
194 'type' => 'CommentNodeIdTypeEnum',
195 'description' => static function () {
196 return __( 'Type of unique identifier to fetch a comment by. Default is Global ID', 'wp-graphql' );
197 },
198 ],
199 ],
200 'resolve' => static function ( $_source, array $args, AppContext $context ) {
201 $id_type = isset( $args['idType'] ) ? $args['idType'] : 'id';
202
203 switch ( $id_type ) {
204 case 'database_id':
205 $id = absint( $args['id'] );
206 break;
207 default:
208 $id_components = Relay::fromGlobalId( $args['id'] );
209 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
210 throw new UserError( esc_html__( 'The ID input is invalid', 'wp-graphql' ) );
211 }
212 $id = absint( $id_components['id'] );
213
214 break;
215 }
216
217 return $context->get_loader( 'comment' )->load_deferred( $id );
218 },
219 ],
220 'contentNode' => [
221 'type' => 'ContentNode',
222 'description' => static function () {
223 return __( 'A node used to manage content', 'wp-graphql' );
224 },
225 'args' => [
226 'id' => [
227 'type' => [
228 'non_null' => 'ID',
229 ],
230 'description' => static function () {
231 return __( 'Unique identifier for the content node.', 'wp-graphql' );
232 },
233 ],
234 'idType' => [
235 'type' => 'ContentNodeIdTypeEnum',
236 'description' => static function () {
237 return __( 'Type of unique identifier to fetch a content node by. Default is Global ID', 'wp-graphql' );
238 },
239 ],
240 'contentType' => [
241 'type' => 'ContentTypeEnum',
242 'description' => static function () {
243 return __( 'The content type the node is used for. Required when idType is set to "name" or "slug"', 'wp-graphql' );
244 },
245 ],
246 'asPreview' => [
247 'type' => 'Boolean',
248 'description' => static function () {
249 return __( 'Whether to return the Preview Node instead of the Published Node. When the ID of a Node is provided along with asPreview being set to true, the preview node with un-published changes will be returned instead of the published node. If no preview node exists or the requester doesn\'t have proper capabilities to preview, no node will be returned. If the ID provided is a URI and has a preview query arg, it will be used as a fallback if the "asPreview" argument is not explicitly provided as an argument.', 'wp-graphql' );
250 },
251 'deprecationReason' => static function () {
252 return Preview::get_as_preview_deprecation_reason();
253 },
254 ],
255 ],
256 'resolve' => static function ( $_root, $args, AppContext $context ) {
257 $idType = $args['idType'] ?? 'global_id';
258 switch ( $idType ) {
259 case 'uri':
260 return $context->node_resolver->resolve_uri(
261 $args['id'],
262 [
263 'nodeType' => 'ContentNode',
264 'asPreview' => $args['asPreview'] ?? null,
265 ]
266 );
267 case 'database_id':
268 $post_id = absint( $args['id'] );
269 break;
270 case 'global_id':
271 default:
272 $id_components = Relay::fromGlobalId( $args['id'] );
273 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
274 throw new UserError( esc_html__( 'The ID input is invalid. Make sure you set the proper idType for your input.', 'wp-graphql' ) );
275 }
276 $post_id = absint( $id_components['id'] );
277 break;
278 }
279
280 if ( isset( $args['asPreview'] ) && true === $args['asPreview'] ) {
281 if ( is_array( $context->preview ) ) {
282 Preview::debug_as_preview_ignored();
283 } else {
284 $post_id = Utils::get_post_preview_id( $post_id );
285 }
286 }
287
288 $allowed_post_types = \WPGraphQL::get_allowed_post_types();
289 $allowed_post_types[] = 'revision';
290
291 return absint( $post_id ) ? $context->get_loader( 'post' )->load_deferred( $post_id )->then(
292 static function ( $post ) use ( $allowed_post_types ) {
293
294 // if the post isn't an instance of a Post model, return
295 if ( ! $post instanceof Post ) {
296 return null;
297 }
298
299 if ( ! isset( $post->post_type ) || ! in_array( $post->post_type, $allowed_post_types, true ) ) {
300 return null;
301 }
302
303 return $post;
304 }
305 ) : null;
306 },
307 ],
308 'contentType' => [
309 'type' => 'ContentType',
310 'description' => static function () {
311 return __( 'Fetch a Content Type node by unique Identifier', 'wp-graphql' );
312 },
313 'args' => [
314 'id' => [
315 'type' => [ 'non_null' => 'ID' ],
316 'description' => static function () {
317 return __( 'Unique Identifier for the Content Type node.', 'wp-graphql' );
318 },
319 ],
320 'idType' => [
321 'type' => 'ContentTypeIdTypeEnum',
322 'description' => static function () {
323 return __( 'Type of unique identifier to fetch a content type by. Default is Global ID', 'wp-graphql' );
324 },
325 ],
326 ],
327 'resolve' => static function ( $_root, $args, $context ) {
328 $id_type = isset( $args['idType'] ) ? $args['idType'] : 'id';
329
330 $id = null;
331 switch ( $id_type ) {
332 case 'name':
333 // The `id` arg is an ID scalar, so no enum coercion
334 // happens, but the schema's vocabulary for content types
335 // is the ContentTypeEnum name (e.g. POST for the type
336 // registered as post). Accept either form.
337 $id = Utils::map_enum_name_to_value( 'ContentTypeEnum', (string) $args['id'] ) ?? $args['id'];
338 break;
339 case 'id':
340 default:
341 $id_parts = Relay::fromGlobalId( $args['id'] );
342 if ( isset( $id_parts['id'] ) ) {
343 $id = $id_parts['id'];
344 }
345 }
346
347 return ! empty( $id ) ? $context->get_loader( 'post_type' )->load_deferred( $id ) : null;
348 },
349 ],
350 'taxonomy' => [
351 'type' => 'Taxonomy',
352 'description' => static function () {
353 return __( 'Fetch a Taxonomy node by unique Identifier', 'wp-graphql' );
354 },
355 'args' => [
356 'id' => [
357 'type' => [ 'non_null' => 'ID' ],
358 'description' => static function () {
359 return __( 'Unique Identifier for the Taxonomy node.', 'wp-graphql' );
360 },
361 ],
362 'idType' => [
363 'type' => 'TaxonomyIdTypeEnum',
364 'description' => static function () {
365 return __( 'Type of unique identifier to fetch a taxonomy by. Default is Global ID', 'wp-graphql' );
366 },
367 ],
368 ],
369 'resolve' => static function ( $_root, $args, $context ) {
370 $id_type = isset( $args['idType'] ) ? $args['idType'] : 'id';
371
372 $id = null;
373 switch ( $id_type ) {
374 case 'name':
375 // The `id` arg is an ID scalar, so no enum coercion
376 // happens, but the schema's vocabulary for taxonomies is
377 // the TaxonomyEnum name, which derives from a taxonomy's
378 // graphql_single_name (e.g. TAG for the taxonomy
379 // registered as post_tag). Accept either form.
380 $id = Utils::map_enum_name_to_value( 'TaxonomyEnum', (string) $args['id'] ) ?? $args['id'];
381 break;
382 case 'id':
383 default:
384 $id_parts = Relay::fromGlobalId( $args['id'] );
385 if ( isset( $id_parts['id'] ) ) {
386 $id = $id_parts['id'];
387 }
388 }
389
390 return ! empty( $id ) ? $context->get_loader( 'taxonomy' )->load_deferred( $id ) : null;
391 },
392 ],
393 'node' => [
394 'type' => 'Node',
395 'description' => static function () {
396 return __( 'Fetches an object given its ID', 'wp-graphql' );
397 },
398 'args' => [
399 'id' => [
400 'type' => 'ID',
401 'description' => static function () {
402 return __( 'The unique identifier of the node', 'wp-graphql' );
403 },
404 ],
405 ],
406 'resolve' => static function ( $root, $args, AppContext $context, ResolveInfo $info ) {
407 return ! empty( $args['id'] ) ? DataSource::resolve_node( $args['id'], $context, $info ) : null;
408 },
409 ],
410 'nodeByUri' => [
411 'type' => 'UniformResourceIdentifiable',
412 'description' => static function () {
413 return __( 'Fetches an object given its Unique Resource Identifier', 'wp-graphql' );
414 },
415 'args' => [
416 'uri' => [
417 'type' => [ 'non_null' => 'String' ],
418 'description' => static function () {
419 return __( 'Unique Resource Identifier in the form of a path or permalink for a node. Ex: "/hello-world"', 'wp-graphql' );
420 },
421 ],
422 ],
423 'resolve' => static function ( $root, $args, AppContext $context ) {
424 return ! empty( $args['uri'] ) ? $context->node_resolver->resolve_uri( $args['uri'] ) : null;
425 },
426 ],
427 'menu' => [
428 'type' => 'Menu',
429 'description' => static function () {
430 return __( 'A WordPress navigation menu', 'wp-graphql' );
431 },
432 'args' => [
433 'id' => [
434 'type' => [
435 'non_null' => 'ID',
436 ],
437 'description' => static function () {
438 return __( 'The globally unique identifier of the menu.', 'wp-graphql' );
439 },
440 ],
441 'idType' => [
442 'type' => 'MenuNodeIdTypeEnum',
443 'description' => static function () {
444 return __( 'Type of unique identifier to fetch a menu by. Default is Global ID', 'wp-graphql' );
445 },
446 ],
447 ],
448 'resolve' => static function ( $source, array $args, AppContext $context ) {
449 $id_type = isset( $args['idType'] ) ? $args['idType'] : 'id';
450
451 switch ( $id_type ) {
452 case 'database_id':
453 $id = absint( $args['id'] );
454 break;
455 case 'location':
456 $locations = get_nav_menu_locations();
457
458 // The `id` arg is an ID scalar, so no enum coercion
459 // happens, but the schema's vocabulary for menu locations
460 // is the MenuLocationEnum name (e.g. MY_LOCATION for a
461 // location registered as my-location). Accept either form.
462 $location = Utils::map_enum_name_to_value( 'MenuLocationEnum', (string) $args['id'] ) ?? (string) $args['id'];
463
464 if ( ! isset( $locations[ $location ] ) || ! absint( $locations[ $location ] ) ) {
465 throw new UserError( esc_html__( 'No menu set for the provided location', 'wp-graphql' ) );
466 }
467
468 $id = absint( $locations[ $location ] );
469 break;
470 case 'name':
471 $menu = new \WP_Term_Query(
472 [
473 'taxonomy' => 'nav_menu',
474 'fields' => 'ids',
475 'name' => $args['id'],
476 'include_children' => false,
477 'count' => false,
478 ]
479 );
480 $id = ! empty( $menu->terms ) ? (int) $menu->terms[0] : null;
481 break;
482 case 'slug':
483 $menu = new \WP_Term_Query(
484 [
485 'taxonomy' => 'nav_menu',
486 'fields' => 'ids',
487 'slug' => $args['id'],
488 'include_children' => false,
489 'count' => false,
490 ]
491 );
492 $id = ! empty( $menu->terms ) ? (int) $menu->terms[0] : null;
493 break;
494 default:
495 $id_components = Relay::fromGlobalId( $args['id'] );
496 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
497 throw new UserError( esc_html__( 'The ID input is invalid', 'wp-graphql' ) );
498 }
499 $id = absint( $id_components['id'] );
500
501 break;
502 }
503
504 return ! empty( $id ) ? $context->get_loader( 'term' )->load_deferred( absint( $id ) ) : null;
505 },
506 ],
507 'menuItem' => [
508 'type' => 'MenuItem',
509 'description' => static function () {
510 return __( 'A WordPress navigation menu item', 'wp-graphql' );
511 },
512 'args' => [
513 'id' => [
514 'type' => [
515 'non_null' => 'ID',
516 ],
517 'description' => static function () {
518 return __( 'The globally unique identifier of the menu item.', 'wp-graphql' );
519 },
520 ],
521 'idType' => [
522 'type' => 'MenuItemNodeIdTypeEnum',
523 'description' => static function () {
524 return __( 'Type of unique identifier to fetch a menu item by. Default is Global ID', 'wp-graphql' );
525 },
526 ],
527 ],
528 'resolve' => static function ( $source, array $args, AppContext $context ) {
529 $id_type = isset( $args['idType'] ) ? $args['idType'] : 'id';
530
531 switch ( $id_type ) {
532 case 'database_id':
533 $id = absint( $args['id'] );
534 break;
535 default:
536 $id_components = Relay::fromGlobalId( $args['id'] );
537 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
538 throw new UserError( esc_html__( 'The ID input is invalid', 'wp-graphql' ) );
539 }
540 $id = absint( $id_components['id'] );
541
542 break;
543 }
544
545 return $context->get_loader( 'post' )->load_deferred( absint( $id ) );
546 },
547 ],
548 'plugin' => [
549 'type' => 'Plugin',
550 'description' => static function () {
551 return __( 'A WordPress plugin', 'wp-graphql' );
552 },
553 'args' => [
554 'id' => [
555 'type' => [
556 'non_null' => 'ID',
557 ],
558 'description' => static function () {
559 return __( 'The globally unique identifier of the plugin.', 'wp-graphql' );
560 },
561 ],
562 ],
563 'resolve' => static function ( $source, array $args, AppContext $context ) {
564 $id_components = Relay::fromGlobalId( $args['id'] );
565
566 return ! empty( $id_components['id'] ) ? $context->get_loader( 'plugin' )->load_deferred( $id_components['id'] ) : null;
567 },
568 ],
569 'termNode' => [
570 'type' => 'TermNode',
571 'description' => static function () {
572 return __( 'A node in a taxonomy used to group and relate content nodes', 'wp-graphql' );
573 },
574 'args' => [
575 'id' => [
576 'type' => [
577 'non_null' => 'ID',
578 ],
579 'description' => static function () {
580 return __( 'Unique identifier for the term node.', 'wp-graphql' );
581 },
582 ],
583 'idType' => [
584 'type' => 'TermNodeIdTypeEnum',
585 'description' => static function () {
586 return __( 'Type of unique identifier to fetch a term node by. Default is Global ID', 'wp-graphql' );
587 },
588 ],
589 'taxonomy' => [
590 'type' => 'TaxonomyEnum',
591 'description' => static function () {
592 return __( 'The taxonomy of the tern node. Required when idType is set to "name" or "slug"', 'wp-graphql' );
593 },
594 ],
595 ],
596 'resolve' => static function ( $root, $args, AppContext $context ) {
597 $idType = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
598 $term_id = null;
599
600 switch ( $idType ) {
601 case 'slug':
602 case 'name':
603 case 'database_id':
604 $taxonomy = isset( $args['taxonomy'] ) ? $args['taxonomy'] : null;
605 if ( empty( $taxonomy ) && in_array(
606 $idType,
607 [
608 'name',
609 'slug',
610 ],
611 true
612 ) ) {
613 throw new UserError( esc_html__( 'When fetching a Term Node by "slug" or "name", the "taxonomy" also needs to be set as an input.', 'wp-graphql' ) );
614 }
615 if ( 'database_id' === $idType ) {
616 $term = get_term( absint( $args['id'] ) );
617 } else {
618 $term = get_term_by( $idType, $args['id'], $taxonomy );
619 }
620 $term_id = isset( $term->term_id ) ? absint( $term->term_id ) : null;
621
622 break;
623 case 'uri':
624 return $context->node_resolver->resolve_uri(
625 $args['id'],
626 [
627 'nodeType' => 'TermNode',
628 ]
629 );
630 case 'global_id':
631 default:
632 $id_components = Relay::fromGlobalId( $args['id'] );
633 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
634 throw new UserError( esc_html__( 'The ID input is invalid', 'wp-graphql' ) );
635 }
636 $term_id = absint( $id_components['id'] );
637 break;
638 }
639
640 return ! empty( $term_id ) ? $context->get_loader( 'term' )->load_deferred( $term_id ) : null;
641 },
642 ],
643 'theme' => [
644 'type' => 'Theme',
645 'description' => static function () {
646 return __( 'A Theme object', 'wp-graphql' );
647 },
648 'args' => [
649 'id' => [
650 'type' => [
651 'non_null' => 'ID',
652 ],
653 'description' => static function () {
654 return __( 'The globally unique identifier of the theme.', 'wp-graphql' );
655 },
656 ],
657 ],
658 'resolve' => static function ( $source, array $args ) {
659 $id_components = Relay::fromGlobalId( $args['id'] );
660
661 return DataSource::resolve_theme( $id_components['id'] );
662 },
663 ],
664 'user' => [
665 'type' => 'User',
666 'description' => static function () {
667 return __( 'Returns a user', 'wp-graphql' );
668 },
669 'args' => [
670 'id' => [
671 'type' => [
672 'non_null' => 'ID',
673 ],
674 'description' => static function () {
675 return __( 'The globally unique identifier of the user.', 'wp-graphql' );
676 },
677 ],
678 'idType' => [
679 'type' => 'UserNodeIdTypeEnum',
680 'description' => static function () {
681 return __( 'Type of unique identifier to fetch a user by. Default is Global ID', 'wp-graphql' );
682 },
683 ],
684 ],
685 'resolve' => static function ( $source, array $args, $context ) {
686 $idType = isset( $args['idType'] ) ? $args['idType'] : 'id';
687
688 switch ( $idType ) {
689 case 'database_id':
690 $id = absint( $args['id'] );
691 break;
692 case 'uri':
693 return $context->node_resolver->resolve_uri(
694 $args['id'],
695 [
696 'nodeType' => 'User',
697 ]
698 );
699 case 'login':
700 $current_user = wp_get_current_user();
701 if ( $current_user->user_login !== $args['id'] ) {
702 if ( ! current_user_can( 'list_users' ) ) {
703 throw new UserError( esc_html__( 'You do not have permission to request a User by Username', 'wp-graphql' ) );
704 }
705 }
706
707 $user = get_user_by( 'login', $args['id'] );
708 $id = isset( $user->ID ) ? $user->ID : null;
709 break;
710 case 'email':
711 $current_user = wp_get_current_user();
712 if ( $current_user->user_email !== $args['id'] ) {
713 if ( ! current_user_can( 'list_users' ) ) {
714 throw new UserError( esc_html__( 'You do not have permission to request a User by Email', 'wp-graphql' ) );
715 }
716 }
717
718 $user = get_user_by( 'email', $args['id'] );
719 $id = isset( $user->ID ) ? $user->ID : null;
720 break;
721 case 'slug':
722 $user = get_user_by( 'slug', $args['id'] );
723 $id = isset( $user->ID ) ? $user->ID : null;
724 break;
725 case 'id':
726 default:
727 $id_components = Relay::fromGlobalId( $args['id'] );
728 $id = absint( $id_components['id'] );
729 break;
730 }
731
732 return ! empty( $id ) ? $context->get_loader( 'user' )->load_deferred( $id ) : null;
733 },
734 ],
735 'userRole' => [
736 'type' => 'UserRole',
737 'description' => static function () {
738 return __( 'Returns a user role', 'wp-graphql' );
739 },
740 'args' => [
741 'id' => [
742 'type' => [
743 'non_null' => 'ID',
744 ],
745 'description' => static function () {
746 return __( 'The globally unique identifier of the user object.', 'wp-graphql' );
747 },
748 ],
749 ],
750 'resolve' => static function ( $source, array $args ) {
751 $id_components = Relay::fromGlobalId( $args['id'] );
752
753 return DataSource::resolve_user_role( $id_components['id'] );
754 },
755 ],
756 'viewer' => [
757 'type' => 'User',
758 'description' => static function () {
759 return __( 'Returns the current user', 'wp-graphql' );
760 },
761 'resolve' => static function ( $source, array $args, AppContext $context ) {
762 return ! empty( $context->viewer->ID ) ? $context->get_loader( 'user' )->load_deferred( $context->viewer->ID ) : null;
763 },
764 ],
765 ];
766
767 // Only expose allSettings when the Settings type can be built with real fields.
768 if ( empty( Settings::get_fields( \WPGraphQL::get_type_registry() ) ) ) {
769 unset( $fields['allSettings'] );
770 }
771
772 return $fields;
773 },
774 ]
775 );
776 }
777
778 /**
779 * Register RootQuery fields for Post Objects of supported post types
780 *
781 * @return void
782 */
783 public static function register_post_object_fields() {
784 $allowed_post_types = \WPGraphQL::get_allowed_post_types( 'objects', [ 'graphql_register_root_field' => true ] );
785
786 foreach ( $allowed_post_types as $post_type_object ) {
787 register_graphql_field(
788 'RootQuery',
789 $post_type_object->graphql_single_name,
790 [
791 'type' => $post_type_object->graphql_single_name,
792 'description' => static function () use ( $post_type_object ) {
793 return sprintf(
794 // translators: %1$s is the post type GraphQL name, %2$s is the post type description
795 __( 'An object of the %1$s Type. %2$s', 'wp-graphql' ),
796 $post_type_object->graphql_single_name,
797 $post_type_object->description
798 );
799 },
800 'args' => [
801 'id' => [
802 'type' => [
803 'non_null' => 'ID',
804 ],
805 'description' => static function () {
806 return __( 'The globally unique identifier of the object.', 'wp-graphql' );
807 },
808 ],
809 'idType' => [
810 'type' => $post_type_object->graphql_single_name . 'IdType',
811 'description' => static function () {
812 return __( 'Type of unique identifier to fetch by. Default is Global ID', 'wp-graphql' );
813 },
814 ],
815 'asPreview' => [
816 'type' => 'Boolean',
817 'description' => static function () {
818 return __( 'Whether to return the Preview Node instead of the Published Node. When the ID of a Node is provided along with asPreview being set to true, the preview node with un-published changes will be returned instead of the published node. If no preview node exists or the requester doesn\'t have proper capabilities to preview, no node will be returned. If the ID provided is a URI and has a preview query arg, it will be used as a fallback if the "asPreview" argument is not explicitly provided as an argument.', 'wp-graphql' );
819 },
820 'deprecationReason' => static function () {
821 return Preview::get_as_preview_deprecation_reason();
822 },
823 ],
824 ],
825 'resolve' => static function ( $source, array $args, AppContext $context ) use ( $post_type_object ) {
826 $idType = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
827 $post_id = null;
828 switch ( $idType ) {
829 case 'slug':
830 return $context->node_resolver->resolve_uri(
831 $args['id'],
832 [
833 'name' => $args['id'],
834 'post_type' => $post_type_object->name,
835 'nodeType' => 'ContentNode',
836 'asPreview' => $args['asPreview'] ?? null,
837 ]
838 );
839 case 'uri':
840 return $context->node_resolver->resolve_uri(
841 $args['id'],
842 [
843 'post_type' => $post_type_object->name,
844 'archive' => false,
845 'nodeType' => 'ContentNode',
846 'asPreview' => $args['asPreview'] ?? null,
847 ]
848 );
849 case 'database_id':
850 $post_id = absint( $args['id'] );
851 break;
852 case 'source_url':
853 $url = $args['id'];
854 $post_id = attachment_url_to_postid( $url ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.attachment_url_to_postid_attachment_url_to_postid
855 if ( empty( $post_id ) ) {
856 return null;
857 }
858 $post_id = absint( attachment_url_to_postid( $url ) ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.attachment_url_to_postid_attachment_url_to_postid
859 break;
860 case 'global_id':
861 default:
862 $id_components = Relay::fromGlobalId( $args['id'] );
863 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
864 throw new UserError( esc_html__( 'The ID input is invalid. Make sure you set the proper idType for your input.', 'wp-graphql' ) );
865 }
866 $post_id = absint( $id_components['id'] );
867 break;
868 }
869
870 if ( isset( $args['asPreview'] ) && true === $args['asPreview'] ) {
871 if ( is_array( $context->preview ) ) {
872 Preview::debug_as_preview_ignored();
873 } else {
874 $post_id = Utils::get_post_preview_id( $post_id );
875 }
876 }
877
878 return absint( $post_id ) ? $context->get_loader( 'post' )->load_deferred( $post_id )->then(
879 static function ( $post ) use ( $post_type_object ) {
880
881 // if the post isn't an instance of a Post model, return
882 if ( ! $post instanceof Post ) {
883 return null;
884 }
885
886 if ( ! isset( $post->post_type ) || ! in_array(
887 $post->post_type,
888 [
889 'revision',
890 $post_type_object->name,
891 ],
892 true
893 ) ) {
894 return null;
895 }
896
897 return $post;
898 }
899 ) : null;
900 },
901 ]
902 );
903 }
904 }
905
906 /**
907 * Register RootQuery fields for Term Objects of supported taxonomies
908 *
909 * @return void
910 */
911 public static function register_term_object_fields() {
912 $allowed_taxonomies = \WPGraphQL::get_allowed_taxonomies( 'objects', [ 'graphql_register_root_field' => true ] );
913
914 foreach ( $allowed_taxonomies as $tax_object ) {
915 register_graphql_field(
916 'RootQuery',
917 $tax_object->graphql_single_name,
918 [
919 'type' => $tax_object->graphql_single_name,
920 'description' => static function () use ( $tax_object ) {
921 return sprintf(
922 // translators: %s is the taxonomys' GraphQL name.
923 __( 'A % object', 'wp-graphql' ),
924 $tax_object->graphql_single_name
925 );
926 },
927 'args' => [
928 'id' => [
929 'type' => [
930 'non_null' => 'ID',
931 ],
932 'description' => static function () {
933 return __( 'The globally unique identifier of the object.', 'wp-graphql' );
934 },
935 ],
936 'idType' => [
937 'type' => $tax_object->graphql_single_name . 'IdType',
938 'description' => static function () {
939 return __( 'Type of unique identifier to fetch by. Default is Global ID', 'wp-graphql' );
940 },
941 ],
942 ],
943 'resolve' => static function ( $_source, array $args, $context ) use ( $tax_object ) {
944 $idType = isset( $args['idType'] ) ? $args['idType'] : 'global_id';
945 $term_id = null;
946
947 switch ( $idType ) {
948 case 'slug':
949 case 'name':
950 case 'database_id':
951 if ( 'database_id' === $idType ) {
952 $idType = 'id';
953 }
954 $term = get_term_by( $idType, $args['id'], $tax_object->name );
955 $term_id = isset( $term->term_id ) ? absint( $term->term_id ) : null;
956 break;
957 case 'uri':
958 return $context->node_resolver->resolve_uri(
959 $args['id'],
960 [
961 'nodeType' => 'TermNode',
962 'taxonomy' => $tax_object->name,
963 ]
964 );
965 case 'global_id':
966 default:
967 $id_components = Relay::fromGlobalId( $args['id'] );
968 if ( ! isset( $id_components['id'] ) || ! absint( $id_components['id'] ) ) {
969 throw new UserError( esc_html__( 'The ID input is invalid', 'wp-graphql' ) );
970 }
971 $term_id = absint( $id_components['id'] );
972 break;
973 }
974
975 return ! empty( $term_id ) ? $context->get_loader( 'term' )->load_deferred( (int) $term_id ) : null;
976 },
977 ]
978 );
979 }
980 }
981 }
982