|
@@ -21,12 +21,23 @@ class ProductReviewProvider implements ProviderInterface
|
|
|
|
|
|
|
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
|
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
|
|
|
{
|
|
{
|
|
|
- $args = $context['args'] ?? [];
|
|
|
|
|
|
|
+ // GraphQL 走 $context['args'];REST 走 request()->query()。
|
|
|
|
|
+ // 两者合并,REST 查询参数优先,保证 REST 与 GraphQL 行为一致。
|
|
|
|
|
+ $args = array_merge($context['args'] ?? [], request()->query());
|
|
|
|
|
+
|
|
|
$query = ProductReview::query();
|
|
$query = ProductReview::query();
|
|
|
|
|
|
|
|
|
|
+ // 优先读取商品子资源路由的路径参数(/products/{productId}/reviews),
|
|
|
|
|
+ // 其次读取查询参数 product_id / productId。
|
|
|
|
|
+ $productId = $uriVariables['productId']
|
|
|
|
|
+ ?? $uriVariables['product_id']
|
|
|
|
|
+ ?? $args['product_id']
|
|
|
|
|
+ ?? $args['productId']
|
|
|
|
|
+ ?? null;
|
|
|
|
|
+
|
|
|
// Apply filters
|
|
// Apply filters
|
|
|
- if (! empty($args['product_id'])) {
|
|
|
|
|
- $query->where('product_id', (int) $args['product_id']);
|
|
|
|
|
|
|
+ if (! empty($productId)) {
|
|
|
|
|
+ $query->where('product_id', (int) $productId);
|
|
|
}
|
|
}
|
|
|
/** Default to approved reviews for storefront API */
|
|
/** Default to approved reviews for storefront API */
|
|
|
$query->where('status', isset($args['status']) ? (string) $args['status'] : 'approved');
|
|
$query->where('status', isset($args['status']) ? (string) $args['status'] : 'approved');
|
|
@@ -37,24 +48,37 @@ class ProductReviewProvider implements ProviderInterface
|
|
|
// Eager load relationships
|
|
// Eager load relationships
|
|
|
$query->with(['product', 'customer']);
|
|
$query->with(['product', 'customer']);
|
|
|
|
|
|
|
|
- // Cursor-based pagination (offset-based cursors from API Platform)
|
|
|
|
|
|
|
+ // 分页参数:REST 用 page / per_page(别名 limit),GraphQL 用 first / last / after / before
|
|
|
|
|
+ $restPage = isset($args['page']) ? max(1, (int) $args['page']) : null;
|
|
|
|
|
+ $restPerPage = isset($args['per_page'])
|
|
|
|
|
+ ? (int) $args['per_page']
|
|
|
|
|
+ : (isset($args['limit']) ? (int) $args['limit'] : null);
|
|
|
|
|
+
|
|
|
$first = isset($args['first']) ? (int) $args['first'] : null;
|
|
$first = isset($args['first']) ? (int) $args['first'] : null;
|
|
|
$last = isset($args['last']) ? (int) $args['last'] : null;
|
|
$last = isset($args['last']) ? (int) $args['last'] : null;
|
|
|
$after = $args['after'] ?? null;
|
|
$after = $args['after'] ?? null;
|
|
|
$before = $args['before'] ?? null;
|
|
$before = $args['before'] ?? null;
|
|
|
|
|
|
|
|
- $perPage = $first ?? $last ?? 30;
|
|
|
|
|
- $offset = 0;
|
|
|
|
|
-
|
|
|
|
|
- if ($after) {
|
|
|
|
|
- $decoded = base64_decode($after, true);
|
|
|
|
|
- $offset = ctype_digit((string) $decoded) ? ((int) $decoded + 1) : 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ($before) {
|
|
|
|
|
- $decoded = base64_decode($before, true);
|
|
|
|
|
- $cursor = ctype_digit((string) $decoded) ? (int) $decoded : 0;
|
|
|
|
|
- $offset = max(0, $cursor - $perPage);
|
|
|
|
|
|
|
+ if ($restPage !== null || $restPerPage !== null) {
|
|
|
|
|
+ // REST 分页
|
|
|
|
|
+ $perPage = $restPerPage !== null ? min(max(1, $restPerPage), 50) : 30;
|
|
|
|
|
+ $page = $restPage ?? 1;
|
|
|
|
|
+ $offset = ($page - 1) * $perPage;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // GraphQL 游标分页
|
|
|
|
|
+ $perPage = $first ?? $last ?? 30;
|
|
|
|
|
+ $offset = 0;
|
|
|
|
|
+
|
|
|
|
|
+ if ($after) {
|
|
|
|
|
+ $decoded = base64_decode($after, true);
|
|
|
|
|
+ $offset = ctype_digit((string) $decoded) ? ((int) $decoded + 1) : 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($before) {
|
|
|
|
|
+ $decoded = base64_decode($before, true);
|
|
|
|
|
+ $cursor = ctype_digit((string) $decoded) ? (int) $decoded : 0;
|
|
|
|
|
+ $offset = max(0, $cursor - $perPage);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
$query->orderBy('id', 'asc');
|
|
$query->orderBy('id', 'asc');
|