Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: [WIP] Exploring a ConnectionResolver 2.0 #2749

Draft
wants to merge 22 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cdfd1a9
feat: wrap `AbstractConnectionResolver::is_valid_model()` in `::get_i…
justlevine May 4, 2024
1d084e8
tests: cleanup
justlevine May 4, 2024
b266d3e
dev: refactor `AbstractConnectionResolver::get_nodes()` and `get_edge…
justlevine May 4, 2024
f58c3e1
chore: relocate `AbstractConnectionResolver::is_valid_offset()` with …
justlevine May 4, 2024
b3da418
dev: refactor AbstractConnectionResolver::get_ids() to `::prepare_ids()`
justlevine May 4, 2024
f6babda
dev: refactor AbstractConnectionResolver::get_ids() to `::prepare_ids()`
justlevine May 4, 2024
6e639de
dev: refactor `AbstractConnectionResolver::get_args()` to `::prepare_…
justlevine May 4, 2024
77d99b9
dev: refactor `AbstractConnectionResolver::get_args()` to `::prepare_…
justlevine May 4, 2024
37e598b
dev: refactor `AbstractConnectionResolver::get_query_args()` to `::pr…
justlevine May 4, 2024
c62f63b
dev: refactor `AbstractConnectionResolver::get_query_args()` to `::pr…
justlevine May 4, 2024
c2b3613
chore: add b/c notes
justlevine May 4, 2024
2e4f976
Merge branch 'dev/refactor-get_args' into tmp/cherrypick
justlevine May 4, 2024
e008719
fix: use `get_query_args()` instead of modifying directly
justlevine May 4, 2024
0847c89
fix: use `get_query_args()` instead of modifying directly
justlevine May 4, 2024
b4aad63
ci: ignore coverage for faux abstract method
justlevine May 4, 2024
da29d16
Merge branch 'dev/refactor-get_args' into tmp/cherrypick
justlevine May 4, 2024
b8dd6a6
feat: refactor query handling in AbstractConnectionResolver
justlevine May 4, 2024
02c6053
tests: custom query class validation
justlevine May 4, 2024
cd00132
fix: deduplicate hook for b/c
justlevine May 4, 2024
765dfcf
Merge branch 'feat/refactor-query-handling' into tmp/cherrypick
justlevine May 4, 2024
eeaed24
chore: rebase remaining changes
justlevine May 4, 2024
025d45e
fix: ensure $this->query_args is primed before overloading
justlevine May 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
999 changes: 578 additions & 421 deletions src/Data/Connection/AbstractConnectionResolver.php

Large diffs are not rendered by default.

75 changes: 32 additions & 43 deletions src/Data/Connection/CommentConnectionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,25 @@

use GraphQL\Error\UserError;
use WPGraphQL\Utils\Utils;
use WP_Comment_Query;

