wp-graphql / wp-graphql-acf

WPGraphQL for Advanced Custom Fields
https://wpgraphql.com/acf
626 stars 123 forks source link

Reusable Fragments on Field Groups shared betweeen Post Types #53

Closed samburgers closed 11 months ago

samburgers commented 5 years ago

Currently I have registered 12 Post Types that need to share a single ACF field group, namely a large ACF "Flexible Content" field type.

All ACF Fields seem to be prefixed by the PostType name ( ie Page_ and Article_ below), which means it is not possible to create GraphQL Fragments to share between post types, leading to a lot of repetition.

export const contentQueryOne = graphql`
  query MyQuery {
    pages {
      nodes {
        acfRichContent {
          richContent {
            ... on Page_Acfrichcontent_RichContent_CardLinks {
              cardField
            }
            ... on Page_Acfrichcontent_RichContent_KeyStats {
              statField
            }
            ... on Page_Acfrichcontent_RichContent_RelatedResources {
              resourceField
            }
            ... on Page_Acfrichcontent_RichContent_Downloads {
              downloadField
            }
          }
        }
      }
    }
    articles {
      nodes {
        acfRichContent {
          richContent {
            ... on Article_Acfrichcontent_RichContent_CardLinks {
              cardField
            }
            ... on Article_Acfrichcontent_RichContent_KeyStats {
              statField
            }
            ... on Article_Acfrichcontent_RichContent_RelatedResources {
              resourceField
            }
            ... on Article_Acfrichcontent_RichContent_Downloads {
              downloadField
            }
          }
        }
      }
    }
  }
`

... and so on x 10 more prefixed post types.

Ideally it would be possible to create a single fragment that can be used across any post type that might want it. Something like the below...

export const richContent = graphql`
  fragment RichContent on Acfrichcontent_RichContent {
    RichContent {
      ... on Acfrichcontent_RichContent_CardLinks {
        cardField
      }
      ... on Acfrichcontent_RichContent_KeyStats {
        statField
      }
      ... on Acfrichcontent_RichContent_RelatedResources {
        resourceField
      }
      ... on Acfrichcontent_RichContent_Downloads {
        downloadField
      }
    }
  }
`

export const contentQuery = graphql`
  query MyQuery {
    pages {
      nodes {
        acfRichContent {
          ...RichContent
        }
      }
    }
    articles {
      nodes {
        acfRichContent {
          ...RichContent
        }
      }
    }
    news {
      nodes {
        acfRichContent {
          ...RichContent
        }
      }
    }
    events {
      nodes {
        acfRichContent {
          ...RichContent
        }
      }
    }
  }
`

Am i missing something, or should that be possible?

sanderploegsma commented 5 years ago

I think it should be possible for the plugin to generate a GraphQL interface for these field groups next to the post type specific versions:

interface MyCustomFieldGroup {
  field: String
  anotherField: String
}

type Page_MyCustomFieldGroup implements MyCustomFieldGroup {
  field: String
  anotherField: String
}

type Article_MyCustomFieldGroup implements MyCustomFieldGroup {
  field: String
  anotherField: String
}

This should allow you to define a fragment on the MyCustomFieldGroup interface and use that in your queries, reducing the amount of repetition needed.

@jasonbahl what do you think?

sanderploegsma commented 4 years ago

@jasonbahl any updates on this? I'm having the same issue with a Gatsby site, which means I can't use fragments to clean up page queries, leading to a lot of repetition and accidental bugs.

I'm not sure whether the suggestion I posted earlier is possible, maybe you could weigh in.

jasonbahl commented 4 years ago

@sanderploegsma I've sadly not been able to work on this yet. 😢 I do like the idea and I think there's a lot of value in it.

I believe I had some reasoning for the prefixing when I initially worked on things, but I don't have any clear memory of what the reasoning is 🤦‍♂

I've done a lot of work on interfaces in the core WPGraphQL plugin over the past few months, so those updates should make this easier to implement.

Have you dug in at all to see what the technical lift would be to make this happen?

sanderploegsma commented 4 years ago

