スキーマチュートリアル
スキーマチュートリアルレッスン5: ユーザーに応じたコンテンツのカスタマイズ

レッスン5: ユーザーに応じたコンテンツのカスタマイズ

クエリされたデータ(ログインユーザーのロールなど)に応じて、フィールドが返す値を変えることができます。

ユーザーに応じてコンテンツをカスタマイズするGraphQLクエリ

以下のGraphQLクエリは投稿のコンテンツを取得し、管理者ユーザーのみコンテンツの末尾に「この投稿を編集」リンクを追加します。

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
}

管理者ユーザーの場合のレスポンスは次のようになります。

{
  "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>"
    }
  }
}

管理者以外のユーザーの場合のレスポンスは次のようになります。

{
  "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サーバーが(あらゆる条件を考慮した上で)フィールドに必要な値を動的に計算することで、次のメリットが得られます。

  • アプリケーションのロジックがシンプルになります。単一の信頼できる情報源が確立され、コードがDRYになり、クライアント側で同じロジックを実装する必要がなくなります
  • アプリケーションの信頼性が向上します。複数のクライアントがサーバーのデータにアクセスする場合、同じロジックを異なる実装で行うと不一致が生じる可能性があり、バグの原因となります(特にウェブサイト向けのJavaScript、Androidアプリ向けのJava、iPhoneアプリ向けのSwiftなど、異なる技術を使ったクライアントが混在する場合はとりわけ顕著です)

ステップバイステップ: GraphQLクエリの作成

以下では、このクエリがどのように機能するかを詳しく解説します。

ユーザーが管理者かどうかを確認する

このクエリは、ログインユーザーが "administrator" ロールを持っているかどうかを確認し、その条件をダイナミック変数 $isAdminUser にエクスポートします。

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

オペレーションの条件付き実行

Multiple Query Execution が有効な場合、ディレクティブ @include および @skip をオペレーションに対しても適用できます。これにより、ダイナミック変数の値に応じてオペレーションを実行するかどうかを制御できます。

以下のクエリでは、2つのオペレーションのうち1つだけが実行されます。

  • RetrieveContentForAdminUser$isAdminUsertrue の場合のみ実行されます
  • RetrieveContentForNonAdminUser$isAdminUserfalse の場合のみ実行されます
query RetrieveContentForAdminUser
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  # ...
}
 
query RetrieveContentForNonAdminUser
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  # ...
}

ユーザーが管理者かどうかに応じて、投稿の content フィールドに対して異なるレスポンスを提供しましょう。

  • 最初のオペレーションは content をエイリアスとして使用し、_sprintf を通じて originalContent フィールドと wpAdminEditURL フィールドを連結することでフィールドの値を動的に計算します
  • 2番目のオペレーションは 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
  }
}

実行するオペレーションを追加する

ここまでで実行される可能性のある2つのオペレーションができましたが、クエリの実行時に指定できる ?operationName=... は1つだけです。

そこで、RetrieveContentForAdminUserRetrieveContentForNonAdminUser の両方に依存するオペレーション ExecuteAll を追加します。オペレーション内で何かをクエリする必要があるため、シンプルなフィールド id を含めています。

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

?operationName=ExecuteAll でエンドポイントを呼び出すと、両方のオペレーションが読み込まれますが、実際に実行されるのはそのうちの1つだけです。

不要なデータを削除する

最後のステップは、補助的なフィールド(レスポンスに出力する必要のないフィールド)をすべて @remove で削除することです。

統合されたGraphQLクエリは次のとおりです。

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
}