wp-graphql / wp-graphql-woocommerce

Add WooCommerce support and functionality to your WPGraphQL server
https://woographql.com
GNU General Public License v3.0
648 stars 130 forks source link

Error when querying products that are ACF relationship field items within a Flexible Content #449

Closed boxxroom closed 3 years ago

boxxroom commented 3 years ago

Describe the bug Attaching a product relationship within a Flexible content ACF field on to a Page post type renders an Abstract type error due to the resolve. The resolve is expected to resolve to an Object type.

To Reproduce Steps to reproduce the behavior:

  1. ACF JSON Export File OR Create a new ACF Field Group (named WPGraphQL Example) with a Location > Rules equal to Page Add a Flexible Content (named Flexible Content) Field Type: Relationship (Layout name: Relationship Field, Field Label: Product Relationship) Filter by Post Type: Product Filter by Taxonomy: Simple (optional) Filters: All Checked Elements: Featured Image Checked Return Object: Post Object Save.

  2. Navigate to Pages Choose a page: Home (take note of page ID) Add ACF Flexible Content Relationship Field Select 1+ products Save

  3. Navigate to GraphiQL IDE > Add GraphQL statement (GraphQL Statement) OR

    
    query PageById($id: Int!) {
    pageBy(pageId: $id) {
    id
    isFrontPage
    wpgraphqlExample {
      flexibleContent {
        ... on Page_Wpgraphqlexample_FlexibleContent_RelationshipField {
          fieldGroupName
          productRelationship {
            ... on SimpleProduct {
              id
              name
            }
          }
        }
      }
    }
    }
    }

// Query Variables

{"id": 33}


4. Press Play > **See error**

**Expected behavior**
A list of product objects

**Screenshots**
<img width="1264" alt="WPGraphQL-Example-Error" src="https://user-images.githubusercontent.com/33870545/110033737-b183b380-7d31-11eb-8d63-15e8a145a214.png">

**Desktop (please complete the following information):**
 - OS: macOS Big Sur 11.2.1
 - Browser Chrome
 - Version 88.0.4324.182 (Official Build) (x86_64)

**Plugin Versions**
- **WooGraphQL Version:** 0.6.1
- **WPGraphQL Version:** 1.2.5
- **WordPress Version:** 5.6.2 (Bedrock)
- **WooCommerce Version:** 4.8.0
- **WPGraphQL for Advanced Custom Fields:** 0.3.5
- **WPGraphQL JWT Authentication:** 0.4.1
- **Disable Gutenberg:** 2.4
- **Safe SVG:** 1.9.9
- **WooCommerce Accommodation Bookings:** 1.1.23
- **WooCommerce Bookings:** 1.15.31
- **WooCommerce Product Vendors:** 2.1.45

**Additional context**
I have also added to this thread with a information, findings and debug https://github.com/wp-graphql/wp-graphql-woocommerce/issues/253#issuecomment-786905842
boxxroom commented 3 years ago

Attempting to find a work around or at the very least a quick fix for the above issue.

The Attempt

add_filter('graphql_Page_Wpgraphqlexample_FlexibleContent_RelationshipField_fields', function ($fields) {
    if (isset($fields['productRelationship'])) {
        $fields['productRelationship']['resolve'] = function ($root, $args, $context, $info) {
            return array_map(function ($id) {
                return new \WPGraphQL\WooCommerce\Model\Product($id);
            }, $root['field_6041494b07558']);
        };
    }
    return $fields;
}, 10, 1);

The Output

Flexi-content-relationship-GraphQL-output

Understanding this falls all over the place, (not dynamic for a start), such as:

  1. It relies on a new filter created if the relationship structure is used else where or the graphql_interface_fields filter could be used but it would still require work.
  2. You would need to hard code the ACF field in the array_map or write code to extract the key => value
  3. This only works with SimpleProduct types, in this instance an AccommodationBookingProduct type is required.

Could I possibly have your thoughts @jasonbahl @kidunot89

kidunot89 commented 3 years ago

@boxxroom You want this filter. This example should work as a workaround.

function workaround( $type, $object, $wp_union ) {
  if ( 'Product' === $type ) {
     $type_name = \WPGraphQL\WooCommerce\Data\Factory::resolve_type( $type );
     $type      = $wp_union->type_registry->get_type( $type );
  }

  return $type;
}
add_filter( 'graphql_union_resolve_type', 'workaround', 10, 3 );
boxxroom commented 3 years ago

Thanks for the reply and the code @kidunot89

