Lesson 9: Inserting/Removing a (Gutenberg) block in bulk
We can update posts by modifying their (Gutenberg) block's HTML content.
Among other use cases, this is useful for promoting campaigns (such as when offering a discount during Black Friday):
- Before the campaign, we create a custom block
mycompany:black-friday-campaign-video
with our Call To Action, and execute a bulk operation to add it to all posts in the website - After the campaign, we execute a bulk operation to remove the block from all posts
For the GraphQL queries in this tutorial lesson to work, the Schema Configuration applied to the endpoint needs to have Nested Mutations enabled
Inserting a block in bulk
This GraphQL query identifies the 3rd paragraph block in a post (by searching for <!-- /wp:paragraph -->
) and places the custom block's HTML content right after it:
mutation InjectBlock(
$limit: Int! = 5,
$offset: Int! = 0
) {
posts: posts(
pagination: { limit: $limit, offset: $offset }
sort: { by: ID, order: ASC }
) {
id
rawContent
adaptedRawContent: _strRegexReplace(
in: $__rawContent,
searchRegex: "#(<!-- /wp:paragraph -->[\\s\\S]+<!-- /wp:paragraph -->[\\s\\S]+<!-- /wp:paragraph -->)#U",
replaceWith: "$1<!-- mycompany:black-friday-campaign-video -->\n<figure class=\"wp-block-video\"><video controls src=\"https://mysite.com/videos/BlackFriday2023.mp4\"></video></figure>\n<!-- /mycompany:black-friday-campaign-video -->",
limit: 1
)
update(input: {
contentAs: { html: $__adaptedRawContent },
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
id
rawContent
}
}
}
}
Inserting the block with more options
This GraphQL query, similar to the previous one, generates the regex dynamically, allowing us to input the block type to search for, and after how many such blocks to place the custom block:
query CreateRegex(
$searchBlockType: String! = "wp:paragraph"
$injectAfterBlockCount: Int!
$injectBlockMarkup: String!
) {
endingBlockMarkup: _sprintf(
string: "<!-- /%s -->",
values: [$searchBlockType]
)
@remove
endingBlockMarkupArray: _arrayPad(
array: [],
length: $injectAfterBlockCount,
value: $__endingBlockMarkup
)
@remove
regexString: _arrayJoin(
array: $__endingBlockMarkupArray,
separator: "[\\s\\S]+"
)
@remove
regex: _sprintf(
string: "#(%s)#U",
values: [$__regexString]
)
@export(as: "regex")
@remove
replaceWith: _sprintf(
string: "$1%s",
values: [$injectBlockMarkup]
)
@export(as: "replaceWith")
@remove
}
mutation InjectBlock(
$limit: Int! = 5,
$offset: Int! = 0
$times: Int! = 1
)
@depends(on: "CreateRegex")
{
posts: posts(
pagination: { limit: $limit, offset: $offset }
sort: { by: ID, order: ASC }
) {
id
rawContent
adaptedRawContent: _strRegexReplace(
in: $__rawContent,
searchRegex: $regex,
replaceWith: $replaceWith,
limit: $times
)
update(input: {
contentAs: { html: $__adaptedRawContent },
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
id
rawContent
}
}
}
}
We provide the variables
dictionary like this:
{
"injectBlockMarkup": "<!-- mycompany:black-friday-campaign-video -->\n<figure class=\"wp-block-video\"><video controls src=\"https://mysite.com/videos/BlackFriday2023.mp4\"></video></figure>\n<!-- /mycompany:black-friday-campaign-video -->",
"injectAfterBlockCount": 3
}
- During development/testing of the GraphQL query, print the regex patterns in the response by placing
#
before the@remove
directives (as to comment them out):
{
field
# @remove <= Adding "#" before will disable the directive
}
Removing a block in bulk
This GraphQL query searches for all posts containing the custom block, and removes it from their HTML source:
mutation RemoveBlock {
posts(filter: { search: "\"<!-- /mycompany:black-friday-campaign-video -->\"" } ) {
id
rawContent
adaptedRawContent: _strRegexReplace(
in: $__rawContent,
searchRegex: "#(<!-- mycompany:black-friday-campaign-video -->[\\s\\S]+<!-- /mycompany:black-friday-campaign-video -->)#",
replaceWith: ""
)
update(input: {
contentAs: { html: $__adaptedRawContent },
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
id
rawContent
}
}
}
}
Removing a block with more options
This GraphQL query, similar to the previous one, generates the regex dynamically, allowing us to input the block type to be removed:
query CreateVars(
$removeBlockType: String!
) {
regex: _sprintf(
string: "#(<!-- %1$s -->[\\s\\S]+<!-- /%1$s -->)#",
values: [$removeBlockType]
)
@export(as: "regex")
@remove
search: _sprintf(
string: "\"<!-- /%1$s -->\"",
values: [$removeBlockType]
)
@export(as: "search")
@remove
}
mutation RemoveBlock
@depends(on: "CreateVars")
{
posts(filter: { search: $search } ) {
id
rawContent
adaptedRawContent: _strRegexReplace(
in: $__rawContent,
searchRegex: $regex,
replaceWith: ""
)
update(input: {
contentAs: { html: $__adaptedRawContent },
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
id
rawContent
}
}
}
}
We provide the variables
dictionary like this:
{
"removeBlockType": "mycompany:black-friday-campaign-video"
}