Duplicating multiple blog posts at once
We can extend the previous recipe, to duplicate multiple posts with a single GraphQL request.
GraphQL query to duplicate multiple posts at once permalink
This GraphQL query duplicates the posts retrieved via the provided $limit
and $offset
variables:
query InitializeDynamicVariables
@configureWarningsOnExportingDuplicateVariable(enabled: false)
{
postInput: _echo(value: [])
@export(as: "postInput")
@remove
}
query GetPostsAndExportData($limit: Int! = 5, $offset: Int! = 0)
@depends(on: "InitializeDynamicVariables")
{
postsToDuplicate: posts(
pagination: {
limit : $limit
offset: $offset
}
sort: {
by: ID,
order: ASC
}
) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
# Already create (and export) the inputs for the mutation
postInput: _echo(value: {
status: draft,
authorBy: {
id: $__author
},
categoriesBy: {
ids: $__categories
},
contentAs: {
html: $__rawContent
},
excerpt: $__excerpt
featuredImageBy: {
id: $__featuredImage
},
tagsBy: {
ids: $__tags
},
title: $__title
})
@export(as: "postInput", type: LIST)
@remove
}
}
mutation DuplicatePosts
@depends(on: "GetPostsAndExportData")
{
createdPostIDs: _echo(value: $postInput)
@underEachArrayItem(
passValueOnwardsAs: "input"
)
@applyField(
name: "createPost"
arguments: {
input: $input
},
setResultInResponse: true
)
@export(as: "createdPostIDs")
}
query RetrieveCreatedPosts
@depends(on: "DuplicatePosts")
{
createdPosts: posts(
filter: {
ids: $createdPostIDs,
status: [draft]
}
) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
Step by step: creating the GraphQL query permalink
Below is the detailed analysis of how the query works.
Extending the "Duplicating a blog post" recipe permalink
The previous recipe employs the following strategy (on the GraphQL query of the second approach):
- Export the resource IDs from the fields (including connection fields):
query GetPostAndExportData {
post {
author @export(as: "authorID") {
id
}
categories @export(as: "categoryIDs") {
id
}
rawContent @export(as: "rawContent")
rawExcerpt @export(as: "excerpt")
featuredImage @export(as: "featuredImageID") {
id
}
tags @export(as: "tagIDs") {
id
}
rawTitle @export(as: "title")
}
}
- Create the input object for
createPost(input:)
from those dynamic variables:
mutation DuplicatePost
@depends(on: "GetPostAndExportData")
{
createPost(input: {
status: draft,
authorBy: {
id: $authorID
},
categoriesBy: {
ids: $categoryIDs
},
contentAs: {
html: $rawContent
},
excerpt: $excerpt
featuredImageBy: {
id: $featuredImageID
},
tagsBy: {
ids: $tagIDs
},
title: $title
}) {
# ...
}
}
Thanks to the Field to Input extension, we can create the input object already on the first operation, and export all the required post data under a single dynamic variable:
query GetPostAndExportData {
post {
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
postInput: _echo(value: {
status: draft,
authorBy: {
id: $__author
},
categoriesBy: {
ids: $__categories
},
contentAs: {
html: $__rawContent
},
excerpt: $__excerpt
featuredImageBy: {
id: $__featuredImage
},
tagsBy: {
ids: $__tags
},
title: $__title
})
@export(as: "postInput")
}
}
Then, in the following mutation, createPost(input:)
directly receives dynamic variable $postInput
:
mutation DuplicatePost
@depends(on: "GetPostAndExportData")
{
createPost(input: $postInput) {
# ...
}
}
Retrieving multiple posts permalink
We must convert the query to retrieve the multiple posts to be duplicated:
- Query the posts via
posts(pagination: { limit : $limit, offset: $offset}) { ... }
- Export
postInput
as a list (i.e. an array containing all the inputs for the queried posts)
query GetPostsAndExportData($limit: Int! = 5, $offset: Int! = 0)
@depends(on: "InitializeDynamicVariables")
{
postsToDuplicate: posts(
pagination: {
limit : $limit
offset: $offset
}
sort: {
by: ID,
order: ASC
}
) {
# ...
postInput: _echo(value: {
# ...
})
@export(
as: "postInput",
type: LIST
)
}
}
Creating multiple posts in a single GraphQL query permalink
Dynamic variable $postInput
by now contains an array with all the input data for each of the posts to duplicate:
[
{
"status": "draft",
"authorBy": {
"id": "2"
},
"categoryIDs": [
1
],
"contentAs": {
"html": "<!-- wp:paragraph -->\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>\n<!-- /wp:paragraph -->"
},
"excerpt": "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!",
"featuredImageBy": {
"id": null
},
"tagsBy": {
"ids": []
},
"title": "Hello world!"
},
{
"status": "draft",
"authorBy": {
"id": "3"
},
"categoryIDs": [
3
],
"contentAs": {
"html": "<!-- wp:paragraph -->\n<p>This is a paragraph block. Professionally productize highly efficient results with world-class core competencies. Objectively matrix leveraged architectures vis-a-vis error-free applications. Completely maximize customized portals via fully researched metrics. Enthusiastically generate premier action items through web-enabled e-markets. Efficiently parallel task holistic intellectual capital and client-centric markets.<br><br></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2>Image Block (Standard)</h2>\n<!-- /wp:heading -->\n\n<!-- wp:image {\"id\":1755} -->\n<figure class=\"wp-block-image\"><img src=\"https://d.pr/i/8pTmgY+\" alt=\"\" class=\"wp-image-1755\"/></figure>\n<!-- /wp:image -->"
},
"excerpt": "This is a paragraph block. Professionally productize highly efficient results with world-class core competencies. Objectively matrix leveraged architectures vis-a-vis error-free applications. Completely maximize customized portals via fully researched metrics. Enthusiastically generate premier action items through web-enabled e-markets. Efficiently parallel task holistic intellectual capital and client-centric markets. Image Block (Standard)",
"featuredImageBy": {
"id": 361
},
"tagsBy": {
"ids": [
11,
10
]
},
"title": "Released v0.6, check it out"
}
]
In the Gato GraphQL schema, there is no mutation to create multiple posts:
- Mutation
createPost
receives a singleinput
object, not an array of them - There is no mutation
createPosts
The solution is to use extensions:
- Field Value Iteration and Manipulation provides directive
@underEachArrayItem
to iterate over all the items in$postInput
- Field on Field provides directive
@applyField
, to apply thecreatePost
mutation on each iterated-upon item from the array
🔥 Tips:
@underEachArrayItem
is a composable directive (or "meta directive", it is a directive which can contain nested directives) that iterates over an array of elements, and applies its nested directive on each of them.
@underEachArrayItem
helps bridge GraphQL types, as it can make a field that returns a [String]
value, be applied a directive that receives a String
value as input (or other combinations).
For instance, in the query below:
- Field
User.capabilities
returns[String]
- Directive
@strUpperCase
receivesString
Thanks to @underEachArrayItem
, we can convert all capability items to upper case:
{
user(by: { id: 3 }) {
capabilities
@underEachArrayItem
@strUpperCase
}
}
...producing:
{
"data": {
"user": {
"capabilities": [
"LEVEL_0",
"READ",
"READ_PRIVATE_EVENTS",
"READ_PRIVATE_LOCATIONS"
]
}
}
}
This GraphQL query passes iterates all items in $postInput
, assigns each of them to dynamic variable $input
, injects into the createPost
mutation, and finally exports the IDs of the created posts under dynamic variable $createdPostIDs
:
mutation DuplicatePosts
@depends(on: "GetPostsAndExportData")
{
createdPostIDs: _echo(value: $postInput)
@underEachArrayItem(
passValueOnwardsAs: "input"
)
@applyField(
name: "createPost"
arguments: {
input: $input
},
setResultInResponse: true
)
@export(as: "createdPostIDs")
}
⚙️ Configuration alert:
For this GraphQL query to work, the Schema Configuration applied to the endpoint needs to have the following configuration:
- In block Payload Types for Mutations, select "Do not use Payload Types for Mutations (i.e. return the mutated entity)" (so that dynamic variable
$createdPostIDs
will contain the IDs of the created posts) - In block Mutation Scheme, select any of the two "Enable Nested Mutations" options (as to use field
_echo
inside amutation
)
Finally, we can use dynamic variable $createdPostIDs
to retrieve the data for the newly-created posts:
query RetrieveCreatedPosts
@depends(on: "DuplicatePosts")
{
createdPosts: posts(
filter: {
ids: $createdPostIDs,
status: [draft]
}
) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
Removing unneeded data permalink
The final step is to remove all fields that are auxiliary (and as such we don't need to print their output in the response) via @remove
.
The consolidated GraphQL query is:
query InitializeDynamicVariables
@configureWarningsOnExportingDuplicateVariable(enabled: false)
{
postInput: _echo(value: [])
@export(as: "postInput")
@remove
}
query GetPostsAndExportData($limit: Int! = 5, $offset: Int! = 0)
@depends(on: "InitializeDynamicVariables")
{
postsToDuplicate: posts(
pagination: {
limit : $limit
offset: $offset
}
sort: {
by: ID,
order: ASC
}
) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
# Already create (and export) the inputs for the mutation
postInput: _echo(value: {
status: draft,
authorBy: {
id: $__author
},
categoriesBy: {
ids: $__categories
},
contentAs: {
html: $__rawContent
},
excerpt: $__excerpt
featuredImageBy: {
id: $__featuredImage
},
tagsBy: {
ids: $__tags
},
title: $__title
})
@export(as: "postInput", type: LIST)
@remove
}
}
mutation DuplicatePosts
@depends(on: "GetPostsAndExportData")
{
createdPostIDs: _echo(value: $postInput)
@underEachArrayItem(
passValueOnwardsAs: "input"
)
@applyField(
name: "createPost"
arguments: {
input: $input
},
setResultInResponse: true
)
@export(as: "createdPostIDs")
}
query RetrieveCreatedPosts
@depends(on: "DuplicatePosts")
{
createdPosts: posts(
filter: {
ids: $createdPostIDs,
status: [draft]
}
) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
Extensions referenced in this recipe permalink
- Field on Field
- Field Response Removal
- Field To Input
- Field Value Iteration and Manipulation
- Multiple Query Execution
- PHP Functions via Schema
(They are all included in “Application Glue & Automator” Bundle, “Content Translation” Bundle, “All Extensions” Bundle)