This filter graphql_union_resolve_type is one we have looked at and also attempted to use, however, as strange as this may sound, it never seems to be invoked. I'm not sure if this is the case for you.

To double check this, we have added your code and additional logging inside the workaround function (logging the $type variable). This tends to break the GraphiQL IDE interface (when pressed play) but logs out the result to file. The output in the log is empty. Moving the logging inside the conditional if ( 'Product' === $type ) allows the GraphiQL IDE interface to work as expected but the log shows no output with the GraphiQL IDE error message returning Abstract type Page_Wpgraphqlexample_FlexibleContent_RelationshipField_ProductRelationship must resolve to an Object type at runtime..... this would suggest this conditional is never satisfied.

kidunot89 commented 3 years ago

@boxxroom There's an error in my example. This is the correction.

function workaround( $type, $object, $wp_union ) {
  if ( 'Product' === $type ) {
     $type_name = \WPGraphQL\WooCommerce\Data\Factory::resolve_type( $type, $object );
     $type      = $wp_union->type_registry->get_type( $type );
  }

  return $type;
}
add_filter( 'graphql_union_resolve_type', 'workaround', 10, 3 );
boxxroom commented 3 years ago

@kidunot89 Thanks for looking in to this.

Even with the updated code, the previous statement still applies with the conditional. The $type variable never resolves to a string in turn never being satisfied.

Also to note, we were unable to locate the method resolve_type within the \WPGraphQL\WooCommerce\Data\Factory class, but did locate a resolve_node_type method.

Attached below is the filter code with a var_dump checking for a string with the output in the developers console.

is_string-code is_string-output

Attached below is the filter code with a var_dump checking what/if classes output in the developers console.

get_class-code get_class-output
boxxroom commented 3 years ago

@kidunot89 We think we have found the issue and it's to do with the booking plugins.

WooCommerce Accommodation Bookings: 1.1.23 https://woocommerce.com/products/woocommerce-accommodation-bookings/

WooCommerce Bookings: 1.15.31 https://woocommerce.com/products/woocommerce-bookings/

In the admin area when editing a product and updating its Product data to Accommodation product or Booking product the error occurs, when using as a Simple product it works but not with the filter code you provided with the workaround filter code we provided:

add_filter('graphql_Page_Wpgraphqlexample_FlexibleContent_RelationshipField_fields', function ($fields) {
    if (isset($fields['productRelationship'])) {
        $fields['productRelationship']['resolve'] = function ($root, $args, $context, $info) {
            return array_map(function ($id) {
                return new \WPGraphQL\WooCommerce\Model\Product($id);
            }, $root['field_6041494b07558']);
        };
    }
    return $fields;
}, 10, 1);

We have created two plugins to add BookingProduct and AccommodationBookingProduct as unions of a Product which can be downloaded here in an attempt to resolve this issue but the error Abstract type Page_Wpgraphqlexample_FlexibleContent_RelationshipField_ProductRelationship must resolve to an Object type at runtime..... still occurs.

Thanks in advance.

jacobarriola commented 3 years ago

