Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<?php
namespace App\GraphQL;
use WC_Bundled_Item_Data;
use WC_PB_DB;
use WPGraphQL\AppContext;
use WPGraphQL\WooCommerce\Data\Connection\Product_Connection_Resolver;
/**
* Register a GraphQL Connection between Product and BundleProduct in order to return the
* bundle items for a given Product Bundle
*/
add_action( 'graphql_register_types', function () {
register_graphql_connection( [
'fromType' => TYPE_BUNDLE_PRODUCT,
'toType' => 'Product',
'fromFieldName' => 'bundleItems',
'resolve' => function ( $source, $args, $context, $info ) {
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
return $resolver->get_connection();
},
'edgeFields' => [
'quantityMin' => [
'type' => 'Int',
'resolve' => function ( $source ) {
wp_send_json($source);
/* @var $bundledItem WC_Bundled_Item_Data */
$bundledItem = $source['bundledItem'];
return $bundledItem->get_meta( 'quantity_min' );
},
],
'quantityMax' => [
'type' => 'Int',
'resolve' => function ( $source ) {
/* @var $bundledItem WC_Bundled_Item_Data */
$bundledItem = $source['bundledItem'];
return $bundledItem->get_meta( 'quantity_max' );
},
],
],
] );
} );
/**
* Short-circuit WP_Query to ensure we only query bundle items for a given post. Ideally
* would like to set query_args at the resolver level during the
* register_graphql_connection(), but query args don't take effect there.
*
* see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/321
*/
add_filter( 'graphql_product_connection_query_args', function ( $query_args, $source, $args, $context, $info ) {
// Bail if this isn't a bundle
if ( 'bundle' !== $source->type ) {
return $query_args;
}
// Get the product id of the Product Bundle parent
$productId = $source->productId;
// Bail if we don't have a product id to run our query
if ( empty( $productId ) ) {
return $query_args;
}
global $wpdb;
/**
* Allow for LIMIT overwrites if greater than n is needed for a use-case
*
* @param array $query_args The args that will be passed to the WP_Query.
* @param mixed $source The source that's passed down the GraphQL queries.
* @param array $args The inputArgs on the field.
* @param AppContext $context The AppContext passed down the GraphQL tree.
* @param ResolveInfo $info The ResolveInfo passed down the GraphQL tree.
*/
$bundle_item_limit = apply_filters( 'graphql_product_bundle_item_connector_limit', 10, $source, $args, $context, $info );
// Run our query - get me all of the post ids of the bundle items
$bundle_item_ids = $wpdb->get_results( $wpdb->prepare( "
SELECT
product_id
FROM
{$wpdb->prefix}woocommerce_bundled_items
WHERE
bundle_id = %d
LIMIT %d
",
intval( $productId ),
intval( $bundle_item_limit )
) );
// Extract IDs from query result with some wp_list_pluck magic and set post__in args
$query_args['post__in'] = wp_list_pluck( $bundle_item_ids, 'product_id' );
return $query_args;
}, 10, 5 );
/**
* Inject the WC_Bundled_Item_Data Class into the resolver so that fields can access
* edge data
*/
add_filter( 'graphql_connection_edges', function ( $edges, $resolver ) {
$source = $resolver->getSource();
// Bail if we don't have a source
if ( empty( $source ) ) {
return $edges;
}
// Bail if we don't have a type to check against
if ( empty( $source->type ) ) {
return $edges;
}
// Bail if our $source isn't a bundle
if ( 'bundle' !== $source->type ) {
return $edges;
}
// Get some info about the $resolver
$resolverInfo = $resolver->getInfo();
// Bail if there isn't any info
if ( empty( $resolverInfo ) ) {
return $edges;
}
// Get the name of the field we are resolving
$fieldName = $resolverInfo->fieldName;
// Bail if there's nothing to check against
if ( empty( $fieldName ) ) {
return $edges;
}
// Bail if our field isn't bundleItems
if ( 'bundleItems' !== $fieldName ) {
return $edges;
}
$bundled_data_items = $resolver->getSource()->get_bundled_data_items();
// Go through each edge, find the bundle item id, and set the bundleItem
foreach ( $edges as $key => $edge ) {
$raw_bundled_data_item = array_filter( $bundled_data_items, function ( WC_Bundled_Item_Data $item ) use ( $edge ) {
return $item->get_product_id() === $edge['node']->ID;
} );
if ( empty( $raw_bundled_data_item ) ) {
$edges[ $key ]['bundledItem'] = [];
continue;
}
$bundled_data_item = array_values( $raw_bundled_data_item );
// Expose the bundleItem so that the resolver can get edge data
$edges[ $key ]['bundledItem'] = WC_PB_DB::get_bundled_item( $bundled_data_item[0]->get_bundled_item_id() );
}
return $edges;
}, 10, 2 );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.