บทช่วยสอน Schema
บทช่วยสอน Schemaบทเรียนที่ 5: การปรับแต่งเนื้อหาสำหรับผู้ใช้ที่แตกต่างกัน

บทเรียนที่ 5: การปรับแต่งเนื้อหาสำหรับผู้ใช้ที่แตกต่างกัน

เราสามารถดึงค่าที่ตอบกลับในฟิลด์ให้แตกต่างกันได้ตามข้อมูลบางส่วนที่ query มา เช่น บทบาทของผู้ใช้ที่ล็อกอินอยู่

GraphQL query เพื่อปรับแต่งเนื้อหาสำหรับผู้ใช้ที่แตกต่างกัน

GraphQL query นี้จะดึงเนื้อหาของโพสต์ และเพิ่มลิงก์ "Edit this post" ไว้ที่ท้ายเนื้อหาสำหรับผู้ใช้ที่เป็น admin เท่านั้น:

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
      value: "administrator",
      array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}

สำหรับผู้ใช้ที่เป็น admin ค่าที่ตอบกลับจะเป็น:

{
  "data": {
    "user": {
      "isAdminUser": true
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n<p><a href=\"https:\/\/mysite.com\/wp-admin\/post.php?post=1&amp;action=edit\">(Admin only) Edit post<\/a><\/p>"
    }
  }
}

สำหรับผู้ใช้ที่ไม่ใช่ admin ค่าที่ตอบกลับจะเป็น:

{
  "data": {
    "user": {
      "isAdminUser": false
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n"
    }
  }
}

การให้ GraphQL server คำนวณค่าที่ฟิลด์ต้องการแบบไดนามิก (โดยพิจารณาจากเงื่อนไขที่เป็นไปได้ทั้งหมด):

  • ทำให้ตรรกะของแอปพลิเคชันเรียบง่ายขึ้น เพราะมีแหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียว โค้ดเป็น DRY และไคลเอนต์ไม่จำเป็นต้องเขียนตรรกะที่สอดคล้องกันนั้นเองอีกต่อไป
  • ทำให้แอปพลิเคชันเชื่อถือได้มากขึ้น โดยเฉพาะเมื่อมีไคลเอนต์หลายตัวเข้าถึงข้อมูลจาก server เพราะการนำตรรกะเดียวกันไปใช้งานในแบบที่ต่างกันอาจไม่เหมือนกัน ซึ่งอาจนำไปสู่บั๊กได้ (ยิ่งเป็นเช่นนั้นเมื่อไคลเอนต์ใช้เทคโนโลยีที่แตกต่างกัน เช่น JavaScript สำหรับเว็บไซต์, Java สำหรับแอป Android, Swift สำหรับแอป iPhone และอื่น ๆ)

ทีละขั้นตอน: การสร้าง GraphQL query

ด้านล่างคือการวิเคราะห์โดยละเอียดว่า query นี้ทำงานอย่างไร

การตรวจสอบว่าผู้ใช้เป็น admin หรือไม่

query นี้ตรวจสอบว่าผู้ใช้ที่ล็อกอินอยู่มีบทบาท "administrator" หรือไม่ และ export เงื่อนไขนี้ไว้ในตัวแปรไดนามิก $isAdminUser:

query
{
  me {
    roleNames
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}

การเรียกใช้ operation แบบมีเงื่อนไข

เมื่อเปิดใช้งาน Multiple Query Execution แล้ว ไดเรกทีฟ @include และ @skip ก็สามารถนำไปใช้กับ operation ได้เช่นกัน วิธีนี้ทำให้เราเลือกได้ว่าจะเรียกใช้ operation หรือไม่ ตามค่าของตัวแปรไดนามิกบางตัว

ใน query ด้านล่าง จะมีการเรียกใช้เพียง operation เดียวจากสอง operation:

  • RetrieveContentForAdminUser จะถูกเรียกใช้เมื่อ $isAdminUser เป็น true เท่านั้น
  • RetrieveContentForNonAdminUser จะถูกเรียกใช้เมื่อ $isAdminUser เป็น false เท่านั้น
query RetrieveContentForAdminUser
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  # ...
}
 
query RetrieveContentForNonAdminUser
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  # ...
}

มาให้ค่าที่ตอบกลับสองแบบที่แตกต่างกันสำหรับฟิลด์ content ของโพสต์ ขึ้นอยู่กับว่าผู้ใช้เป็น admin หรือไม่:

  • operation แรกใช้ content เป็น alias และคำนวณค่าของฟิลด์แบบไดนามิก โดยนำฟิลด์ originalContent และ wpAdminEditURL มาต่อกันผ่าน _sprintf
  • operation ที่สองดึงฟิลด์ content มาโดยตรง
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content
    wpAdminEditURL
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}

การเพิ่ม operation ที่จะถูกเรียกใช้

ตอนนี้เรามีสอง operation ที่อาจถูกเรียกใช้ได้ แต่เราระบุ ?operationName=... ได้เพียงค่าเดียวเมื่อเรียกใช้ query

ดังนั้น เราจึงเพิ่ม operation ExecuteAll ที่ขึ้นอยู่กับทั้ง RetrieveContentForAdminUser และ RetrieveContentForNonAdminUser โดยมีฟิลด์อย่างง่าย id อยู่ภายใน (เพราะเราต้อง query บางอย่างใน operation):

query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id
}

การเรียก endpoint ด้วย ?operationName=ExecuteAll จะโหลดทั้งสอง operation แต่จะมีเพียง operation เดียวเท่านั้นที่ถูกเรียกใช้งานจริง

การลบข้อมูลที่ไม่จำเป็นออก

ขั้นตอนสุดท้ายคือการลบฟิลด์ทั้งหมดที่เป็นฟิลด์เสริม (ซึ่งเราไม่จำเป็นต้องแสดงผลลัพธ์ในค่าที่ตอบกลับ) ออกด้วย @remove

GraphQL query ที่รวมเข้าด้วยกันแล้วคือ:

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}