Skip to content

Instantly share code, notes, and snippets.

@therealgilles
Last active February 4, 2026 03:51
Show Gist options
  • Select an option

  • Save therealgilles/1c31817ccc231a8771e094b1d84fbf88 to your computer and use it in GitHub Desktop.

Select an option

Save therealgilles/1c31817ccc231a8771e094b1d84fbf88 to your computer and use it in GitHub Desktop.
Events GraphQL functions
<?php
/**
* Events GraphQL functions
*/
namespace Events;
use WPGraphQL\Data\Connection\TermObjectConnectionResolver;
use WPGraphQL\AppContext;
use WPGraphQL\Model\Post;
defined( 'ABSPATH' ) || die( 'Direct script access disallowed.' );
/**
* GraphQL class
*/
class GQL {
/**
* Constructor
*/
public function __construct() {
$class = get_called_class();
// Exclude default Event→taxonomy connections so we can register custom ones.
add_filter( 'register_post_type_args', array( $class, 'exclude_event_taxonomy_connections' ), 10, 2 );
// register additional query fields with GraphQL
add_action( 'graphql_register_types', array( $class, 'gql_register_types' ) );
// register custom Event→taxonomy connections with provisional ID normalization
add_action( 'graphql_register_types', array( $class, 'gql_register_connections' ) );
add_filter(
'graphql_post_object_connection_query_args',
array( $class, 'gql_post_object_connection_query_args' ),
10,
5
);
}
/**
* Exclude default Event→taxonomy connections via graphql_exclude_connections.
*
* This allows us to register custom versions that normalize TEC provisional IDs.
*
* @param array $args The post type args.
* @param string $post_type The post type name.
* @return array Modified args.
*/
public static function exclude_event_taxonomy_connections( $args, $post_type ) {
if ( 'tribe_events' !== $post_type ) {
return $args;
}
// Exclude these connections so we can register custom ones with provisional ID support.
$exclude = array( 'eventsCategories', 'categories' );
if ( empty( $args['graphql_exclude_connections'] ) ) {
$args['graphql_exclude_connections'] = $exclude;
} else {
$args['graphql_exclude_connections'] = array_merge(
$args['graphql_exclude_connections'],
$exclude
);
}
return $args;
}
/**
* Register additional query fields with GraphQL
*/
public static function gql_register_types() {
register_graphql_fields(
'RootQueryToEventConnectionWhereArgs',
array(
'CategorySlugIn' => array(
'type' => array( 'list_of' => 'String' ),
'description' => __( 'Include only event in these category slugs', 'theme' ),
),
'CategorySlugNotIn' => array(
'type' => array( 'list_of' => 'String' ),
'description' => __( 'Exclude event in these category slugs', 'theme' ),
),
'EventsCategorySlugIn' => array(
'type' => array( 'list_of' => 'String' ),
'description' => __( 'Include only event in these events category slugs', 'theme' ),
),
'EventsCategorySlugNotIn' => array(
'type' => array( 'list_of' => 'String' ),
'description' => __( 'Exclude event in these events category slugs', 'theme' ),
),
)
);
}
/**
* Register GraphQL connections for Event→taxonomy with provisional ID normalization.
*
* TEC Pro uses provisional IDs for recurring event occurrences. Taxonomy terms
* are attached to the event post, not the occurrence, so we normalize the ID.
*/
public static function gql_register_connections() {
$taxonomies = array(
'tribe_events_cat' => array(
'graphql_plural_name' => 'eventsCategories',
'graphql_single_name' => 'eventsCategory',
),
'category' => array(
'graphql_plural_name' => 'categories',
'graphql_single_name' => 'Category',
),
);
foreach ( $taxonomies as $taxonomy => $names ) {
register_graphql_connection(
array(
'fromType' => 'Event',
'toType' => $names['graphql_single_name'],
'fromFieldName' => $names['graphql_plural_name'],
'resolve' => function (
Post $post,
array $args,
AppContext $context,
$info
) use ( $taxonomy ) {
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$object_id = true === $post->isPreview
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
&& ! empty( $post->parentDatabaseId ) ? $post->parentDatabaseId : $post->databaseId;
if ( empty( $object_id ) || ! absint( $object_id ) ) {
return null;
}
// Normalize provisional IDs from TEC custom tables.
if ( $object_id >= 10000000 ) {
$object_id = \TEC\Events\Custom_Tables\V1\Models\Occurrence::normalize_id( $object_id );
}
$resolver = new TermObjectConnectionResolver( $post, $args, $context, $info, $taxonomy );
$resolver->set_query_arg( 'object_ids', absint( $object_id ) );
return $resolver->get_connection();
},
)
);
}
}
/**
* Implement where filter for categorySlugIn/categorySlugNotIn
* and eventsCategorySlugIn/eventsCategorySlugNotIn for event post type
*
* @param array $query_args The WP_Query arguments.
* @param mixed $source The source object for the connection.
* @param array $input_args The `where` input arguments.
* @param \WPGraphQL\AppContext $context The GraphQL AppContext.
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo object with field-specific execution state
* (AST info, return type, path, etc).
* @return array Modified $query_args to be passed into WP_Query.
*/
public static function gql_post_object_connection_query_args( $query_args, $source, $input_args, $context, $info ) {
$post_types = (array) ( $query_args['post_type'] ?? array() );
if ( in_array( \Tribe__Events__Main::POSTTYPE, $post_types, true ) ) {
if ( ! empty( $input_args['where']['categorySlugIn'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array_map( 'sanitize_title', $input_args['where']['categorySlugIn'] ),
'operator' => 'IN',
);
}
if ( ! empty( $input_args['where']['categorySlugNotIn'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array_map( 'sanitize_title', $input_args['where']['categorySlugNotIn'] ),
'operator' => 'NOT IN',
);
}
if ( ! empty( $input_args['where']['eventsCategorySlugIn'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => \Tribe__Events__Main::TAXONOMY,
'field' => 'slug',
'terms' => array_map( 'sanitize_title', $input_args['where']['eventsCategorySlugIn'] ),
'operator' => 'IN',
);
}
if ( ! empty( $input_args['where']['eventsCategorySlugNotIn'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => \Tribe__Events__Main::TAXONOMY,
'field' => 'slug',
'terms' => array_map( 'sanitize_title', $input_args['where']['eventsCategorySlugNotIn'] ),
'operator' => 'NOT IN',
);
}
}
return $query_args;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment