Coder Social home page Coder Social logo

wp-graphql-gravity-forms's Introduction

๐Ÿš€๐Ÿ“„ WPGraphQL for Gravity Forms

A WordPress plugin that provides a GraphQL API for interacting with Gravity Forms.

Overview

Using WordPress as a headless CMS with a separate JavaScript-powered frontend single-page app is an increasingly popular tech stack. Traditionally, REST APIs have been used for the purpose of sending data back & forth between the frontend and backend in setups like this but the REST architecture has its limitations.

Using GraphQL means that if your frontend app needs to fetch data for a number of different resources, all of that data can be fetched from the server with a single request. Your frontend app can even define which fields it requires for each of the resources, giving it full control over which pieces of data are fetched and included in the response.

Fortunately, a GraphQL implementation exists for WordPress - WPGraphQL.

WPGraphQL for Gravity Forms extends the WPGraphQL plugin, allowing frontend apps to interact with the Gravity Forms data stored in a headless WordPress backend. This plugin couples the great forms functionality of Gravity Forms with the powerful WordPress-specific GraphQL implementation that WPGraphQL provides.

Our hope for this open source project is that it will enable more teams to leverage GraphQL for building fast, interactive frontend apps that source their data from WordPress and Gravity Forms.

System Requirements

  • PHP 7.4+
  • WPGraphQL 0.12.3
  • Gravity Forms 2.4+
  • WordPress 5.4.1+

Getting Started

  1. Use Composer to require the plugin as a dependency of your project. Alternatively, you can download it into your plugins directory, just like any other WordPress plugin.
  2. Activate the plugin, along with the WPGraphQL and Gravity Forms plugins that it depends on.
  3. Use a tool like GraphiQL to view the schema and send a few test requests to your /graphql endpoint to interact with Gravity Forms data, and start sending requests from your frontend app.

Documentation

Get a Form and its Fields

The example query below shows how you can get a form and its fields. If you want to get the form with an ID of 1, you need to generate a global ID for that object and pass the global ID in as the id input. This can be done in JavaScript using the btoa() function like this, where GravityFormsForm is the GraphQL type and 1 is the form ID:

const globalId = btoa(`GravityFormsForm:1`); // Results in "R3Jhdml0eUZvcm1zRm9ybTox"

For fields, pass in first:300, where 300 is the maximum number of fields you want to query for.

Inside of fields, you must include query fragments indicating what data you'd like back for each field, as shown below. You'll want to make sure that you have a fragment for every type of field that your form has.

Example Query

{
  gravityFormsForm(id: "R3Jhdml0eUZvcm1zRm9ybTox") {
    formId
    cssClass
    cssClassList
    fields(first: 300) {
      nodes {
        ... on TextField {
          type
          id
          label
          cssClass
          cssClassList
        }
        ... on TextAreaField {
          type
          id
          label
          cssClass
          cssClassList
        }
        ... on SelectField {
          type
          id
          label
          cssClass
          cssClassList
        }
      }
    }
  }
}

Submit a Form Entry

The form entry submission process works like this:

  1. Send a createGravityFormsDraftEntry mutation to create a new draft form entry. This gives you back the resumeToken, which is the unique identifier for the draft entry that you need to pass in to all the mutations below.
  2. Send as many "update" mutations (such as updateDraftEntryTextFieldValue, updateDraftEntrySelectFieldValue, etc.) as you need to update the values of the draft entry.
  3. When ready, send a submitGravityFormsDraftEntry that turns the draft entry into a permanent entry.

If you're wondering why several mutations are required to submit a form entry rather than just a single mutation, please read the comments in this issue.

For large forms, #2 on the list above could potentially result in the need to send a large number of "update" mutations to the backend to update form entry field values. Using something like Apollo Client's apollo-link-batch-http is recommended so that your app will be able to send a large number of mutations to the backend all within a single HTTP request to update the draft entry.

As of Dec. 23, 2019, updating draft entries with file upload field data is not yet supported, along with a few other field types.

Example Mutations

Create a New Draft Entry

