From b17419765685174fdd0893b3bb456916d0a8535e Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Thu, 14 Mar 2024 16:37:54 -0400 Subject: [PATCH 01/10] Add GraphQL feature class. Wip functions --- src/features/class-graphql.php | 141 +++++++++++++++++++++++++++++++++ src/main.php | 1 + 2 files changed, 142 insertions(+) create mode 100644 src/features/class-graphql.php diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php new file mode 100644 index 00000000..c125b050 --- /dev/null +++ b/src/features/class-graphql.php @@ -0,0 +1,141 @@ +allowed_post_types, true ) ) { + return ''; + } + + + return ucfirst( $post_type_object->graphql_single_name ); + } + + /** + * Get GraphQL Types from allowed post types. + * + * @return array + */ + private function get_types_from_allowed_post_types(): array { + $interface_to_types = []; + + foreach ( $this->allowed_post_types as $post_type ) { + $interface_to_types[] = $this->get_graphql_type_by_post_type( $post_type ); + } + + return $interface_to_types; + } + + /** + * Set up. + */ + public function __construct() { + $this->allowed_post_types = apply_filters( 'wp_curate_allowed_post_types', [ 'post', 'opinion' ] ); + } + + /** + * Boot the feature. + */ + public function boot(): void { + // Assumes that WPGraphQL has been installed as a composer dependency of a parent project. + if ( class_exists( 'WPGraphQL' ) ) { + add_action( 'graphql_register_types', [ $this, 'graphql_register_types' ] ); + } + } + + /** + * Register types in WPGraphQL + * + * @return void + */ + public function graphql_register_types(): void { + + /** + * Add an Interface Type for WP Curate to the registry. + * + * @see https://www.wpgraphql.com/functions/register_graphql_interface_type + */ + register_graphql_interface_type( + 'WPCurateInterface', + [ + 'description' => __( 'Represents the interface type a WP Curate post', 'wp-curate' ), + 'interfaces' => [ 'ContentNode', 'NodeWithTitle', 'NodeWithFeaturedImage' ], + 'fields' => [], + 'resolveType' => function ( $node ) { + return $this->get_graphql_type_by_post_type( $node->post_type ); + }, + ] + ); + + + /** + * Apply the WP Curate interface to registered GraphQL Types. + * Types can be filtered to include project specific custom post types. + * + * @see https://www.wpgraphql.com/functions/register_graphql_interfaces_to_types + */ + register_graphql_interfaces_to_types( [ 'WPCurateInterface' ], $this->get_types_from_allowed_post_types() ); + + /** + * Register a new connection field named 'wpCuratePosts' on `RootQuery` + * to access WP Curate posts. Supports Inline Fragments when constructing + * your GraphQL query. + * + * @see https://www.wpgraphql.com/functions/register_graphql_connection + */ + register_graphql_connection( + [ + 'fromType' => 'RootQuery', + 'toType' => 'WPCurateInterface', + 'fromFieldName' => 'wpCuratePosts', + 'connectionArgs' => [ + 'in' => [ + 'type' => [ 'list_of' => 'ID' ], + 'description' => __( 'Array of IDs for the objects to retrieve', 'wp-curate' ), + ], + ], + 'resolve' => function ( $source, $args, AppContext $context, ResolveInfo $info ) { + $resolver = new PostObjectConnectionResolver( $source, $args, $context, $info ); + + $resolver->set_query_arg( 'post__in', $args['where']['in'] ); + $resolver->set_query_arg( 'post_type', $this->allowed_post_types ); + + return $resolver->get_connection(); + }, + ], + ); + } +} diff --git a/src/main.php b/src/main.php index 0459def0..ad1643ac 100644 --- a/src/main.php +++ b/src/main.php @@ -41,6 +41,7 @@ function main(): void { $features[] = new Features\Parsely_Support(); $features[] = new Features\Rest_Api(); + $features[] = new Features\GraphQL(); foreach ( $features as $feature ) { $feature->boot(); From 1b9c2f247c1eb46dd52499bef83dd9091a646008 Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 09:33:25 -0400 Subject: [PATCH 02/10] Add type to function param Co-authored-by: Kevin Fodness <2650828+kevinfodness@users.noreply.github.com> --- src/features/class-graphql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index c125b050..9895a2eb 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -28,7 +28,7 @@ final class GraphQL implements Feature { * * @return string GraphQL Type name. Ex: 'Post'. */ - private function get_graphql_type_by_post_type( $post_type_string ): string { + private function get_graphql_type_by_post_type( string $post_type_string ): string { $post_type_object = get_post_type_object( $post_type_string ); if ( empty( $post_type_object ) ) { From 5b48193892aaecb8961d3d2378dc532ca493818c Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 09:34:30 -0400 Subject: [PATCH 03/10] Update src/features/class-graphql.php Co-authored-by: Kevin Fodness <2650828+kevinfodness@users.noreply.github.com> --- src/features/class-graphql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index 9895a2eb..05774f6d 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -63,7 +63,7 @@ private function get_types_from_allowed_post_types(): array { * Set up. */ public function __construct() { - $this->allowed_post_types = apply_filters( 'wp_curate_allowed_post_types', [ 'post', 'opinion' ] ); + $this->allowed_post_types = apply_filters( 'wp_curate_allowed_post_types', [ 'post' ] ); } /** From d8509ea8b0addb09f01b7aa6c3f1b825e2d75d1c Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 09:36:26 -0400 Subject: [PATCH 04/10] Hook will not run unless plugin is active Co-authored-by: Kevin Fodness <2650828+kevinfodness@users.noreply.github.com> --- src/features/class-graphql.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index 05774f6d..7a894a0c 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -70,10 +70,7 @@ public function __construct() { * Boot the feature. */ public function boot(): void { - // Assumes that WPGraphQL has been installed as a composer dependency of a parent project. - if ( class_exists( 'WPGraphQL' ) ) { - add_action( 'graphql_register_types', [ $this, 'graphql_register_types' ] ); - } + add_action( 'graphql_register_types', [ $this, 'graphql_register_types' ] ); } /** From d6644c4e6727953425c9ad0899f4de0f041f1806 Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 09:42:21 -0400 Subject: [PATCH 05/10] Run allowed post type check first --- src/features/class-graphql.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index 7a894a0c..7c5d3ff1 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -31,15 +31,14 @@ final class GraphQL implements Feature { private function get_graphql_type_by_post_type( string $post_type_string ): string { $post_type_object = get_post_type_object( $post_type_string ); - if ( empty( $post_type_object ) ) { - return ''; - } - // Only return a GraphQL type for allowed post types in WP Curate. if ( ! in_array( $post_type_string, $this->allowed_post_types, true ) ) { return ''; } + if ( empty( $post_type_object ) ) { + return ''; + } return ucfirst( $post_type_object->graphql_single_name ); } From 29cffeb4cb9d4de2167bbd4d945a2f6d33bddd81 Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 09:44:05 -0400 Subject: [PATCH 06/10] Sniff fix --- src/features/class-graphql.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index 7c5d3ff1..db009e7f 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -18,6 +18,8 @@ final class GraphQL implements Feature { /** * Post types allowed in GraphQL. + * + * @var array */ private $allowed_post_types; From 9f2626f6f8903c58b3151bb6d7a8bf488c372dfc Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 10:03:51 -0400 Subject: [PATCH 07/10] Correct var type --- src/features/class-graphql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index db009e7f..11b6b8b0 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -19,7 +19,7 @@ final class GraphQL implements Feature { /** * Post types allowed in GraphQL. * - * @var array + * @var string[] */ private $allowed_post_types; From 876f7a5427cca4e027df4fcad9be5fd5e8202f6c Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 18:31:42 -0400 Subject: [PATCH 08/10] Ignore phpstan errors related to WPGraphQL refs --- src/features/class-graphql.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index 11b6b8b0..d163db1d 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -42,6 +42,7 @@ private function get_graphql_type_by_post_type( string $post_type_string ): stri return ''; } + // @phpstan-ignore-next-line return ucfirst( $post_type_object->graphql_single_name ); } @@ -86,7 +87,7 @@ public function graphql_register_types(): void { * * @see https://www.wpgraphql.com/functions/register_graphql_interface_type */ - register_graphql_interface_type( + register_graphql_interface_type( // @phpstan-ignore-line 'WPCurateInterface', [ 'description' => __( 'Represents the interface type a WP Curate post', 'wp-curate' ), @@ -105,7 +106,7 @@ public function graphql_register_types(): void { * * @see https://www.wpgraphql.com/functions/register_graphql_interfaces_to_types */ - register_graphql_interfaces_to_types( [ 'WPCurateInterface' ], $this->get_types_from_allowed_post_types() ); + register_graphql_interfaces_to_types( [ 'WPCurateInterface' ], $this->get_types_from_allowed_post_types() ); // @phpstan-ignore-line /** * Register a new connection field named 'wpCuratePosts' on `RootQuery` @@ -114,7 +115,7 @@ public function graphql_register_types(): void { * * @see https://www.wpgraphql.com/functions/register_graphql_connection */ - register_graphql_connection( + register_graphql_connection( // @phpstan-ignore-line [ 'fromType' => 'RootQuery', 'toType' => 'WPCurateInterface', @@ -125,13 +126,13 @@ public function graphql_register_types(): void { 'description' => __( 'Array of IDs for the objects to retrieve', 'wp-curate' ), ], ], - 'resolve' => function ( $source, $args, AppContext $context, ResolveInfo $info ) { - $resolver = new PostObjectConnectionResolver( $source, $args, $context, $info ); + 'resolve' => function ( $source, $args, AppContext $context, ResolveInfo $info ) { // @phpstan-ignore-line + $resolver = new PostObjectConnectionResolver( $source, $args, $context, $info ); // @phpstan-ignore-line - $resolver->set_query_arg( 'post__in', $args['where']['in'] ); - $resolver->set_query_arg( 'post_type', $this->allowed_post_types ); + $resolver->set_query_arg( 'post__in', $args['where']['in'] ); // @phpstan-ignore-line + $resolver->set_query_arg( 'post_type', $this->allowed_post_types ); // @phpstan-ignore-line - return $resolver->get_connection(); + return $resolver->get_connection(); // @phpstan-ignore-line }, ], ); From 41a4fb46d62e6d381337c0480f25e7712d165727 Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 18:35:58 -0400 Subject: [PATCH 09/10] Sniff fix --- src/features/class-graphql.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/class-graphql.php b/src/features/class-graphql.php index d163db1d..19f41d42 100644 --- a/src/features/class-graphql.php +++ b/src/features/class-graphql.php @@ -126,7 +126,8 @@ public function graphql_register_types(): void { 'description' => __( 'Array of IDs for the objects to retrieve', 'wp-curate' ), ], ], - 'resolve' => function ( $source, $args, AppContext $context, ResolveInfo $info ) { // @phpstan-ignore-line + // @phpstan-ignore-next-line + 'resolve' => function ( $source, $args, AppContext $context, ResolveInfo $info ) { $resolver = new PostObjectConnectionResolver( $source, $args, $context, $info ); // @phpstan-ignore-line $resolver->set_query_arg( 'post__in', $args['where']['in'] ); // @phpstan-ignore-line From e161d2ae9fc31b8abb7e7a2fc26f67f3fd7764ab Mon Sep 17 00:00:00 2001 From: Matthew Ell Date: Tue, 19 Mar 2024 18:45:02 -0400 Subject: [PATCH 10/10] Version bump --- CHANGELOG.md | 4 ++++ wp-curate.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa2b6554..84b940bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `WP Curate` will be documented in this file. +## 1.8.0 - 2024-03-19 + +- Enhancement: Integration with [WPGraphQL plugin](https://wordpress.org/plugins/wp-graphql/) to support custom GraphQL interface type and connection. + ## 1.7.0 - 2024-03-06 - Enhancement: Integration with [Parse.ly plugin](https://wordpress.org/plugins/wp-parsely/) to support querying trending posts. diff --git a/wp-curate.php b/wp-curate.php index 65cd81ab..475f112c 100644 --- a/wp-curate.php +++ b/wp-curate.php @@ -3,7 +3,7 @@ * Plugin Name: WP Curate * Plugin URI: https://github.com/alleyinteractive/wp-curate * Description: Plugin to curate homepages and other landing pages - * Version: 1.7.0 + * Version: 1.8.0 * Author: Alley Interactive * Author URI: https://github.com/alleyinteractive/wp-curate * Requires at least: 6.4