@boxxroom I think I ran into something similar when working with the ProductBundle type (https://github.com/jacobarriola/woographql-product-bundles).

I had to to do 2 things to get it working:

  1. Fork ACF for WPGraphQL plugin and add a filter (graphql_acf_relationship_model) to be able to filter the post model getting set at the relationship field type. I have a PR open for this; it may no longer be necessary as @jasonbahl is going to make some changes to better filter this more centrally. -- this solves the problem of rendering standard WC types in relationship fields
  2. Use the graphql_union_resolve_type filter to eliminate the error for your custom types (looks like Accomodation and Booking in your case).
/**
 * Set source Type to correct WooCommerce Type when dealing with the Relationship field.
 *
 * Not doing so causes GraphQL to try and resolve a WC Type (ie Product) to the Post
 * model, which causes resolve errors.
 */
add_filter( 'graphql_acf_relationship_model', function ( $source, $value ) {
    $post = get_post( $value );

    // Bail if we're not dealing with a product
    if ( $post->post_type !== 'product' ) {
        return $source;
    }

    // Only allow published posts to go through. This causes GraphQL errors and breaks builds.
    if ( 'publish' !== $post->post_status ) {
        return;
    }

    if ( false === class_exists( 'WPGraphQL\WooCommerce\ACF_Schema_Filters' ) ) {
        return $source;
    }

    $source = ACF_Schema_Filters::resolve_post_object_source( $source, $value );

    return $source;
}, 10, 2 );

/**
 * Set correct product type so that ACF relationship -> product bundles can resolve. Without this, a bundle added to
 * an ACF relationship field will throw an error.
 */
add_filter( 'graphql_union_resolve_type', function ( $type, $object, $wp_union ) {
    // Bail if not a Product type
    if ( $type->name !== 'Product' ) {
        return $type;
    }

    // Bail if as_WC_Data() is not available
    if ( false === method_exists( $object, 'as_WC_Data') ) {
        return $type;
    }

    // Bail if $object is not a bundle
    if ( $object->as_WC_Data()->product_type !== 'bundle' ) {
        return $type;
    }

    // Set the correct type
    return $wp_union->type_registry->get_type( 'BundleProduct' );
}, 10, 3 );
boxxroom commented 3 years ago

@jacobarriola thanks very much for chiming in on this. We have this working with a tweak to the graphql_union_resolve_type filter.

To test - We initially added the WPGraphQL ACF graphql_acf_relationship_model filter to the production plugin.

We then set out adding the code you provided, but this still failed with the same error output Abstract type Page_Wpgraphqlexample_FlexibleContent_RelationshipField_ProductRelationship must resolve to an Object type at runtime...... After a bit of debugging and digging, we noted that as_WC_Data isn't available so bails (as expected). We were also unable to find this method in the codebase to confirm it exists.

The way we got this working was to build the product from factory and check it is an instance of WC_Product_Accommodation_Booking. Not sure what side effects this may have but initial tests suggest all is working ok.

/**
 * Set correct product type so that ACF relationship -> product accommodations can resolve. Without this, an accommodation added to
 * an ACF relationship field will throw an error.
 */
add_filter('graphql_union_resolve_type', function ($type, $object, $wp_union) {
    // Bail if not a Product type
    if ($type->name !== 'Product') {
        return $type;
    }

    $product = $object->product_factory()->get_product();

    // Bail if $product is not an instance of WC_Product_Accommodation_Booking
    if (! $product instanceof WC_Product_Accommodation_Booking) {
        return $type;
    }

    // Set the correct type
    return $wp_union->type_registry->get_type('AccommodationBookingProduct');
}, 10, 3);

Even if this is only a temporary/workaround fix. It's ideal and allows the build to move on.

Thank you @kidunot89, @jacobarriola for your time and help resolving this.

jacobarriola commented 3 years ago

@boxxroom glad you got it working!

as_WC_Data() is a method of WC_Post: https://github.com/wp-graphql/wp-graphql-woocommerce/blob/develop/includes/model/class-wc-post.php#L138

boxxroom commented 3 years ago

@jacobarriola. This is a small side note: Just checked this file in our codebase and it definitely doesn't have this as_WC_Data() method or the as_WP_Post() it finished with the delete() method. Is it perhaps because it is the development branch you have linked to as opposed to a release branch. https://github.com/wp-graphql/wp-graphql-woocommerce/blob/release/v0.6.0/includes/model/class-wc-post.php#L108

We are using plugin version: 0.6.1

jacobarriola commented 3 years ago

@boxxroom Looks like it was added in 0.7.0 release. https://github.com/wp-graphql/wp-graphql-woocommerce/blob/release/v0.7.0/includes/model/class-wc-post.php#L135

Since you're on a recent version of core wp-graphql, I'd consider an update. There are some breaking changes from your version to 0.8.x, but a lot of good bug fixes too.

boxxroom commented 3 years ago

@jacobarriola updated to the latest versions, updated the code to reflect that of yours and all seems to be working perfect.

Thanks for your help on this.

RodrigoTomeES commented 3 years ago

Hi,

I have the same issue but I couldn't fix it with the code of the comments. When it will be fixed? Thanks!

imagen

hellopath commented 3 years ago

Hi there, Unfortunately I have the same issue even if i use the last version of WPGraphQL WooCommerce (WooGraphQL) v0.8.1

Screenshot 2021-05-17 at 23 45 54

Is there any other fix for that? Thank you !

gvocale commented 3 years ago

Running into this issue as well.

I try to establish a relationship to WooCommerce Product (from WooCommerce). If I change the relationship instead to a different post type Gallery, it seems to work. Maybe WooCommerce assigns on its Product post type some private fields that are tripping GraphQL?

If so, how could we solve it?

brianpereradap commented 2 years ago

Having the same issue. Was anyone able to get is resolved with the above-mentioned filter changes? I tried but couldn't get it to work. image