Lập trình với API
Lập trình với APISử dụng mã DRY để render các block ở phía server (PHP) và phía client (JS)

Sử dụng mã DRY để render các block ở phía server (PHP) và phía client (JS)

Các block động (Gutenberg) là những block xây dựng cấu trúc và nội dung của chúng ngay lúc chạy khi block được render trên front-end.

Việc render một block động ở front-end (để hiển thị trong trình chỉnh sửa WordPress) và ở phía server (để tạo HTML cho bài đăng blog) thường sẽ lấy dữ liệu theo hai cách khác nhau:

  • Kết nối tới API ở phía client (JavaScript)
  • Gọi các hàm WordPress ở phía server (PHP)

Với Gato GraphQL và các tiện ích mở rộng, bạn hoàn toàn có thể làm cho logic này trở nên DRY, có một nguồn dữ liệu duy nhất để lấy dữ liệu cho cả phía client lẫn phía server. Hãy cùng khám phá cách thực hiện điều này.

Lưu trữ các queries GraphQL trong các file .gql

Để kết nối với server GraphQL từ phía client, chúng ta thường thực thi GraphQL query được nhúng trong mã JavaScript, như sau:

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

Chúng ta có thể lưu GraphQL query trong một file .gql (hoặc .graphql) và import nội dung của nó bằng cách sử dụng raw-loader của 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;

(Mã này hoạt động với Webpack v4; đối với v5, chúng ta phải sử dụng Asset Modules thay thế.)

Tiếp theo, chúng ta đặt GraphQL query bên trong một file .gql:

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

Cuối cùng, trong mã của block, chúng ta import file và truyền nội dung của nó vào fetch:

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

Xử lý các file .gql ở phía server

File GraphQL mà chúng ta đã tạo ở trên sẽ là nguồn dữ liệu duy nhất để lấy dữ liệu cho block. Nó đã đáp ứng yêu cầu này cho phía client; bây giờ hãy xem cách thực hiện cho phía server.

Tiện ích mở rộng Internal GraphQL Server cài đặt một server có thể được gọi bên trong ứng dụng của chúng ta, sử dụng mã PHP.

Internal GraphQL Server cung cấp các phương thức tĩnh sau, thông qua lớp GraphQLServer:

  • executeQuery: Thực thi một GraphQL query
  • executeQueryInFile: Thực thi một GraphQL query chứa trong một file (.gql)
  • executePersistedQuery: Thực thi một GraphQL query được lưu trữ (cung cấp ID của nó dưới dạng int, hoặc slug dưới dạng string) (cần có tiện ích mở rộng Persisted Queries)

Chữ ký của executeQueryInFile trông như sau:

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 {
    // ...
  }
}

Bằng cách gọi executeQueryInFile và truyền vào file .gql đã tạo trước đó, chúng ta lấy được dữ liệu khi render block động:

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);

Bằng cách này, một file .gql duy nhất lấy dữ liệu để cung cấp cho các block trên cả phía client lẫn phía server.