Để lấy tất cả trang con (bao gồm cả cấp 1, cấp 2, cấp 3,...) của một trang cụ thể trong WordPress

Để lấy tất cả trang con (bao gồm cả cấp 1, cấp 2, cấp 3,...) của một trang cụ thể trong WordPress bằng GraphQL, bạn cần sử dụng plugin như WPGraphQL, và truy vấn phải sử dụng đệ quy (nếu được hỗ trợ) hoặc truy cập qua các cấp con cụ thể.

Tuy nhiên, WPGraphQL không hỗ trợ đệ quy tự động, nên bạn phải lặp các cấp con thủ công nếu số cấp đã biết. Dưới đây là ví dụ minh họa:

Muốn trích xuất tất cả các trang con (bao gồm cả các cấp độ sâu hơn như "Test 2.1", "Test 2.2") từ kết quả GraphQL đã cung cấp và đưa chúng vào một mảng phẳng.

Dưới đây là cách bạn có thể thực hiện điều đó bằng JavaScript (hoặc ngôn ngữ lập trình tương tự) để xử lý dữ liệu JSON đã nhận được:

const graphQLResult = {
  "data": {
    "page": {
      "id": "cG9zdDo1OA==",
      "uri": "/en/test/",
      "children": {
        "edges": [
          {
            "node": {
              "id": "cG9zdDo4NQ==",
              "excerpt": "",
              "title": "Test 1",
              "children": {
                "nodes": [
                  {
                    "id": "cG9zdDo5MA==",
                    "excerpt": "",
                    "featuredImage": null,
                    "title": "Test 2.2"
                  },
                  {
                    "id": "cG9zdDo4Nw==",
                    "excerpt": "",
                    "featuredImage": null,
                    "title": "Test 2.1"
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
};

function getAllChildren(pageData) {
  const childrenArray = [];

  function traverseChildren(nodes) {
    if (!nodes) {
      return;
    }

    nodes.forEach(node => {
      // Thêm thông tin của trang con hiện tại vào mảng
      childrenArray.push({
        id: node.id,
        excerpt: node.excerpt,
        title: node.title,
        featuredImage: node.featuredImage // Bao gồm featuredImage nếu có
      });

      // Nếu có các trang con tiếp theo, đệ quy
      if (node.children && node.children.nodes) {
        traverseChildren(node.children.nodes);
      }
    });
  }

  // Bắt đầu duyệt từ các con trực tiếp của trang "test"
  if (graphQLResult.data.page && graphQLResult.data.page.children && graphQLResult.data.page.children.edges) {
    graphQLResult.data.page.children.edges.forEach(edge => {
      // Đảm bảo rằng node tồn tại trước khi xử lý
      if (edge.node) {
        // Thêm trang con trực tiếp (Test 1)
        childrenArray.push({
          id: edge.node.id,
          excerpt: edge.node.excerpt,
          title: edge.node.title,
          featuredImage: edge.node.featuredImage
        });

        // Duyệt các con của Test 1 (Test 2.1, Test 2.2)
        if (edge.node.children && edge.node.children.nodes) {
          traverseChildren(edge.node.children.nodes);
        }
      }
    });
  }

  return childrenArray;
}

const allChildPages = getAllChildren(graphQLResult);
console.log(allChildPages);

Kết quả đầu ra (từ console.log(allChildPages)):

Giải thích:

  1. graphQLResult: Đây là đối tượng JSON chứa kết quả từ truy vấn GraphQL của bạn.

  2. getAllChildren(pageData):

    • Hàm này sẽ nhận đối tượng kết quả GraphQL làm đầu vào.

    • childrenArray: Một mảng trống để lưu trữ tất cả các trang con đã tìm thấy.

    • traverseChildren(nodes): Đây là một hàm đệ quy.

      • Nó duyệt qua từng node trong danh sách nodes được cung cấp.

      • Với mỗi node, nó lấy các thông tin cần thiết (id, excerpt, title, featuredImage) và thêm vào childrenArray.

      • Nếu node đó có thêm children (tức là nó có các trang con riêng), hàm traverseChildren sẽ được gọi lại với nodes của các trang con đó, tạo ra quá trình đệ quy để duyệt qua tất cả các cấp.

    • Logic chính:

      • Đầu tiên, nó kiểm tra xem graphQLResult.data.page và các children.edges có tồn tại hay không.

      • Sau đó, nó duyệt qua các edge trong children.edges (đây là các trang con trực tiếp của "Test", trong trường hợp này là "Test 1").

      • Nó thêm "Test 1" vào childrenArray.

      • Cuối cùng, nó gọi traverseChildren để xử lý các trang con của "Test 1" (là "Test 2.1" và "Test 2.2"), đảm bảo rằng tất cả các cấp độ đều được đưa vào mảng.

Cách tiếp cận này hiệu quả để làm phẳng cấu trúc cây và lấy tất cả các trang con vào một mảng duy nhất, dễ dàng để xử lý tiếp.

Khai báo với typescript

Bạn muốn khai báo hàm getAllChildren với TypeScript, dựa trên cấu trúc dữ liệu GraphQL mà bạn đã cung cấp trước đó. Hàm này sẽ duyệt đệ quy qua các trang con để thu thập tất cả chúng vào một mảng phẳng.

Dưới đây là cách bạn có thể khai báo và triển khai hàm getAllChildren bằng TypeScript:


Khai báo Type và Hàm getAllChildren

Để khai báo hàm này một cách rõ ràng trong TypeScript, trước tiên chúng ta cần định nghĩa các interface (kiểu dữ liệu) cho cấu trúc dữ liệu mà GraphQL trả về. Điều này giúp TypeScript hiểu rõ hình dạng của dữ liệu và cung cấp tính năng tự động hoàn thành, kiểm tra lỗi tĩnh.

TypeScript


Giải thích:

  1. Định nghĩa Interfaces (PageNode, GraphQLPageResult, v.v.):

    • Đây là bước quan trọng nhất của TypeScript. Chúng ta tạo ra các "khuôn mẫu" để mô tả chính xác cấu trúc của dữ liệu mà bạn mong đợi từ GraphQL.

    • PageNode là interface trung tâm, mô tả một đối tượng trang với các thuộc tính như id, title, excerpt, featuredImage, và quan trọng nhất là children.

    • Thuộc tính children trong PageNode được định nghĩa đệ quy để có thể chứa một mảng các PageNode khác, phản ánh cấu trúc phân cấp. Tôi đã bao gồm cả nodesedges để phù hợp với các định dạng trả về khác nhau của WPGraphQL.

    • GraphQLPageResult là interface bao bọc toàn bộ kết quả GraphQL, đảm bảo cấu trúc data.page được xác định.

  2. Hàm getAllChildren(graphQLResult: GraphQLPageResult): PageNode[]:

    • Kiểu tham số: Hàm nhận một đối tượng graphQLResult có kiểu GraphQLPageResult. TypeScript sẽ kiểm tra xem đối tượng bạn truyền vào có khớp với cấu trúc này không.

    • Kiểu trả về: Hàm được khai báo sẽ trả về một mảng các PageNode (PageNode[]).

    • childrenArray: PageNode[] = []: Mảng này được khai báo với kiểu PageNode[], đảm bảo rằng chỉ các đối tượng khớp với PageNode mới có thể được thêm vào.

    • Hàm traverseChildren(nodes: PageNode[] | undefined):

      • Đây là hàm đệ quy được sử dụng để duyệt qua các cấp độ con.

      • Tham số nodes có kiểu PageNode[] | undefined để xử lý trường hợp không có node nào.

      • Khi childrenArray.push(node) được gọi, TypeScript đảm bảo rằng node khớp với PageNode.

      • Logic kiểm tra node.children && node.children.nodes hoặc node.children && node.children.edges giúp hàm xử lý cả hai dạng trả về của children (trực tiếp nodes hoặc qua edges).

Bằng cách sử dụng các interface này, bạn sẽ nhận được sự hỗ trợ mạnh mẽ từ TypeScript, giúp phát hiện lỗi cú pháp, lỗi logic sớm hơn trong quá trình phát triển, và làm cho mã nguồn của bạn dễ đọc và bảo trì hơn.

Bạn muốn chuyển đổi cấu trúc dữ liệu GraphQL phức tạp thành một mảng phẳng các đối tượng Product đơn giản hơn, phù hợp với interface Product mà bạn đã định nghĩa.

Dưới đây là hàm TypeScript để thực hiện việc chuyển đổi này, bao gồm cả việc xử lý ảnh đại diện (featuredImage).

TypeScript

Giải thích các thay đổi và logic:

  1. Cập nhật Interfaces GraphQL:

    • FeaturedImageGraphQL: Đã đổi tên để tránh nhầm lẫn với trường img trong Product.

    • Đảm bảo __typename được bao gồm ở tất cả các cấp nơi nó xuất hiện trong dữ liệu của bạn.

  2. Hàm convertGraphQLToProducts(graphQLResult: GraphQLPageResult): Product[]:

    • products: Product[] = []: Khởi tạo một mảng rỗng để lưu trữ các đối tượng Product đã chuyển đổi.

    • processPageNode(pageNode: PageNode): Đây là hàm đệ quy chính:

      • Nó nhận một PageNode làm đối số.

      • Lấy img (sourceUrl):TypeScript

        Dòng này sử dụng optional chaining (?.) để truy cập an toàn vào featuredImage, nodesourceUrl. Nếu bất kỳ phần nào trên đường dẫn là null hoặc undefined, nó sẽ trả về undefined, và sau đó toán tử || '' sẽ gán một chuỗi rỗng.

      • Gán category:

        • Tôi đã thêm một logic giả định để gán category dựa trên title của trang (if (pageNode.title.includes('Test 1')) { category = 'category-A'; }).

        • Bạn cần thay đổi logic này để phù hợp với cách bạn muốn phân loại các sản phẩm của mình. Ví dụ, bạn có thể có một trường custom field trong WordPress lưu trữ category của sản phẩm, và bạn sẽ cần truy vấn trường đó qua GraphQL để lấy thông tin category.

      • Thêm vào products array: Một đối tượng Product mới được tạo với id, title (từ pageNode.title), img (từ imageUrl), và category và được push vào mảng products.

      • Đệ quy cho children: Hàm này sau đó kiểm tra pageNode.children và gọi đệ quy processPageNode cho tất cả các trang con (dù chúng nằm trong nodes hay edges).

    • Logic bắt đầu:

      • Hàm bắt đầu bằng cách lấy rootPage (graphQLResult.data.page).

      • Nó sau đó kiểm tra rootPage.children và bắt đầu quá trình đệ quy processPageNode cho tất cả các trang con trực tiếp của trang gốc.

      • Nếu bạn muốn bản thân trang gốc (ví dụ "Test") cũng được chuyển đổi thành một "Product" và thêm vào danh sách, bạn chỉ cần gọi processPageNode(rootPage); trước khi xử lý các con của nó. Hiện tại, nó chỉ xử lý các trang con của "Test".

Với hàm này, bạn có thể biến dữ liệu GraphQL phân cấp của mình thành một mảng phẳng các đối tượng Product dễ dàng quản lý trong ứng dụng Next.js của bạn.

profile pictureGenerate Audio Overview

src\graphql\queries.ts

Last updated

Was this helpful?