/**
* Class CommentConnectionResolver
*
* @package WPGraphQL\Data\Connection
* @extends \WPGraphQL\Data\Connection\AbstractConnectionResolver<\WP_Comment_Query>
*/
class CommentConnectionResolver extends AbstractConnectionResolver {
/**
* {@inheritDoc}
*
* @var \WP_Comment_Query
*/
protected $query;

/**
* {@inheritDoc}
*
* @throws \GraphQL\Error\UserError
*/
public function get_query_args() {

protected function prepare_query_args( array $args ): array {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function prepare_query_args has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.

/**
* Prepare for later use
*/
$last = ! empty( $this->args['last'] ) ? $this->args['last'] : null;
$last = ! empty( $args['last'] ) ? $args['last'] : null;

$query_args = [];

Expand All @@ -56,18 +49,18 @@ public function get_query_args() {
$query_args['orderby'] = 'comment_date';

/**
* Take any of the $this->args that were part of the GraphQL query and map their
* Take any of the $args that were part of the GraphQL query and map their
* GraphQL names to the WP_Term_Query names to be used in the WP_Term_Query
*
* @since 0.0.5
*/
$input_fields = [];
if ( ! empty( $this->args['where'] ) ) {
$input_fields = $this->sanitize_input_fields( $this->args['where'] );
if ( ! empty( $args['where'] ) ) {
$input_fields = $this->sanitize_input_fields( $args['where'] );
}

/**
* Merge the default $query_args with the $this->args that were entered
* Merge the default $query_args with the $args that were entered
* in the query.
*
* @since 0.0.5
Expand Down Expand Up @@ -113,14 +106,14 @@ public function get_query_args() {
$query_args['graphql_before_cursor'] = $this->get_before_offset();

/**
* Pass the graphql $this->args to the WP_Query
* Pass the graphql $args to the WP_Query
*/
$query_args['graphql_args'] = $this->args;
$query_args['graphql_args'] = $args;

// encode the graphql args as a cache domain to ensure the
// graphql_args are used to identify different queries.
// see: https://core.trac.wordpress.org/ticket/35075
$encoded_args = wp_json_encode( $this->args );
$encoded_args = wp_json_encode( $args );
$query_args['cache_domain'] = ! empty( $encoded_args ) ? 'graphql:' . md5( $encoded_args ) : 'graphql';

/**
Expand All @@ -130,28 +123,21 @@ public function get_query_args() {
$query_args['fields'] = 'ids';

/**
* Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
* the GraphQL Query have been applied and has the potential to override the GraphQL Query Input Args.
* Filters the query args used by the connection.
*
* @param array<string,mixed> $query_args array of query_args being passed to the
* @param mixed $source source passed down from the resolve tree
* @param array<string,mixed> $args array of arguments input in the field as part of the GraphQL query
* @param \WPGraphQL\AppContext $context object passed down the resolve tree
* @param \GraphQL\Type\Definition\ResolveInfo $info info about fields passed down the resolve tree
* @param array<string,mixed> $query_args The query args to be used with the executable query to get data.
* @param self $resolver The AbstractConnectionResolver instance.
*
* @since 0.0.6
*/
return apply_filters( 'graphql_comment_connection_query_args', $query_args, $this->source, $this->args, $this->context, $this->info );
return apply_filters( 'graphql_comment_connection_query_args', $query_args, $this );
}

/**
* {@inheritDoc}
*
* @return \WP_Comment_Query
* @throws \Exception
*/
public function get_query() {
return new WP_Comment_Query( $this->query_args );
protected function query_class(): string {
return \WP_Comment_Query::class;
}

/**
Expand All @@ -164,26 +150,26 @@ protected function loader_name(): string {
/**
* {@inheritDoc}
*/
public function get_ids_from_query() {
public function get_ids_from_query(): array {
$queried = $this->get_query();
$comments = $queried->get_comments();
/** @var int[]|string[] $ids */
$ids = ! empty( $this->query->get_comments() ) ? $this->query->get_comments() : [];
$ids = ! empty( $comments ) ? $comments : [];

// If we're going backwards, we need to reverse the array.
if ( ! empty( $this->args['last'] ) ) {
$args = $this->get_args();

if ( ! empty( $args['last'] ) ) {
$ids = array_reverse( $ids );
}

return $ids;
}

/**
* Filters the GraphQL args before they are used in get_query_args().
*
* @return array<string,mixed>
* {@inheritDoc}
*/
public function get_args(): array {
$args = $this->get_unfiltered_args();

protected function prepare_args( array $args ): array {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function prepare_args has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method prepare_args has 48 lines of code (exceeds 40 allowed). Consider refactoring.

if ( ! empty( $args['where'] ) ) {
// Ensure all IDs are converted to database IDs.
foreach ( $args['where'] as $input_key => $input_value ) {
Expand Down Expand Up @@ -238,8 +224,8 @@ static function ( $id ) {
/**
* Filters the GraphQL args before they are used in get_query_args().
*
* @param array<string,mixed> $args The GraphQL args passed to the resolver.
* @param \WPGraphQL\Data\Connection\CommentConnectionResolver $connection_resolver Instance of the ConnectionResolver
* @param array<string,mixed> $args The GraphQL args passed to the resolver.
* @param self $resolver Instance of the ConnectionResolver
*
* @since 1.11.0
*/
Expand Down Expand Up @@ -296,9 +282,12 @@ public function sanitize_input_fields( array $args ) {
* This allows plugins/themes to hook in and alter what $args should be allowed to be passed
* from a GraphQL Query to the get_terms query
*
* @param array<string,mixed> $query_args The array of query arguments
* @param self $resolver The instance of the CommentConnectionResolver
*
* @since 0.0.5
*/
$query_args = apply_filters( 'graphql_map_input_fields_to_wp_comment_query', $query_args, $args, $this->source, $this->args, $this->context, $this->info );
$query_args = apply_filters( 'graphql_map_input_fields_to_wp_comment_query', $query_args, $this );

return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
}
Expand All @@ -308,7 +297,7 @@ public function sanitize_input_fields( array $args ) {
*
* @param int $offset The ID of the node used for the cursor offset.
*/
public function is_valid_offset( $offset ) {
public function is_valid_offset( $offset ): bool {
return ! empty( get_comment( $offset ) );
}
}
213 changes: 213 additions & 0 deletions src/Data/Connection/ConnectionResolverShim.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php

namespace WPGraphQL\Data\Connection;

use Exception;
use GraphQL\Type\Definition\ResolveInfo;
use WPGraphQL\AppContext;

/**
* Class ConnectionResolverShim - Shim for ConnectionResolver 2.0
*
* Note: This is NOT a true shim, but rather illustrates the changes that need to be made to convert classes to use the new ConnectionResolver.
*
* Issues like changes in method signatures (visibility, arg/return types, etc) are not addressed here.
*
* @package WPGraphQL\Data\Connection
* @extends \WPGraphQL\Data\Connection\AbstractConnectionResolver<mixed[]|object|mixed>
*/
abstract class ConnectionResolverShim extends AbstractConnectionResolver {

/**
* Returns the source of the connection
*
* @deprecated @todo
* @return mixed
*
* @codeCoverageIgnore
*/
public function getSource() {
_deprecated_function( __METHOD__, '@todo', parent::class . '::get_source()' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return $this->get_source();
}

/**
* Get the loader name
*
* @deprecated @todo
*
* @return \WPGraphQL\Data\Loader\AbstractDataLoader
*
* @codeCoverageIgnore
*/
protected function getLoader() {
_deprecated_function( __METHOD__, '@todo', parent::class . '::get_loader()' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

return $this->get_loader();
}

/**
* Returns the $args passed to the connection
*
* @deprecated @todo
*
* @return array<string,mixed>
*
* @codeCoverageIgnore
*/
public function getArgs(): array {
_deprecated_function( __METHOD__, '1.11.0', static::class . '::get_args()' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return $this->get_args();
}

/**
* Returns the AppContext of the connection
*
* @deprecated @todo
*
* @codeCoverageIgnore
*/
public function getContext(): AppContext {
_deprecated_function( __METHOD__, '@todo', parent::class . '::get_context()' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return $this->get_context();
}

/**
* Returns the ResolveInfo of the connection
*
* @deprecated @todo
*
* @codeCoverageIgnore
*/
public function getInfo(): ResolveInfo {
_deprecated_function( __METHOD__, '@todo', parent::class . '::get_info()' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return $this->get_info();
}

/**
* Returns whether the connection should execute
*
* @deprecated @todo
*
* @codeCoverageIgnore
*/
public function getShouldExecute(): bool {
_deprecated_function( __METHOD__, '@todo', parent::class . '::get_should_execute()' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return $this->get_should_execute();
}

/**
* @param string $key The key of the query arg to set
* @param mixed $value The value of the query arg to set
*
* @return static
*
* @deprecated 0.3.0
*
* @codeCoverageIgnore
*/
public function setQueryArg( $key, $value ) {
_deprecated_function( __METHOD__, '0.3.0', static::class . '::set_query_arg()' );

return $this->set_query_arg( $key, $value );
}

/**
* {@inheritDoc}
*
* Method `get_loader_name()` is no longer abstract.
*
* Overloading should be done via `loader_name()`.
*
* @throws \Exception
*/
protected function loader_name(): string {
throw new Exception(
sprintf(
// translators: %s is the name of the connection resolver class.
esc_html__( 'Class %s does not implement a valid method `loader_name()`.', 'wp-graphql' ),
static::class
)
);
}

/**
* {@inheritDoc}
*
* Method `get_query_args()` is no longer abstract.
*
* Overloading should be done via `prepare_query_args()`.
*
* @throws \Exception
*/
protected function prepare_query_args( array $args ): array {
throw new Exception(
sprintf(
// translators: %s is the name of the connection resolver class.
esc_html__( 'Class %s does not implement a valid method `prepare_query_args()`.', 'wp-graphql' ),
static::class
)
);
}

/**
* Method `get_query()` is no longer abstract. Overloading (if necesary) should be done via `query()` and `query_class()`.
*/

/**
* Method `should_execute()` is now protected and no longer abstract. It defaults to `true`.
*
* Overload `should_execute()` or `pre_should_execute()` should only occur if there is a reason the connecton should not always execute..
*/

/**
* This method is now abstract.
*
* {@inheritDoc}
*
* @throws \Exception
*/
public function get_ids_from_query(): array {
throw new Exception(
sprintf(
// translators: %s is the name of the connection resolver class.
esc_html__( 'Class %s does not implement a valid method `get_ids_from_query()`.', 'wp-graphql' ),
static::class
)
);
}

/**
* This returns the offset to be used in the $query_args based on the $args passed to the
* GraphQL query.
*
* @deprecated 1.9.0
*
* @codeCoverageIgnore
*
* @return int|mixed
*/
public function get_offset() {
_deprecated_function( __METHOD__, '1.9.0', static::class . '::get_offset_for_cursor()' );

$args = $this->get_args();

// Using shorthand since this is for deprecated code.
$cursor = $args['after'] ?? null;
$cursor = $cursor ?: ( $args['before'] ?? null );

return $this->get_offset_for_cursor( $cursor );
}

/**
* Method get_nodes() should now be overloaded `prepare_nodes()`.
*/

/**
* Method `get_edges()` should be overloaded `prepare_edges()`.
*/

/**
* Method `get_page_info()` should now be overloaded via `prepare_page_info()`.
*/
}