Bài 3: Nhân bản bài viết blog
Nhân bản một bài viết là ví dụ về khả năng truy xuất, xử lý và lưu lại dữ liệu trên site của Gato GraphQL.
GraphQL query để nhân bản bài viết blog
GraphQL query này nhân bản bài viết được chỉ định bởi biến $postId:
query InitializeDynamicVariables
@configureWarningsOnExportingDuplicateVariable(enabled: false)
{
authorID: _echo(value: null)
@export(as: "authorID")
@remove
categoryIDs: _echo(value: [])
@export(as: "categoryIDs")
@remove
featuredImageID: _echo(value: null)
@export(as: "featuredImageID")
@remove
tagIDs: _echo(value: [])
@export(as: "tagIDs")
@remove
}
query GetPostAndExportData($postId: ID!)
@depends(on: "InitializeDynamicVariables")
{
post(by: { id : $postId }) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id @export(as: "authorID")
}
categories {
id @export(as: "categoryIDs", type: LIST)
}
rawContent @export(as: "rawContent")
rawExcerpt @export(as: "excerpt")
featuredImage {
id @export(as: "featuredImageID")
}
tags {
id @export(as: "tagIDs", type: LIST)
}
rawTitle @export(as: "title")
}
}
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
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
}Từng bước: tạo GraphQL query
Dưới đây là phân tích chi tiết về cách query hoạt động.
Truy xuất dữ liệu bài viết
GraphQL query này truy xuất dữ liệu cơ bản của một bài viết:
query GetPost($postId: ID!) {
post(by: { id : $postId }) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}Khi thực thi query (truyền biến $postId), phản hồi có thể là:
{
"data": {
"post": {
"id": 25,
"slug": "public-or-private-api-mode-for-extra-security",
"date": "2020-12-12T04:06:52+00:00",
"author": {
"id": 2
},
"categories": [
{
"id": 4
},
{
"id": 3
},
{
"id": 2
}
],
"rawContent": "<!-- wp:heading -->\n<h2>Verse Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:verse -->\n<pre class=\"wp-block-verse\">Write poetry and other literary expressions honoring all spaces and line-breaks.</pre>\n<!-- /wp:verse -->\n\n<!-- wp:heading -->\n<h2>Table Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:table {\"className\":\"is-style-stripes\"} -->\n<figure class=\"wp-block-table is-style-stripes\"><table><tbody><tr><td>Row 1 Column 1</td><td>Row 1 Column 2</td></tr><tr><td>Row 2 Column 1</td><td>Row 2 Column 2</td></tr><tr><td>Row 3 Column 1</td><td>Row 3 Column 2</td></tr></tbody></table></figure>\n<!-- /wp:table -->\n\n<!-- wp:heading -->\n<h2>Separator Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:separator -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Spacer Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:spacer -->\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n<!-- /wp:spacer -->",
"excerpt": "Verse Block Write poetry and other literary expressions honoring all spaces and line-breaks. Table Block Row 1 Column 1 Row 1 Column 2 Row 2 Column 1 Row 2 Column 2 Row 3 Column 1 Row 3 Column 2 Separator Block Spacer Block",
"featuredImage": {
"id": 362
},
"tags": [
{
"id": 12
},
{
"id": 7
}
],
"title": "Public or Private API mode, for extra security"
}
}
}Lưu ý rằng một số trường được dùng để nhân bản (bao gồm tác giả, tiêu đề và nội dung), trong khi một số khác thì không (như id, slug và ngày tạo).
Nhân bản bài viết: Cách tiếp cận thứ nhất
Với extension Multiple Query Execution, chúng ta có thể xuất các mục dữ liệu của bài viết và đưa chúng vào một query hoặc mutation khác trong cùng một tài liệu GraphQL.
Multiple Query Execution cho phép chúng ta thực thi chức năng phức tạp trong một request duy nhất và tổ chức logic tốt hơn bằng cách chia tài liệu GraphQL thành một loạt các đơn vị logic/nguyên tử:
- Không có giới hạn về số lượng thao tác có thể thêm vào pipeline
- Bất kỳ thao tác nào cũng có thể khai báo nhiều hơn một phụ thuộc:
query SomeQuery @depends(on: ["SomePreviousOp", "AnotherPreviousOp"]) {
# ...
}- Bất kỳ thao tác nào cũng có thể phụ thuộc vào một thao tác khác, mà thao tác đó lại phụ thuộc vào một thao tác khác nữa, và cứ tiếp tục như vậy:
query ExecuteFirst
# ...
}
query ExecuteSecond @depends(on: ["ExecuteFirst"]) {
# ...
}
query ExecuteThird @depends(on: ["ExecuteSecond"]) {
# ...
}-
Chúng ta có thể thực thi bất kỳ thao tác nào trong tài liệu:
?operationName=ExecuteThirdthực thiExecuteFirst>ExecuteSecond>ExecuteThird?operationName=ExecuteSecondthực thiExecuteFirst>ExecuteSecond?operationName=ExecuteFirstthực thiExecuteFirst
-
Khi
@dependsnhận chỉ một thao tác, nó có thể nhận mộtString(thay vì[String]):
query ExecuteFirst
# ...
}
query ExecuteSecond @depends(on: "ExecuteFirst") {
# ...
}- Cả thao tác
queryvàmutationđều có thể phụ thuộc vào nhau:
query GetAndExportData
# ...
}
mutation MutateData @depends(on: "GetAndExportData") {
# ...
}
query CountMutatedResults @depends(on: "MutateData") {
# ...
}- Biến động không cần được khai báo trong thao tác
- Thông qua đầu vào
@export(type:)chúng ta có thể chọn đầu ra của dữ liệu được xuất vào biến động:SINGLE(mặc định): Giá trị của một trường duy nhấtLIST: Một mảng chứa giá trị trường của nhiều tài nguyênDICTIONARY: Một từ điển chứa giá trị trường của nhiều tài nguyên, với khóa:${resource ID}và giá trị:${field value}
Query sau tạo một pipeline gồm hai thao tác trong tài liệu GraphQL (GetPostAndExportData và DuplicatePost), có thể chia sẻ dữ liệu với nhau:
DuplicatePostchỉ định thực thiGetPostAndExportDatatrước, thông qua directive@dependsGetPostAndExportDataxuất dữ liệu thông qua directive@exportvào các biến độngDuplicatePostsau đó đọc các biến động và đưa chúng vào mutationcreatePost
query GetPostAndExportData($postId: ID!) {
post(by: { id : $postId }) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id @export(as: "authorID")
}
categories {
id @export(as: "categoryIDs", type: LIST)
}
rawContent @export(as: "rawContent")
rawExcerpt @export(as: "excerpt")
featuredImage {
id @export(as: "featuredImageID")
}
tags {
id @export(as: "tagIDs", type: LIST)
}
rawTitle @export(as: "title")
}
}
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
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
}Trong phản hồi, chúng ta có thể thấy rằng các trường của bài viết mới thực sự giống nhau:
{
"data": {
"post": {
"id": 25,
"slug": "public-or-private-api-mode-for-extra-security",
"date": "2020-12-12T04:06:52+00:00",
"status": "publish",
"author": {
"id": 2
},
"categories": [
{
"id": 4
},
{
"id": 3
},
{
"id": 2
}
],
"rawContent": "<!-- wp:heading -->\n<h2>Verse Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:verse -->\n<pre class=\"wp-block-verse\">Write poetry and other literary expressions honoring all spaces and line-breaks.</pre>\n<!-- /wp:verse -->\n\n<!-- wp:heading -->\n<h2>Table Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:table {\"className\":\"is-style-stripes\"} -->\n<figure class=\"wp-block-table is-style-stripes\"><table><tbody><tr><td>Row 1 Column 1</td><td>Row 1 Column 2</td></tr><tr><td>Row 2 Column 1</td><td>Row 2 Column 2</td></tr><tr><td>Row 3 Column 1</td><td>Row 3 Column 2</td></tr></tbody></table></figure>\n<!-- /wp:table -->\n\n<!-- wp:heading -->\n<h2>Separator Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:separator -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Spacer Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:spacer -->\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n<!-- /wp:spacer -->",
"excerpt": "Verse Block Write poetry and other literary expressions honoring all spaces and line-breaks. Table Block Row 1 Column 1 Row 1 Column 2 Row 2 Column 1 Row 2 Column 2 Row 3 Column 1 Row 3 Column 2 Separator Block Spacer Block",
"featuredImage": {
"id": 362
},
"tags": [
{
"id": 12
},
{
"id": 7
}
],
"title": "Public or Private API mode, for extra security"
},
"createPost": {
"status": "SUCCESS",
"errors": null,
"post": {
"id": 1207,
"slug": "public-or-private-api-mode-for-extra-security-2",
"date": "2023-07-07T02:06:17+00:00",
"status": "draft",
"author": {
"id": 2
},
"categories": [
{
"id": 4
},
{
"id": 3
},
{
"id": 2
}
],
"rawContent": "<!-- wp:heading -->\n<h2>Verse Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:verse -->\n<pre class=\"wp-block-verse\">Write poetry and other literary expressions honoring all spaces and line-breaks.</pre>\n<!-- /wp:verse -->\n\n<!-- wp:heading -->\n<h2>Table Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:table {\"className\":\"is-style-stripes\"} -->\n<figure class=\"wp-block-table is-style-stripes\"><table><tbody><tr><td>Row 1 Column 1</td><td>Row 1 Column 2</td></tr><tr><td>Row 2 Column 1</td><td>Row 2 Column 2</td></tr><tr><td>Row 3 Column 1</td><td>Row 3 Column 2</td></tr></tbody></table></figure>\n<!-- /wp:table -->\n\n<!-- wp:heading -->\n<h2>Separator Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:separator -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Spacer Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:spacer -->\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n<!-- /wp:spacer -->",
"excerpt": "Verse Block Write poetry and other literary expressions honoring all spaces and line-breaks. Table Block Row 1 Column 1 Row 1 Column 2 Row 2 Column 1 Row 2 Column 2 Row 3 Column 1 Row 3 Column 2 Separator Block Spacer Block",
"featuredImage": {
"id": 362
},
"tags": [
{
"id": 12
},
{
"id": 7
}
],
"title": "Public or Private API mode, for extra security"
}
}
}
}Vấn đề với cách tiếp cận thứ nhất
Query ở trên sẽ trả về lỗi khi một trường kết nối rỗng, vì biến động sẽ không được xuất.
Ví dụ, khi bài viết cần nhân bản không có ảnh đại diện, trường featuredImage sẽ là null, và do đó id @export(as: "featuredImageID") sẽ không bao giờ được thực thi:
{
post {
featuredImage {
id @export(as: "featuredImageID")
}
}
}Do biến động $featuredImageID khi đó sẽ không tồn tại, phản hồi sẽ trả về lỗi:
{
"errors": [
{
"message": "No value has been exported for dynamic variable 'featuredImageID'",
"locations": [
{
"line": 39,
"column": 22
}
]
}
],
"data": {
// ...
}
}Hai cách tiếp cận sau đây giải quyết vấn đề này.
Nhân bản bài viết: Cách tiếp cận thứ hai
Các trường kết nối cũng lưu trữ một giá trị trong Gato GraphQL. Khi được giải quyết lần đầu, các trường này sẽ chứa ID của tài nguyên mà chúng trỏ đến (hoặc ID của tài nguyên được liên kết, hoặc một mảng với các ID của các tài nguyên được liên kết). Chỉ sau đó, khi kết nối được giải quyết, ID mới được thay thế bằng các đối tượng tài nguyên thực tế.
Ví dụ, trong query sau:
{
post {
featuredImage {
id
}
tags {
id
}
}
}...trường featuredImage ban đầu sẽ chứa 362 (đó là ID của ảnh đại diện), và trường tags sẽ chứa mảng [12, 7] (đó là các ID của thẻ).
Khi giá trị cần xuất là một ID (như $featuredImageID) hoặc mảng các ID (như $tagIDs), chúng ta có thể tận dụng đặc điểm này và đã xuất ID trong trường kết nối.
Thay vì làm thế này:
{
post {
featuredImage {
id @export(as: "featuredImageID")
}
tags {
id @export(as: "tagIDs", type: LIST)
}
}
}...chúng ta có thể làm thế này:
{
post {
featuredImage @export(as: "featuredImageID") {
id
}
tags @export(as: "tagIDs") {
id
}
}
}(Lưu ý rằng đối số type: LIST đã được loại bỏ khi xuất $tagIDs, vì trường kết nối đã là một danh sách.)
Bây giờ, các biến động này sẽ luôn được xuất, với giá trị:
nullcho$featuredImageIDkhi bài viết không có ảnh đại diện- mảng rỗng
[]cho$tagIDskhi bài viết không có thẻ
Điều chỉnh GraphQL query, nó trở thành:
query GetPostAndExportData($postId: ID!) {
post(by: { id : $postId }) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
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")
}
}
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
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
}...phản hồi bây giờ hoạt động đúng:
{
"data": {
"post": {
"id": 23,
"slug": "graphql-or-rest-you-can-have-both",
"date": "2020-12-12T04:04:54+00:00",
"status": "publish",
"author": {
"id": 2
},
"categories": [
{
"id": 1
}
],
"rawContent": "<!-- wp:heading -->\n<h2>Audio Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:audio -->\n<figure class=\"wp-block-audio\"><audio controls src=\"https://freemusicarchive.org/file/music/WFMU/Broke_For_Free/Directionless_EP/Broke_For_Free_-_01_-_Night_Owl.mp3\"></audio></figure>\n<!-- /wp:audio -->\n\n<!-- wp:heading -->\n<h2>Video Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:video -->\n<figure class=\"wp-block-video\"><video controls src=\"https://archive.org/download/SlowMotionFlame/slomoflame_512kb.mp4\"></video></figure>\n<!-- /wp:video -->\n\n<!-- wp:heading -->\n<h2>Custom HTML Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:html -->\n<strong>This is a HTML block.</strong>\n<!-- /wp:html -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Preformatted Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:preformatted -->\n<pre class=\"wp-block-preformatted\">This is some preformatted text. Preformatted text keeps your s p a c e s, tabs and<br>linebreaks as they are.</pre>\n<!-- /wp:preformatted -->",
"excerpt": "Audio Block Video Block Custom HTML Block This is a HTML block. Preformatted Block This is some preformatted text. Preformatted text keeps your s p a c e s, tabs andlinebreaks as they are.",
"featuredImage": null,
"tags": [],
"title": "GraphQL or REST? Why not both?"
},
"createPost": {
"status": "SUCCESS",
"errors": null,
"post": {
"id": 1209,
"slug": "graphql-or-rest-why-not-both",
"date": "2023-07-07T03:24:31+00:00",
"status": "draft",
"author": {
"id": 2
},
"categories": [
{
"id": 1
}
],
"rawContent": "<!-- wp:heading -->\n<h2>Audio Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:audio -->\n<figure class=\"wp-block-audio\"><audio controls src=\"https://freemusicarchive.org/file/music/WFMU/Broke_For_Free/Directionless_EP/Broke_For_Free_-_01_-_Night_Owl.mp3\"></audio></figure>\n<!-- /wp:audio -->\n\n<!-- wp:heading -->\n<h2>Video Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:video -->\n<figure class=\"wp-block-video\"><video controls src=\"https://archive.org/download/SlowMotionFlame/slomoflame_512kb.mp4\"></video></figure>\n<!-- /wp:video -->\n\n<!-- wp:heading -->\n<h2>Custom HTML Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:html -->\n<strong>This is a HTML block.</strong>\n<!-- /wp:html -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Preformatted Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:preformatted -->\n<pre class=\"wp-block-preformatted\">This is some preformatted text. Preformatted text keeps your s p a c e s, tabs and<br>linebreaks as they are.</pre>\n<!-- /wp:preformatted -->",
"excerpt": "Audio Block Video Block Custom HTML Block This is a HTML block. Preformatted Block This is some preformatted text. Preformatted text keeps your s p a c e s, tabs andlinebreaks as they are.",
"featuredImage": null,
"tags": [],
"title": "GraphQL or REST? Why not both?"
}
}
}
}Vấn đề với cách tiếp cận thứ hai
Giải pháp trên chỉ hoạt động để xuất các ID (vì đây là các giá trị được lưu trong các trường kết nối). Nó sẽ không hoạt động cho bất kỳ thứ gì khác, chẳng hạn như slug của thẻ:
{
post {
tags {
slug @export(as: "tagSlugs", type: LIST)
}
}
}Cách tiếp cận sau đây giải quyết vấn đề này.
Nhân bản bài viết: Cách tiếp cận thứ ba
Chúng ta có thể thực thi một thao tác bổ sung ở đầu để khởi tạo mỗi biến động với giá trị null hoặc rỗng (thông qua trường toàn cục _echo từ extension PHP Functions Via Schema).
Sau đó, mỗi biến động sẽ luôn được xuất, ít nhất một lần. Khi giá trị trường không rỗng, nó sẽ được xuất lại, và giá trị thứ hai này sẽ ghi đè lên giá trị đầu tiên.
Trong query này, biến động $tagSlugs được khởi tạo với một mảng rỗng, và sau đó sẽ được xuất lại nếu bài viết có slug:
query InitializeDynamicVariables {
tagSlugs: _echo(value: []) @export(as: "tagSlugs")
}
query ExportData
@depends(on: "InitializeDynamicVariables")
{
post {
tags {
slug @export(as: "tagSlugs", type: LIST)
}
}
}- Trường toàn cục
_echotrả về bất cứ thứ gì được cung cấp, bất kể kiểu dữ liệu của nó:
query {
string: _echo(value: "page")
int: _echo(value: 3)
bool: _echo(value: true)
jsonObject: _echo(value: {
name: "Robert"
surname: "Spencer"
})
null: _echo(value: null)
arrayOfString: _echo(value: ["something", "new"])
arrayOfInt: _echo(value: [1, 3, 5])
arrayOfArraysOfBool: _echo(value: [[true, false], [false]])
arrayOfMixed: _echo(value: [1, true, "string", [1, 3, 5], {key: "value"}])
}Giải pháp này toàn diện hơn giải pháp trước, vì nó hoạt động để xuất bất kỳ loại dữ liệu nào (dù là ID hay loại khác).
Điều chỉnh GraphQL query, nó trở thành:
query InitializeDynamicVariables {
authorID: _echo(value: null) @export(as: "authorID")
categoryIDs: _echo(value: []) @export(as: "categoryIDs")
featuredImageID: _echo(value: null) @export(as: "featuredImageID")
tagIDs: _echo(value: []) @export(as: "tagIDs")}
query GetPostAndExportData($postId: ID!)
@depends(on: "InitializeDynamicVariables")
{
post(by: { id : $postId }) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id @export(as: "authorID")
}
categories {
id @export(as: "categoryIDs", type: LIST)
}
rawContent @export(as: "rawContent")
rawExcerpt @export(as: "excerpt")
featuredImage {
id @export(as: "featuredImageID")
}
tags {
id @export(as: "tagIDs", type: LIST)
}
rawTitle @export(as: "title")
}
}
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
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
}Cảnh báo trong cách tiếp cận thứ ba
Mỗi khi một biến động được xuất nhiều hơn một lần, công cụ GraphQL theo mặc định sẽ thêm một cảnh báo vào phản hồi GraphQL:
{
"extensions": {
"warnings": [
{
"message": "Dynamic variable with name 'tagSlugs' had already been set, had its value overridden",
"locations": [
{
"line": 22,
"column": 21
}
]
}
]
},
"data": {
// ...
}
}Cách tiếp cận hợp nhất tiếp theo giải quyết cảnh báo này.
Nhân bản bài viết: Cách tiếp cận hợp nhất
Chúng ta sử dụng GraphQL query từ cách tiếp cận trước và tối ưu hóa nó bằng cách:
- Không hiển thị cảnh báo "biến động trùng lặp"
- Không in các giá trị cho các trường trong
InitializeDynamicVariablestrong phản hồi GraphQL (vì không cần thiết, chúng chỉ là các trường trợ giúp)
Chúng ta giải quyết các yếu tố này (theo thứ tự):
- Thêm directive
@configureWarningsOnExportingDuplicateVariable(enabled: false)vào thao tác, điều này vô hiệu hóa việc hiển thị cảnh báo - Thêm directive
@remove(từ extension Field Response Removal) vào mỗi trường cần loại bỏ
Đây là GraphQL query hợp nhất để nhân bản một bài viết:
query InitializeDynamicVariables
@configureWarningsOnExportingDuplicateVariable(enabled: false)
{
authorID: _echo(value: null)
@export(as: "authorID")
@remove
categoryIDs: _echo(value: [])
@export(as: "categoryIDs")
@remove
featuredImageID: _echo(value: null)
@export(as: "featuredImageID")
@remove
tagIDs: _echo(value: [])
@export(as: "tagIDs")
@remove
}
query GetPostAndExportData($postId: ID!)
@depends(on: "InitializeDynamicVariables")
{
post(by: { id : $postId }) {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id @export(as: "authorID")
}
categories {
id @export(as: "categoryIDs", type: LIST)
}
rawContent @export(as: "rawContent")
rawExcerpt @export(as: "excerpt")
featuredImage {
id @export(as: "featuredImageID")
}
tags {
id @export(as: "tagIDs", type: LIST)
}
rawTitle @export(as: "title")
}
}
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
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
# Fields not to be duplicated
id
slug
date
status
# Fields to be duplicated
author {
id
}
categories {
id
}
rawContent
excerpt
featuredImage {
id
}
tags {
id
}
title
}
}
}