스키마 튜토리얼레슨 13: 게시물의 모든 Image 블록에서 이미지 URL을 수정하고 (다시) 저장하기
레슨 13: 게시물의 모든 Image 블록에서 이미지 URL을 수정하고 (다시) 저장하기
이전 튜토리얼 레슨에서는 게시물 콘텐츠 내 (Gutenberg) 블록의 내부 구조를 반복 처리하여 원하는 항목을 추출할 수 있다는 것을 배웠습니다.
이제 해당 항목들을 변환하여 게시물에 다시 저장하는 방법을 살펴보겠습니다.
이 튜토리얼 레슨의 쿼리는 게시물 내 core/image 블록에 있는 이미지 URL을 다음과 같이 수정합니다:
mysite.com을cdn.mysite.com으로 교체 (해당 에셋을 CDN에서 제공하기 위해).jpg를.avif로 교체
게시물의 모든 core/image 블록에서 이미지 URL을 변환(및 재저장)하는 GraphQL 쿼리
뮤테이션 updatePost는 게시물의 HTML 콘텐츠를 받습니다. 따라서 다음 처리가 필요합니다:
- 게시물의
rawContent가져오기 - 해당 HTML 코드에 변환을 적용하여 원본 URL을 변환된 URL로 교체하기
- 변환된 콘텐츠 저장하기
아래 GraphQL 쿼리는 디렉티브 @strRegexReplaceMultiple을 통해 HTML 코드 내 변환을 실행합니다. 각 정규식 패턴은 블록의 내부 HTML 콘텐츠(이 경우, core/image 블록의 HTML 코드 내 src 요소를 대상으로 함)를 기반으로 동적으로 생성됩니다. 생성된 정규식 패턴에는 정규식 특수 문자(., +, ( 등)가 포함될 수 있고, 교체 문자열에는 정규식 교체 변수($1 등)가 포함될 수 있으므로, 이들은 반드시 이스케이프 처리되어야 합니다.
query InitializeEmptyVariables {
emptyArray: _echo(value: [])
@export(as: "coreImageURLItems")
@export(as: "coreImageURLReplacementsFrom")
@export(as: "coreImageURLReplacementsTo")
@remove
}
# Extract all the image URLs from the `core/image` blocks, and export them under `$coreImageURLItems`
query FetchData($postID: ID!)
@configureWarningsOnExportingDuplicateVariable(enabled: false)
@depends(on: "InitializeEmptyVariables")
{
post(by: { id: $postID } ) {
rawContent
@export(as: "rawContent")
coreImage: blockFlattenedDataItems(
filterBy: { include: "core/image" }
)
@underEachArrayItem
@underJSONObjectProperty(
by: { key: "attributes" }
)
@underJSONObjectProperty(
by: { key: "url" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "coreImageURLItems"
)
}
}
# Convert the URLs and place the results under `$transformations`
query TransformData
@depends(on: "FetchData")
{
transformations: _echo(value: {
coreImageURL: {
from: $coreImageURLItems,
to: $coreImageURLItems,
},
})
@underEachJSONObjectProperty
@underJSONObjectProperty(by: { key: "to" })
@underEachArrayItem
@strRegexReplace(
searchRegex: "#^https?://mysite.com/(.*)\\.jpg$#",
replaceWith: "https://cdn.mysite.com/$1.avif"
)
@export(as: "transformations")
}
# Escape the regex patterns and their replacements
query EscapeRegexStrings
@depends(on: "TransformData")
{
escapedRegexStrings: _echo(value: $transformations)
@underEachJSONObjectProperty
@underJSONObjectProperty(by: { key: "from" })
@underEachArrayItem
@strQuoteRegex
@underEachJSONObjectProperty
@underJSONObjectProperty(
by: { key: "to" }
affectDirectivesUnderPos: [1, 3],
)
@underEachArrayItem
@strRegexReplace(
searchRegex: "#\\$(\\d+)#",
replaceWith: "\\\\\\$1"
)
@underEachArrayItem(
passValueOnwardsAs: "value"
)
@applyField(
name: "_sprintf",
arguments: {
string: "$1%s$2",
values: [$value]
},
setResultInResponse: true
)
@export(as: "escapedRegexTransformations")
}
# Generate the regex patterns, and assign them to `$coreImageURLReplacementsFrom`
query CreateRegexReplacements
@depends(on: "EscapeRegexStrings")
{
regexReplacements: _echo(value: $escapedRegexTransformations)
@underJSONObjectProperty(
by: { key: "coreImageURL" }
affectDirectivesUnderPos: [1, 5]
)
@underJSONObjectProperty(
by: { key: "from" }
affectDirectivesUnderPos: [1, 3],
)
@underEachArrayItem(
passValueOnwardsAs: "value"
)
@applyField(
name: "_sprintf",
arguments: {
string: "#(<!-- wp:image .*?-->\\n?.*<img .*?src=\\\")%s(\\\".*>.*\\n?<!-- /wp:image -->)#",
values: [$value]
},
setResultInResponse: true
)
@export(
as: "coreImageURLReplacementsFrom",
)
@underJSONObjectProperty(
by: { key: "to" }
)
@export(
as: "coreImageURLReplacementsTo",
)
}
# Execute the regex search and replace, export the results under `$transformedRawContent`
query ExecuteRegexReplacements
@depends(on: "CreateRegexReplacements")
{
transformedRawContent: _echo(value: $rawContent)
@strRegexReplaceMultiple(
limit: 1,
searchRegex: $coreImageURLReplacementsFrom,
replaceWith: $coreImageURLReplacementsTo
)
@export(as: "transformedRawContent")
}
# Execute the mutation to update the post
mutation ModifyAndUpdatePost($postID: ID!)
@depends(on: "ExecuteRegexReplacements")
{
updatePost(input: {
id: $postID,
contentAs: {
html: $transformedRawContent
}
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
id
title
rawContent
}
}
}응답은 다음과 같습니다:
{
"data": {
"post": {
"rawContent": "<!-- wp:paragraph -->\n<p>This is a paragraph block.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Image Block (Standard)</h2>\n<!-- /wp:heading -->\n\n<!-- wp:image {\"sizeSlug\":\"large\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"http://mysite.com/fs_img/mysite/2008/themes_photo.jpg\" alt=\"\"/></figure>\n<!-- /wp:image -->\n\n<!-- wp:image {\"sizeSlug\":\"large\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"http://mysite.com/fs_img/mysite/2007/email_photo.jpg\" alt=\"\"/></figure>\n<!-- /wp:image -->",
"coreImage": [
{
"name": "core/image",
"attributes": {
"sizeSlug": "large",
"url": "http://mysite.com/fs_img/mysite/2008/themes_photo.jpg",
"alt": ""
}
},
{
"name": "core/image",
"attributes": {
"sizeSlug": "large",
"url": "http://mysite.com/fs_img/mysite/2007/email_photo.jpg",
"alt": ""
}
}
]
},
"transformations": {
"coreImageURL": {
"from": [
"http://mysite.com/fs_img/mysite/2008/themes_photo.jpg",
"http://mysite.com/fs_img/mysite/2007/email_photo.jpg"
],
"to": [
"https://cdn.mysite.com/fs_img/mysite/2008/themes_photo.avif",
"https://cdn.mysite.com/fs_img/mysite/2007/email_photo.avif"
]
}
},
"escapedRegexStrings": {
"coreImageURL": {
"from": [
"http://mysite\\.com/fs_img/mysite/2008/themes_photo\\.jpg",
"http://mysite\\.com/fs_img/mysite/2007/email_photo\\.jpg"
],
"to": [
"$1https://cdn.mysite.com/fs_img/mysite/2008/themes_photo.avif$2",
"$1https://cdn.mysite.com/fs_img/mysite/2007/email_photo.avif$2"
]
}
},
"regexReplacements": {
"coreImageURL": {
"from": [
"#(<!-- wp:image .*?-->\\n?.*<img .*?src=\\\")http://mysite\\.com/fs_img/mysite/2008/themes_photo\\.jpg(\\\".*>.*\\n?<!-- /wp:image -->)#",
"#(<!-- wp:image .*?-->\\n?.*<img .*?src=\\\")http://mysite\\.com/fs_img/mysite/2007/email_photo\\.jpg(\\\".*>.*\\n?<!-- /wp:image -->)#"
],
"to": [
"$1https://cdn.mysite.com/fs_img/mysite/2008/themes_photo.avif$2",
"$1https://cdn.mysite.com/fs_img/mysite/2007/email_photo.avif$2"
]
}
},
"transformedRawContent": "<!-- wp:paragraph -->\n<p>This is a paragraph block.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Image Block (Standard)</h2>\n<!-- /wp:heading -->\n\n<!-- wp:image {\"sizeSlug\":\"large\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https://cdn.mysite.com/fs_img/mysite/2008/themes_photo.avif\" alt=\"\"/></figure>\n<!-- /wp:image -->\n\n<!-- wp:image {\"sizeSlug\":\"large\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https://cdn.mysite.com/fs_img/mysite/2007/email_photo.avif\" alt=\"\"/></figure>\n<!-- /wp:image -->",
"updatePost": {
"status": "SUCCESS",
"errors": null,
"post": {
"id": 13,
"title": "Released v0.6, check it out",
"rawContent": "<!-- wp:paragraph -->\n<p>This is a paragraph block.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Image Block (Standard)</h2>\n<!-- /wp:heading -->\n\n<!-- wp:image {\"sizeSlug\":\"large\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https://cdn.mysite.com/fs_img/mysite/2008/themes_photo.avif\" alt=\"\"/></figure>\n<!-- /wp:image -->\n\n<!-- wp:image {\"sizeSlug\":\"large\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https://cdn.mysite.com/fs_img/mysite/2007/email_photo.avif\" alt=\"\"/></figure>\n<!-- /wp:image -->"
}
}
}
}