@jasonbahl I'm not that well versed in WPGraphQL development to be honest, so I would have no idea. The docs don't help much 😅 https://docs.wpgraphql.com/extending/types/#interfaces

I did set up a very basic test instance with some custom post types and fields to see what happens when I remove the PostType_ prefix from the generated fields, which didn't result in any issues...

If that is at all possible, it would eliminate the need for interfaces completely.

sanderploegsma commented 4 years ago

Small update: I tested some more with a local copy of the WP instance that backs our website. Just removing the PostType_ prefix was a bit too simple, mainly because we had an ACF Field Group with the same name as a custom post type, so the types overlapped.

But, after making the following changes, everything worked:

  1. "Root" ACF field groups are prefixed with Acf_, to prevent conflicts with other type names
  2. Nested ACF fields remain prefixed like they were.

This means you can do:

query {
  postType1(id: "id") {
    someAcfFieldGroup {
      ...AcfFields
    }
  }
  postType2(id: "id") {
    someAcfFieldGroup {
      ...AcfFields
    }
  }
}

fragment AcfFields on Acf_Root_Field_Group {
  content {
    ... on Acf_Root_Field_Group_Nested_1 {
      value
    }
    ... on Acf_Root_Field_Group_Nested_2 {
      value
    }
  }
}
Twansparant commented 4 years ago

@sanderploegsma I have the same issue, just not with a Gatsby site so I'm using Apollo for my queries.

Let's say I have a field group called acfFrontPage for just the front page and a field group called acfPage for all other pages.

Both field groups have a flexible content field called blocks with a few different layout blocks: LayoutTypeA & LayoutTypeB.

So now I want to make reusable fragments to get the contents of these modules regardless from which page. So I bumped into the same issue:

const GET_PAGE = gql`
  query GET_PAGE($uri: String!) {
    pageBy(uri: $uri) {
      ...PageAttributes
      acfPage {
        blocks {
          ...FlexibleModules
        }
      }
    }
  }
  ${PAGE_ATTRIBUTES}
  ${FLEXIBLE_MODULES}
`;

const GET_FRONT_PAGE = gql`
  query GET_FRONT_PAGE($id: ID!) {
    page(id: $id) {
      ...PageAttributes
      acfFrontPage {
        blocks {
          ...FlexibleModules
        }
      }
    }
  }
  ${PAGE_ATTRIBUTES}
  ${FLEXIBLE_MODULES}
`;

const FLEXIBLE_MODULES = gql`
  fragment FlexibleModules on Page_Acffrontpage_Blocks {
    ... on Page_Acffrontpage_Blocks_LayoutTypeA {
      ...LayoutTypeA
    }
    ... on Page_Acffrontpage_Blocks_LayoutTypeB {
      ...LayoutTypeB
    }
  }
  ${LAYOUT_TYPE_A}
  ${LAYOUT_TYPE_B}
`;

const LAYOUT_TYPE_A = gql`
  fragment LayoutTypeA on Page_Acffrontpage_Blocks_LayoutTypeA {
    fieldName1
    fieldName2
    ...etc
  }
`;

const LAYOUT_TYPE_B = gql`
  fragment LayoutTypeB on Page_Acffrontpage_Blocks_LayoutTypeB {
    fieldName1
    fieldName2
    ...etc
  }
`;

How did you manage to get this working exactly? You say:

But, after making the following changes, everything worked:

Where and what changes did you make exactly?

Thanks!

sanderploegsma commented 4 years ago

This is about the gist of it: https://github.com/sanderploegsma/wp-graphql-acf/commit/3cadc18a7a4a6407246d56dc14b2d8cdb66acd4d

It tries to be backwards-compatible by allowing you to configure the prefix in the field group settings, falling back to the default when nothing is set. Surely there's a better way to do this, but it works for now.

So far I haven't encountered any issues, but YMMV I guess, since I only tested it on our WP instance.

Twansparant commented 4 years ago

Thanks a lot @sanderploegsma Waiting for your PR to get merged!

mikkelwf commented 1 year ago

Any update on this?

jasonbahl commented 11 months ago

