Учебник по схеме
Учебник по схемеУрок 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 для веб-сайта, Java для приложения Android, Swift для приложения iPhone и других)

Пошагово: создание GraphQL-запроса

Ниже приведён подробный анализ того, как работает запрос.

Определение того, является ли пользователь администратором

Этот запрос проверяет, имеет ли авторизованный пользователь роль "administrator", и экспортирует это условие в динамическую переменную $isAdminUser:

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

Условное выполнение операций

Когда включено Выполнение нескольких queries, директивы @include и @skip также можно применять к операциям. Таким образом, мы можем выполнять или не выполнять операцию в зависимости от значения некоторой динамической переменной.

В приведённом ниже запросе будет выполнена только одна из двух операций:

  • RetrieveContentForAdminUser выполняется только когда $isAdminUser равно true
  • RetrieveContentForNonAdminUser выполняется только когда $isAdminUser равно false
query RetrieveContentForAdminUser
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  # ...
}
 
query RetrieveContentForNonAdminUser
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  # ...
}

Предоставим два разных ответа для поля content записи в зависимости от того, является ли пользователь администратором:

  • Первая операция использует content в качестве псевдонима и динамически вычисляет значение поля, объединяя поля originalContent и wpAdminEditURL с помощью _sprintf
  • Вторая операция получает поле 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
  }
}

Добавление операции для выполнения

Теперь у нас есть две операции, которые могут быть выполнены, однако при выполнении запроса можно указать только одно ?operationName=....

Поэтому мы добавляем операцию ExecuteAll, которая зависит от RetrieveContentForAdminUser и RetrieveContentForNonAdminUser и содержит простое поле id (поскольку в операции необходимо запрашивать хотя бы что-то):

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

Вызов эндпоинта с ?operationName=ExecuteAll теперь загрузит обе операции, однако реально выполнена будет только одна из них.

Удаление ненужных данных

Последний шаг — удалить все вспомогательные поля (вывод которых нам не нужен в ответе) с помощью @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
}