Программирование с API
Программирование с APIИспользование принципа DRY для рендеринга блоков на стороне сервера (PHP) и клиента (JS)

Использование принципа DRY для рендеринга блоков на стороне сервера (PHP) и клиента (JS)

Динамические блоки (Gutenberg) — это блоки, которые строят свою структуру и содержимое «на лету» в момент отображения на фронтенде.

Рендеринг динамического блока на фронтенде (для отображения в редакторе WordPress) и на стороне сервера (для генерации HTML записи блога) обычно предполагает получение данных двумя разными способами:

  • Обращение к API на стороне клиента (JavaScript)
  • Вызов функций WordPress на стороне сервера (PHP)

С помощью Gato GraphQL и расширений эту логику можно сделать DRY, имея единый источник истины для получения данных как на стороне клиента, так и на стороне сервера. Рассмотрим, как это сделать.

Хранение GraphQL queries в файлах .gql

Для подключения к серверу GraphQL со стороны клиента мы обычно выполняем GraphQL query, встроенный непосредственно в код JavaScript, вот так:

const response = await fetch(endpoint, {
  body: JSON.stringify({
    query: `
      query {
        posts {
          id
          title
          author {
            id
            name
          }
        }
      }
    `
  )
} );

Альтернативный подход — сохранить GraphQL query в файл .gql (или .graphql) и импортировать его содержимое с помощью raw-loader для Webpack:

// File webpack.config.js
const config = require( '@wordpress/scripts/config/webpack.config' );
 
config.module.rules.push(
  {
    test: /\.(gql|graphql)$/i,
    use: 'raw-loader',
  },
);
 
module.exports = config;

(Этот код работает для Webpack v4; для v5 вместо него необходимо использовать Asset Modules.)

Далее помещаем GraphQL query в файл .gql:

# File graphql-documents/fetch-posts-with-author.gql
query {
  posts {
    id
    title
    author {
      id
      name
    }
  }
}

Наконец, в коде блока импортируем файл и передаём его содержимое в fetch:

import graphQLQuery from './graphql-documents/fetch-posts-with-author.gql';
 
// ...
 
const response = await fetch(endpoint, {
  body: JSON.stringify({
    query: graphQLQuery
  )
} );

Использование файлов .gql на стороне сервера

Созданный выше файл GraphQL станет единым источником истины для получения данных блока. Для клиентской стороны задача уже решена; теперь посмотрим, как реализовать то же самое на стороне сервера.

Расширение Internal GraphQL Server устанавливает сервер, который можно вызывать внутри нашего приложения с помощью PHP-кода.

Internal GraphQL Server предоставляет следующие статические методы через класс GraphQLServer:

  • executeQuery: выполняет GraphQL query
  • executeQueryInFile: выполняет GraphQL query, содержащийся в файле (.gql)
  • executePersistedQuery: выполняет сохранённый GraphQL query (передавая его ID в виде int или slug в виде string) (требуется расширение Persisted Queries)

Сигнатура метода executeQueryInFile выглядит следующим образом:

namespace GatoGraphQL\InternalGraphQLServer;
 
class GraphQLServer {
  /**
   * Execute a GraphQL query contained in a (`.gql`) file
   */
  public static function executeQueryInFile(
      string $file,
      array $variables = [],
      ?string $operationName = null
  ): Response {
    // ...
  }
}

Вызывая executeQueryInFile с ранее созданным файлом .gql, мы получаем данные при рендеринге динамического блока:

use GatoGraphQL\InternalGraphQLServer\GraphQLServer;
 
$block = [
  'render_callback' => function(array $attributes, string $content): string {
    // Provide the GraphQL query file
    $file = __DIR__ . '/blocks/my-block/graphql-documents/fetch-posts-with-author.gql';
 
    // Execute the query against the internal server
    $response = GraphQLServer::executeQueryInFile($file);
 
    // Get the content and decode it
    $responseContent = json_decode($response->getContent(), true);
 
    // Access the data and errors from the response
    $data = $responseContent["data"] ?? [];
    $errors = $responseContent["errors"] ?? [];
 
    // Do something with the data
    // $content = $this->useGraphQLData($content, $data, $errors);
    // ...
 
    return $content;
  },
];
register_block_type("namespace/my-block", $block);

Таким образом, единственный файл .gql обеспечивает получение данных для блоков как на стороне клиента, так и на стороне сервера.