基于WPGraphQL的无头WordPress产品分页问题求助
问题:Next.js集成WPGraphQL实现产品页码分页无效
原本在WordPress中可通过page/2、page/3这类URL实现产品分页,现在在Next.js应用中想要实现相同的页码切换分页(直接跳转至第N页加载对应产品)。但WPGraphQL默认仅支持基于游标的分页,尝试使用wp-graphql-offset-pagination插件注册offsetPagination字段后,修改offset值加载的产品始终一致。
当前代码情况
Next.js查询代码
export const useProductsData = () => { const params = useParams() const { handle } = params; const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchProductsData = async () => { const query = gql` query { products(where: { categoryIn: "${handle}", stockStatus: IN_STOCK }, first: 25) { nodes { id sku slug status name image { sourceUrl } ... on SimpleProduct { id name price } ... on VariableProduct { id name price } } } } `; try { const result = await fetchWooCommerceData(query); setProducts(result.products.nodes); setLoading(false); } catch (error) { setError(error); setLoading(false); } }; fetchProductsData(); return { products, loading, error }; };
注册offsetPagination字段的PHP代码
// Register offsetPagination fields for WooCommerce products register_graphql_field( 'RootQueryToProductConnectionWhereArgs', 'offsetPagination', [ 'type' => 'OffsetPagination', 'description' => 'Paginate products with offsets', ] );
测试查询与输出
query Products { products(where: { offsetPagination: { size: 10, offset: 30 } }) { pageInfo { offsetPagination { hasMore hasPrevious total } } nodes { id name # Other product fields } } }
查询输出:
{ "data": { "products": { "pageInfo": { "offsetPagination": { "hasMore": true, "hasPrevious": true, "total": 16300 } }, "nodes": [ // 产品列表始终相同 ] } } }
解决方案
1. 修复offsetPagination参数的映射逻辑
仅注册字段不足以让参数生效,需要将offsetPagination的参数映射到WooCommerce的产品查询逻辑中。替换原注册代码为:
add_action( 'graphql_register_types', function() { // 为产品连接添加offset分页参数映射 add_filter( 'graphql_product_connection_query_args', function( $query_args, $source, $args, $context, $info ) { if ( isset( $args['where']['offsetPagination'] ) ) { $pagination = $args['where']['offsetPagination']; // 传递offset参数到查询 if ( isset( $pagination['offset'] ) ) { $query_args['offset'] = $pagination['offset']; } // 传递每页数量参数到查询 if ( isset( $pagination['size'] ) ) { $query_args['posts_per_page'] = $pagination['size']; } } return $query_args; }, 10, 5 ); // 注册offsetPagination到where参数 register_graphql_field( 'RootQueryToProductConnectionWhereArgs', 'offsetPagination', [ 'type' => 'OffsetPagination', 'description' => 'Paginate products with offsets', ] ); } );
2. 修改Next.js查询代码,支持动态页码
- 从URL参数获取当前页码,计算对应的offset值
- 使用GraphQL变量传递参数(避免字符串拼接风险)
- 监听页码和分类变化,自动重新查询
import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; import gql from 'graphql-tag'; export const useProductsData = () => { const params = useParams() const { handle, page = 1 } = params; // 默认页码为1 const pageSize = 25; const offset = (parseInt(page) - 1) * pageSize; // 计算偏移量:(页码-1)*每页数量 const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fetchProductsData = async () => { const query = gql` query GetProducts($category: String!, $offset: Int!, $pageSize: Int!) { products(where: { categoryIn: $category, stockStatus: IN_STOCK, offsetPagination: { offset: $offset, size: $pageSize } }) { pageInfo { offsetPagination { hasMore hasPrevious total } } nodes { id sku slug status name image { sourceUrl } ... on SimpleProduct { price } ... on VariableProduct { price } } } } `; try { const result = await fetchWooCommerceData(query, { variables: { category: handle, offset: offset, pageSize: pageSize } }); setProducts(result.products.nodes); setLoading(false); } catch (error) { setError(error); setLoading(false); } }; // 监听分类和页码变化,重新获取数据 useEffect(() => { setLoading(true); fetchProductsData(); }, [handle, page]); return { products, loading, error }; };
3. 固定排序规则避免结果重复
如果分页结果仍有重复,可在查询中添加固定的排序条件,确保每次查询的产品顺序一致:
query GetProducts($category: String!, $offset: Int!, $pageSize: Int!) { products(where: { categoryIn: $category, stockStatus: IN_STOCK, offsetPagination: { offset: $offset, size: $pageSize }, orderby: { field: DATE, order: DESC } // 按发布时间倒序 }) { // ... 其他字段 } }
内容的提问来源于stack exchange,提问作者Sylwester Zabielski
相关产品推荐
相关产品推荐