mutation {
  createGravityFormsDraftEntry(
    input: { clientMutationId: "123abc", formId: 2 }
  ) {
    resumeToken
  }
}

Update a Draft Entry

The example below shows updateDraftEntryTextFieldValue, which can be used for updating the value of a text field. Similar mutations exist for other field types, such as updateDraftEntrySelectFieldValue, updateDraftEntryAddressFieldValue, etc.

Use the resumeToken from the createGravityFormsDraftEntry mutation's response. It is this draft entry's unique identifier.

mutation {
  updateDraftEntryTextFieldValue(
    input: {
      clientMutationId: "123abc"
      resumeToken: "524d5f3a30c845b29a8db35c9f2aaf29"
      fieldId: 5
      value: "new text field value"
    }
  ) {
    resumeToken
    entry {
      entryId # This will be null, since draft entries don't have an ID yet.
      resumeToken # This will be the same resumeToken that was passed in.
      isDraft # This will be set to true.
      fields(first: 300) {
        edges {
          node {
            ... on TextField {
              id
              type
            }
          }
          fieldValue {
            ... on TextFieldValue {
              value
            }
          }
        }
      }
    }
    errors {
      message
    }
  }
}

If the field is updated successfully, the errors field will be null, and the entry in the response will be the newly updated entry, with the new field value.

If the field is NOT updated successfully, such as when a field validation error occurs, the entry object in the response will be unchanged (the new field value will NOT have been applied), and the errors field will provide data about the error. Example:

"errors": [
  {
    "message": "The text entered exceeds the maximum number of characters."
  }
]

Submit a Draft Entry

Once all updates have been performed, the submitGravityFormsDraftEntry mutation shown below can be run to submit the draft entry so that it becomes a permanent entry.

The entry field in the response contains data for the newly created entry.

mutation {
  submitGravityFormsDraftEntry(
    input: {
      clientMutationId: "123abc"
      resumeToken: "5df948218f40484d81e808d0ebc8651b"
    }
  ) {
    entryId # This will be the ID of the newly created entry.
    entry {
      entryId # This will be the ID of the newly created entry.
      resumeToken # This will be null, since the entry is no longer a draft.
      isDraft # This will be set to false.
      fields {
        edges {
          node {
            __typename
            ... on TextField {
              type
              id
            }
          }
          fieldValue {
            __typename
            ... on TextFieldValue {
              value
            }
          }
        }
      }
    }
  }
}

Delete a Draft Entry

The mutation below shows how to delete a draft entry. The resumeToken of the deleted draft entry will be in the response.

Example Mutation

mutation {
  deleteGravityFormsDraftEntry(
    input: {
      clientMutationId: "123abc"
      resumeToken: "524d5f3a30c845b29a8db35c9f2aaf29"
    }
  ) {
    resumeToken
  }
}

Get a Single Draft Entry

The gravityFormsEntry query supports both entries and draft entries. See the "Get a Single Entry" section below.

Get a List of Entries

The code comments in the example below explain how you can get a filtered list of entries.

The plugin supports first/after cursor-based pagination, but does not yet support before/last pagination.

Inside of fields, you must include query fragments indicating what data you'd like back for each field, as shown below. You'll want to make sure that you have a fragments inside of node { ... } and inside of fieldValue { ... } for every type of field that your form has.

Example Query

{
  gravityFormsEntries(
    first: 20
    after: "eyJvZmZzZXQiOjAsImluZGV4Ijo0fQ==" # Or pass null to start from the beginning.
    where: {
      # List of all the form IDs to include.
      formIds: [1]
      # Find entries between this start & end date.
      dateFilters: {
        startDate: "2019-09-22 02:26:23"
        endDate: "2019-10-25 02:26:23"
      }
      fieldFiltersMode: "all"
      fieldFilters: [
        # Find entries created by user ID 1.
        { key: "created_by", intValues: [1], operator: IN }
        # Find entries where field 5 has a value of "somevalue".
        { key: "5", stringValues: ["somevalue"], operator: IN }
      ]
    }
  ) {
    nodes {
      entryId
      formId
      isDraft
      status
      dateCreated
      createdBy {
        node {
          userId
          name
        }
      }
      fields(first: 300) {
        edges {
          node {
            ... on TextField {
              type
              label
            }
            ... on SelectField {
              type
              label
            }
            ... on AddressField {
              type
              label
            }
          }
          fieldValue {
            ... on TextFieldValue {
              value
            }
            ... on SelectFieldValue {
              value
            }
            ... on AddressFieldValue {
              street
              lineTwo
              city
              state
              zip
              country
            }
          }
        }
      }
    }
  }
}

