$wc_post_types, 'post_status' => 'any', 'posts_per_page' => count( $keys ), 'post__in' => $keys, 'orderby' => 'post__in', 'no_found_rows' => true, 'split_the_query' => false, 'ignore_sticky_posts' => true, ); /** * Ensure that WP_Query doesn't first ask for IDs since we already have them. */ add_filter( 'split_the_query', function ( $split, \WP_Query $query ) { if ( false === $query->get( 'split_the_query' ) ) { return false; } return $split; }, 10, 2 ); new \WP_Query( $args ); $loaded_posts = []; /** * Loop over the posts and return an array of all_posts, * where the key is the ID and the value is the Post passed through * the model layer. */ foreach ( $keys as $key ) { /** * The query above has added our objects to the cache * so now we can pluck them from the cache to return here * and if they don't exist or aren't a valid post-type we can throw an error, otherwise * we can proceed to resolve the object via the Model layer. */ $post_type = get_post_type( $key ); if ( ! $post_type ) { $loaded_posts[ $key ] = null; } if ( ! in_array( $post_type, $wc_post_types, true ) ) { /* translators: invalid post-type error message */ throw new UserError( sprintf( __( '%s is not a valid WooCommerce post-type', 'wp-graphql-woocommerce' ), $post_type ) ); } /** * If there's a customer connected to the order, we need to resolve the * customer */ $context = $this->context; $customer_id = null; $parent_id = null; // Resolve post author for future capability checks. switch ( $post_type ) { case 'shop_order': $customer_id = get_post_meta( $key, '_customer_user', true ); if ( ! empty( $customer_id ) ) { $this->context->getLoader( 'wc_customer' )->buffer( [ $customer_id ] ); } break; case 'product_variation': case 'shop_refund': $parent_id = get_post_field( 'post_parent', $key ); $this->buffer( [ $parent_id ] ); break; } /** * This is a deferred function that allows us to do batch loading * of dependant resources. When the Model Layer attempts to determine * access control of a Post, it needs to know the owner of it, and * if it's a revision, it needs the Parent. * * This deferred function allows for the objects to be loaded all at once * instead of loading once per entity, thus reducing the n+1 problem. */ $load_dependencies = new Deferred( function() use ( $key, $post_type, $customer_id, $parent_id, $context ) { if ( ! empty( $customer_id ) ) { $context->getLoader( 'wc_customer' )->load( $customer_id ); } if ( ! empty( $parent_id ) ) { $this->load( $parent_id ); } /** * Run an action when the dependencies are being loaded for * Post Objects */ do_action( 'woographql_cpt_loader_load_dependencies', $this, $key, $post_type ); return; } ); /** * Once dependencies are loaded, return the Post Object */ $loaded_posts[ $key ] = $load_dependencies->then( function() use ( $post_type, $key ) { return self::resolve_model( $post_type, $key ); } ); } return ! empty( $loaded_posts ) ? $loaded_posts : []; } /** * Callback for inject the PostObject dataloader with WC_Post models. * * @param null $model * @param mixed $entry * @param mixed $key * @return void */ public static function inject_post_loader_models( $model, $entry, $key ) { if ( is_a( $entry, \WP_Post::class ) ) { $model = self::resolve_model( $entry->post_type, $key, false ); } return $model; } }