👋🏻 we've re-built WPGraphQL for ACF over here: https://github.com/wp-graphql/wpgraphql-acf and this is one of the central features we wanted to have better support for.

I'm happy to say, I think things are working pretty well now!

We're working on official documentation for the new plugin, but there's some basic information that shows the concept of how things work over here: https://github.com/wp-graphql/wpgraphql-acf#readme

I highly recommend checking out the new version of the plugin when you can as I believe the experience is SIGNIFICANTLY better and it's the version we'll be supporting long-term.


Here's an example query (using WPGraphQL for ACF v2.0.0-beta.5.0.0) to help showcase this in action:

CleanShot 2023-11-01 at 08 57 49

full query:

query KitchenSink($uri: String! = "kitchen-sink") {
  nodeByUri(uri: $uri) {
    id
    uri
    ...AcfProKitchenSink
  }
}

fragment AcfProKitchenSink on WithAcfAcfProKitchenSink {
  acfProKitchenSink {
    flexibleContent {
      __typename
      ...LayoutWithText
      ...LayoutWithGallery
      ...LayoutWithRepeater
      ...LayoutWithClonedGroup
    }
  }
}

fragment LayoutWithText on AcfProKitchenSinkFlexibleContentLayoutWithTextField {
  textField
}

fragment LayoutWithGallery on AcfProKitchenSinkFlexibleContentLayoutWithGallery {
  gallery {
    nodes {
      id
      mediaItemUrl
    }
  }
}

fragment LayoutWithRepeater on AcfProKitchenSinkFlexibleContentLayoutWithRepeater {
  repeater {
    text
  }
}

fragment LayoutWithClonedGroup on AcfProKitchenSinkFlexibleContentLayoutWithClonedGroup {
  clonedTextField
  clonedUserField {
    nodes {
      id
      name
    }
  }
  clonedImageField {
    node {
      id
      mediaItemUrl
    }
  }
}

full response:

{
  "data": {
    "nodeByUri": {
      "id": "cG9zdDozMzEz",
      "uri": "/kitchen-sink/",
      "acfProKitchenSink": {
        "flexibleContent": [
          {
            "__typename": "AcfProKitchenSinkFlexibleContentLayoutWithClonedGroup",
            "clonedTextField": "cloned text value...",
            "clonedUserField": {
              "nodes": [
                {
                  "id": "dXNlcjox",
                  "name": "jasonbahl"
                }
              ]
            },
            "clonedImageField": {
              "node": {
                "id": "cG9zdDoxNDc0",
                "mediaItemUrl": "http://acf2.local/wp-content/uploads/2022/11/cropped-WPGQelephant.png"
              }
            }
          },
          {
            "__typename": "AcfProKitchenSinkFlexibleContentLayoutWithGallery",
            "gallery": {
              "nodes": [
                {
                  "id": "cG9zdDoyOTMx",
                  "mediaItemUrl": "http://acf2.local/wp-content/uploads/2022/11/cropped-WPGQelephant.png"
                },
                {
                  "id": "cG9zdDoxNDc0",
                  "mediaItemUrl": "http://acf2.local/wp-content/uploads/2022/11/cropped-WPGQelephant.png"
                },
                {
                  "id": "cG9zdDozMTUw",
                  "mediaItemUrl": "http://acf2.local/wp-content/uploads/2023/04/taco-surprise-cups.jpeg"
                }
              ]
            }
          },
          {
            "__typename": "AcfProKitchenSinkFlexibleContentLayoutWithRepeater",
            "repeater": [
              {
                "text": "text row one value..."
              },
              {
                "text": "text row two value..."
              }
            ]
          },
          {
            "__typename": "AcfProKitchenSinkFlexibleContentLayoutWithTextField",
            "textField": "Value for the text field..."
          }
        ]
      }
    }
  }
}

I'm going to close this issue as the re-architecture of the plugin has resolved this and we highly encourage you to make the switch to the new plugin when you can. 🙏🏻

It's the version we'll be releasing to WordPress.org (in the review queue now) and it's what we'll be supporting long term.