Get a Single Entry

The example query below shows how you can get a single entry by ID, and data about the fields and their values.

If you want to get the form with an ID of 1, you need to generate a global ID for that object and pass the global ID in as the id input. This can be done in JavaScript using the btoa() function like this, where GravityFormsEntry is the GraphQL type and 1 is the form ID:

const globalId = btoa(`GravityFormsEntry:1`); // Results in "R3Jhdml0eUZvcm1zRW50cnk6MQ=="

The id input field can be the entry's ID, or the resumeToken if it is a draft entry.

For fields, pass in first: 300, where 300 is the maximum number of fields you want to query for.

Inside of fields, you must include query fragments indicating what data you'd like back for each field, as shown below. You'll want to make sure that you have a fragments inside of node { ... } and inside of fieldValue { ... } for every type of field that your form has.

Example Query

{
  gravityFormsEntry(id: "R3Jhdml0eUZvcm1zRW50cnk6MQ==") {
    id
    entryId
    formId
    isDraft
    resumeToken
    fields(first: 300) {
      edges {
        node {
          ... on TextField {
            type
            label
          }
          ... on SelectField {
            type
            label
          }
          ... on AddressField {
            type
            label
          }
        }
        fieldValue {
          ... on TextFieldValue {
            value
          }
          ... on SelectFieldValue {
            value
          }
          ... on AddressFieldValue {
            street
            lineTwo
            city
            state
            zip
            country
          }
        }
      }
    }
  }
}

Delete an Entry

The mutation below shows how to delete an entry. The entryId of the deleted entry will be in the response.

Example Mutation

mutation {
  deleteGravityFormsEntry(input: { clientMutationId: "123abc", entryId: 5 }) {
    entryId
  }
}

Get a List of Forms

The example query below shows how you can fetch data for multiple forms at once.

Cursor-based pagination is supported. You can use the first, last, before and after fields, along with the data inside of pageInfo and the cursors returned by the API to get each page of forms data.

Filtering is also supported. For the where field, you can specify a status to get forms that are active, inactive, in the trash, etc.

For fields, pass in first: 300, where 300 is the maximum number of fields you want to query for.

Inside of fields, you must include query fragments indicating what data you'd like back for each field, as shown below. You'll want to make sure that you have a fragment for every type of field that your forms have.

Example Query

{
  gravityFormsForms(first: 10, after: null, where: { status: ACTIVE }) {
    pageInfo {
      startCursor
      endCursor
      hasPreviousPage
      hasNextPage
    }
    edges {
      cursor
      node {
        formId
        title
        fields(first: 300) {
          nodes {
            ... on TextField {
              type
              id
              label
              cssClass
              cssClassList
            }
            ... on TextAreaField {
              type
              id
              label
              cssClass
              cssClassList
            }
            ... on SelectField {
              type
              id
              label
              cssClass
              cssClassList
            }
          }
        }
      }
    }
  }
}

Future Feature Enhancements

  • Add support for backwards pagination of lists of entries.
  • Ability to query for lists of draft entries, or both entries and draft entries
  • Add support for updating draft entries with file upload data.
  • Ability to get the total count for a list of entries.
  • Ability to update an individual Gravity Forms entry.
  • Create & update integration tests.
  • Ability to create an individual Gravity Form.
  • Ability to update an individual Gravity Form.
  • Ability to delete an individual Gravity Form.

wp-graphql-gravity-forms's People

Contributors

kellenmace avatar mtdbyanechko avatar etylermoss avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.