Archives: Recipes

  • Add WP Admin Color Schemes to WPGraphQL

    The following example code allows you to add WP Admin Color Schemes data to WPGraphQL.

    add_action( 'graphql_register_types', function() {
    
    	$mock_colors = '
    		{
    		   "fresh":{
    		      "name":"Default",
    		      "url":false,
    		      "colors":[
    		         "#222",
    		         "#333",
    		         "#0073aa",
    		         "#00a0d2"
    		      ],
    		      "icon_colors":{
    		         "base":"#a0a5aa",
    		         "focus":"#00a0d2",
    		         "current":"#fff"
    		      }
    		   },
    		   "light":{
    		      "name":"Light",
    		      "url":"http://acf2.local/wp-admin/css/colors/light/colors.min.css",
    		      "colors":[
    		         "#e5e5e5",
    		         "#999",
    		         "#d64e07",
    		         "#04a4cc"
    		      ],
    		      "icon_colors":{
    		         "base":"#999",
    		         "focus":"#ccc",
    		         "current":"#ccc"
    		      }
    		   },
    		   "blue":{
    		      "name":"Blue",
    		      "url":"http://acf2.local/wp-admin/css/colors/blue/colors.min.css",
    		      "colors":[
    		         "#096484",
    		         "#4796b3",
    		         "#52accc",
    		         "#74B6CE"
    		      ],
    		      "icon_colors":{
    		         "base":"#e5f8ff",
    		         "focus":"#fff",
    		         "current":"#fff"
    		      }
    		   },
    		   "midnight":{
    		      "name":"Midnight",
    		      "url":"http://acf2.local/wp-admin/css/colors/midnight/colors.min.css",
    		      "colors":[
    		         "#25282b",
    		         "#363b3f",
    		         "#69a8bb",
    		         "#e14d43"
    		      ],
    		      "icon_colors":{
    		         "base":"#f1f2f3",
    		         "focus":"#fff",
    		         "current":"#fff"
    		      }
    		   },
    		   "sunrise":{
    		      "name":"Sunrise",
    		      "url":"http://acf2.local/wp-admin/css/colors/sunrise/colors.min.css",
    		      "colors":[
    		         "#b43c38",
    		         "#cf4944",
    		         "#dd823b",
    		         "#ccaf0b"
    		      ],
    		      "icon_colors":{
    		         "base":"#f3f1f1",
    		         "focus":"#fff",
    		         "current":"#fff"
    		      }
    		   },
    		   "ectoplasm":{
    		      "name":"Ectoplasm",
    		      "url":"http://acf2.local/wp-admin/css/colors/ectoplasm/colors.min.css",
    		      "colors":[
    		         "#413256",
    		         "#523f6d",
    		         "#a3b745",
    		         "#d46f15"
    		      ],
    		      "icon_colors":{
    		         "base":"#ece6f6",
    		         "focus":"#fff",
    		         "current":"#fff"
    		      }
    		   },
    		   "ocean":{
    		      "name":"Ocean",
    		      "url":"http://acf2.local/wp-admin/css/colors/ocean/colors.min.css",
    		      "colors":[
    		         "#627c83",
    		         "#738e96",
    		         "#9ebaa0",
    		         "#aa9d88"
    		      ],
    		      "icon_colors":{
    		         "base":"#f2fcff",
    		         "focus":"#fff",
    		         "current":"#fff"
    		      }
    		   },
    		   "coffee":{
    		      "name":"Coffee",
    		      "url":"http://acf2.local/wp-admin/css/colors/coffee/colors.min.css",
    		      "colors":[
    		         "#46403c",
    		         "#59524c",
    		         "#c7a589",
    		         "#9ea476"
    		      ],
    		      "icon_colors":{
    		         "base":"#f3f2f1",
    		         "focus":"#fff",
    		         "current":"#fff"
    		      }
    		   }
    		}
    	';
    
    	register_graphql_object_type( 'AdminIconColor', [
    		'fields' => [
    			'base'    => [
    				'type' => 'String'
    			],
    			'focus'   => [
    				'type' => 'String',
    			],
    			'current' => [
    				'type' => 'String',
    			],
    		],
    	] );
    
    	register_graphql_object_type( 'AdminColorPalette', [
    		'description' => __( 'todo', 'wp-graphql' ),
    		'fields' => [
    			'primary' => [
    				'type' => 'String',
    				'resolve' => function( $colors ) {
    					return $colors[0];
    				}
    			],
    			'secondary' => [
    				'type' => 'String',
    				'resolve' => function( $colors ) {
    					return $colors[1];
    				}
    			],
    			'tertiary' => [
    				'type' => 'String',
    				'resolve' => function( $colors ) {
    					return $colors[2];
    				}
    			],
    			'quaternary' => [
    				'type' => 'String',
    				'resolve' => function( $colors ) {
    					return $colors[3];
    				}
    			],
    		],
    	]);
    
    	register_graphql_object_type( 'AdminColorScheme', [
    		'fields' => [
    			'name'           => [
    				'type'        => 'String',
    				'description' => __( 'todo', 'wp-graphql' ),
    			],
    			'colors' => [
    				'type' => 'AdminColorPalette',
    				'description' => __( 'todo', 'wp-graphql' ),
    			],
    			'colorsList'     => [
    				'type'        => [
    					'list_of' => 'String',
    				],
    				'description' => __( 'todo', 'wp-graphql' ),
    				'resolve' => function( $admin_colors ) {
    					return ! empty( $admin_colors->colors ) ? $admin_colors->colors : null;
    				},
    			],
    			'iconColorsList' => [
    				'type' => [
    					'list_of' => 'String',
    				],
    				'resolve' => function($admin_colors  ) {
    
    					$colors = [];
    					if ( ! empty( $admin_colors->icon_colors ) ) {
    						foreach ( $admin_colors->icon_colors as $key => $color ) {
    							$colors[] = $color;
    						}
    					}
    
    					return ! empty( $colors ) ? $colors : null;
    				},
    
    			],
    			'iconColors'     => [
    				'type' => 'AdminIconColor',
    				'resolve' => function($admin_colors  ) {
    					return ! empty( $admin_colors->icon_colors ) ? $admin_colors->icon_colors : null;
    				},
    			]
    		]
    	] );
    
    	register_graphql_field( 'User', 'selectedAdminColorScheme', [
    		'type' => 'AdminColorScheme',
    		'resolve' => function( \WPGraphQL\Model\User $user ) use ( $mock_colors ) {
    			$encoded = json_decode( $mock_colors );
    
    			$colors = [];
    
    			foreach ($encoded as $key => $value) {
    				$value->key = $key;
    				$colors[$key] = $value;
    			}
    
    			$color = get_user_meta( $user->userId, 'admin_color', true );
    			return isset( $colors[ $color ] ) ? $colors[ $color ] : $colors[ 'fresh' ];
    		}
    	] );
    
    	register_graphql_field( 'RootQuery', 'allAdminColorSchemes', [
    		'description' => __( 'todo', 'wp-graphql' ),
    		'type'        => [
    			'list_of' => 'AdminColorScheme',
    		],
    		'resolve'     => function() use ( $mock_colors ) {
    
    			$encoded = json_decode( $mock_colors );
    
    			$colors = [];
    
    			foreach ($encoded as $key => $value) {
    				$value->key = $key;
    				$colors[$key] = $value;
    			}
    
    			return $colors;
    
    			// NEED TO FIGURE OUT HOW TO GET THESE VALUES WHEN WE'RE NOT IN THE ADMIN
    //			require_once( ABSPATH . 'wp-admin/includes/misc.php' );
    //			require_once( ABSPATH . 'wp-includes/general-template.php' );
    //			global $_wp_admin_css_colors;
    //
    //			wp_send_json( $_wp_admin_css_colors );
    		}
    	] );
    
    } );
  • Log WPGraphQL requests to error log

    This snippet logs all WPGraphQL requests to the error log

    add_action( 'do_graphql_request', function( $query, $operation, $variables, $params) {
    	error_log( wp_json_encode( [
    		'query' => $query,
    		'operation' => $operation,
    		'variables' => $variables,
    		'params' => $params
    	] ) );
    }, 10, 4 );
  • Filter to add restricted field on Model

    Labels on Post Types are not publicly exposed by WordPress. They are attributes for use in the Admin, and are treated with respect to proper access to the admin.

    To see the labels, the user requesting them must be authenticated.

    When a user requesting a PostType, these are the following fields that are by default allowed to be viewed by a public request: https://github.com/wp-graphql/wp-graphql/blob/develop/src/Model/PostType.php#L59

    You can use the graphql_allowed_fields_on_restricted_type filter to expose more fields publicly if you chose to do so: https://github.com/wp-graphql/wp-graphql/blob/develop/src/Model/Model.php#L292

    add_filter( 'graphql_allowed_fields_on_restricted_type', function( $fields, $model_name, $data, $visibility, $owner, $current_user ) {
    	if ( 'PostTypeObject' === $model_name ) {
    		$fields[] = 'label';
    	}
    	return $fields;
    }, 10, 6 );

    Before adding the filter:

    Restricted field before adding filter

    After adding the filter:

    Restricted field after adding filter

    Github Issue: https://github.com/wp-graphql/wp-graphql/issues/1304#issuecomment-626836656

  • Tag to Content Node Connection

    The following code registers a connection from Tags to ContentNodes. A field name called contentNodes will be added to the Tag type to make it easy to view all Posts that are tagged with that specific term.

    add_action( 'graphql_register_types', function() {
    	register_graphql_connection([
    		'fromType' => 'Tag',
    		'toType' => 'ContentNode',
    		'fromFieldName' => 'contentNodes',
    		'resolve' => function( \WPGraphQL\Model\Term $source, $args, $context, $info ) {
    			// Get all post types allowed in GraphQL
    			$post_types = WPGraphQL::get_allowed_post_types();
    
    			// Instantiate a new PostObjectConnectionResolver class
    			$resolver = new \WPGraphQL\Data\Connection\PostObjectConnectionResolver( $source, $args, $context, $info, $post_types );
    
    			// Set the argument that will be passed to WP_Query. We want only Posts (of any post type) that are tagged with this Tag's ID
    			$resolver->set_query_arg( 'tag_id', $source->term_id );
    
    			// Return the connection
    			return $resolver->get_connection();
    		}
    	]);
    } );
  • List of Key Values

    This is an example showing how to return a list of keys and values where the keys and values are both strings.

    add_action( 'graphql_register_types', function() {
    
    	register_graphql_object_type( 'keyValue', [
    		'description' => __( 'Keys and their values, both cast as strings', 'your-textdomain' ),
    		'fields'      => [
    			'key'   => [
    				'type' => 'String',
    			],
    			'value' => [
    				'type' => 'String',
    			],
    		]
    	] );
    
    	register_graphql_field( 'RootQuery', 'listOfKeyValues', [
    		'type'        => [ 'list_of' => 'KeyValue' ],
    		'description' => __( 'Field that resolves as a list of keys and values', 'your-textdomain' ),
    		'resolve'     => function() {
    			$mock_array = [
    				'key1' => 'Value1',
    				'key2' => 'Value2',
    				'key3' => 'Value3'
    			];
    
    			$list = [];
    			foreach ( $mock_array as $key => $value ) {
    				$list[] = [
    					'key' => $key,
    					'value' => $value,
    				];
    			}
    
    			return $list;
    		}
    	] );
    
    } );
  • Add Primary Category field for The SEO Framework plugin

    The following adds a field called primaryCat field when using The SEO Framework WordPress Plugin

    add_action( 'init', function() {
    
    	register_graphql_connection([
    		'fromType' => 'Post',
    		'toType' => 'Category',
    		'fromFieldName' => 'primaryCat',
    		'oneToOne' => true,
    		'resolve' => function( \WPGraphQL\Model\Post $post, $args, $context, $info ) {
    
    			$primary_term = null;
    
    			if ( function_exists( 'the_seo_framework' ) ) {
    				$primary_term = the_seo_framework()->get_primary_term( $post->ID, 'category' );
    			}
    
    			// If there's no primary term from the SEO Framework, get the first category assigned
    			if ( empty( $primary_term ) ) {
    				$terms = get_the_terms( $post->ID, 'category' );
    				if ( ! empty( $terms ) ) {
    					$primary_term = $terms[0]->term_id;
    				}
    			}
    
    			// If there's no primary term, return null for the connection
    			if ( empty( $primary_term ) ) {
    				return null;
    			}
    
    			$resolver = new \WPGraphQL\Data\Connection\TermObjectConnectionResolver( $post, $args, $context, $info, 'category' );
    			$resolver->set_query_arg( 'include', absint( $primary_term ) );
    			return $resolver->one_to_one()->get_connection();
    
    		}
    	]);
    
    } );
  • Debugging JWT Authentication

    This snippet outputs the $_SERVER superglobal so we can see if the Authorization token is being passed to the server or not.

    add_action( 'init', function() {
    
    	if ( is_graphql_http_request() ) {
    		wp_send_json( [ 'server' => $_SERVER ] );
    	}
    
    } );
  • Filter Connection Args

    This filters connection args by checking the field name the connection is coming from as well as the type the connection is coming from.

    add_filter( 'graphql_connection_query_args', function( $args, \WPGraphQL\Data\Connection\AbstractConnectionResolver $connection ) {
    
    	// Get the Info from the resolver
    	$info = $connection->getInfo();
    
    	// Get the field name being queried
    	$field_name = $info->fieldName;
    
    	// If the resolver isn't for the children field, return the $args and move on
    	if ( 'children' !== $field_name ) {
    		return $args;
    	}
    
    	// Get the context
    	$context = $connection->getContext();
    
    	// Get the args that were input for the query. ex: first/last/after/before/where
    	$input_args = $connection->getArgs();
    
    	// If the field had orderby arguments input, respect them. Return the $args as-is.
    	if ( isset( $input_args['where']['orderby'] ) ) {
    		return $args;
    	}
    
    	// Determine if the parent type implements the ContentNode interface
    	$parent_implements_content_node = $info->parentType->implementsInterface( $context->type_registry->get_type( 'ContentNode' ) );
    
    	// If the parent type implements ContentNode, this means the `children` field is a connection
    	// from a ContentNode to children (as a pose to menuItem children, Term children, etc)
    	// So we will want to modify the args being sent to WP_Query
    	if ( $parent_implements_content_node ) {
    		// modify the args here.
    		// the $args are what get passed to WP_Query, so make sure they're formatted
    		// for WP_Query.
    		$args['orderby'] = [ 'menu_order' ];
    		$args['order'] = 'ASC';
    	}
    	return $args;
    
    }, 10, 2 );
  • Add field for unencoded content

    The following adds a field to the NodeWithContentEditor interface to get the unencoded content for a post:

    add_action( 'graphql_register_types', function() {
    	register_graphql_field( 'NodeWithContentEditor', 'unencodedContent', [
    		'type' => 'String',
    		'resolve' => function( $post ) {
    			$content = get_post( $post->databaseId )->post_content;
    			return ! empty( $content ) ?  apply_filters( 'the_content', $content ) : null;
    		}
    	]);
    });

    You can query now query for this field:

    {
      contentNode(id: 952, idType: DATABASE_ID) {
        id
        ... on NodeWithTitle {
          title
        }
        ... on NodeWithContentEditor {
          content
          unencodedContent
        }
      }
    }
    Unencoded Content query in GraphiQL

    Related Links

  • ACF Nav Menu Plugin Support

    This adds support (native) for the ACF Nav Menu field plugin (https://github.com/jgraup/advanced-custom-fields-nav-menu-field). This also requires WPGraphQL for Advanced Custom Fields.

    add_filter( 'wpgraphql_acf_register_graphql_field', function( $field_config, $type_name, $field_name, $config ) {
    	if ( isset( $config['acf_field']['type'] ) && 'nav_menu' === $config['acf_field']['type'] ) {
    		$field_config['type'] = 'Menu';
    		$field_config['resolve'] = function( $root, $args, \WPGraphQL\AppContext $context, $info ) use ( $config ) {
    			$menu_id = get_field( $config['acf_field']['key'], $root->databaseId );
    			return ! empty( $menu_id ) ? $context->get_loader( 'term' )->load_deferred( $menu_id ) : null;
    		};
    	}
      return $field_config;
    }, 10, 4 );

    You can query for this:

    {
      post(id: 1669, idType: DATABASE_ID) {
        id
        title
        content
        acfPostFieldGroup {
          menu {
            id
            name
          }
        }
      }
    }
    ACF Nav Menu Plugin query in GraphiQL