ProductReviewProvider.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. namespace Webkul\BagistoApi\State;
  3. use ApiPlatform\Laravel\Eloquent\Paginator;
  4. use ApiPlatform\Metadata\Operation;
  5. use ApiPlatform\State\Pagination\Pagination;
  6. use ApiPlatform\State\ProviderInterface;
  7. use Illuminate\Pagination\LengthAwarePaginator;
  8. use Webkul\BagistoApi\Models\ProductReview;
  9. /**
  10. * Provider for ProductReview queries
  11. * Handles filtering by product_id, status, and rating
  12. */
  13. class ProductReviewProvider implements ProviderInterface
  14. {
  15. public function __construct(
  16. private readonly Pagination $pagination
  17. ) {}
  18. public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
  19. {
  20. // GraphQL 走 $context['args'];REST 走 request()->query()。
  21. // 两者合并,REST 查询参数优先,保证 REST 与 GraphQL 行为一致。
  22. $args = array_merge($context['args'] ?? [], request()->query());
  23. $query = ProductReview::query();
  24. // 优先读取商品子资源路由的路径参数(/products/{productId}/reviews),
  25. // 其次读取查询参数 product_id / productId。
  26. $productId = $uriVariables['productId']
  27. ?? $uriVariables['product_id']
  28. ?? $args['product_id']
  29. ?? $args['productId']
  30. ?? null;
  31. // Apply filters
  32. if (! empty($productId)) {
  33. $query->where('product_id', (int) $productId);
  34. }
  35. /** Default to approved reviews for storefront API */
  36. $query->where('status', isset($args['status']) ? (string) $args['status'] : 'approved');
  37. if (! empty($args['rating'])) {
  38. $query->where('rating', (int) $args['rating']);
  39. }
  40. // 仅返回带图片附件的评论(前端 Photos 筛选)
  41. if (! empty($args['has_images'])) {
  42. $hasImages = filter_var($args['has_images'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
  43. if ($hasImages === true) {
  44. $query->has('images');
  45. } elseif ($hasImages === false) {
  46. $query->doesntHave('images');
  47. }
  48. }
  49. // 按附件类型过滤:image / video
  50. if (! empty($args['attachment_type'])) {
  51. $type = (string) $args['attachment_type'];
  52. $query->whereHas('images', function ($q) use ($type) {
  53. $q->where('type', $type);
  54. });
  55. }
  56. // Eager load relationships
  57. $query->with(['product', 'customer']);
  58. // 分页参数:REST 用 page / per_page(别名 limit),GraphQL 用 first / last / after / before
  59. $restPage = isset($args['page']) ? max(1, (int) $args['page']) : null;
  60. $restPerPage = isset($args['per_page'])
  61. ? (int) $args['per_page']
  62. : (isset($args['limit']) ? (int) $args['limit'] : null);
  63. $first = isset($args['first']) ? (int) $args['first'] : null;
  64. $last = isset($args['last']) ? (int) $args['last'] : null;
  65. $after = $args['after'] ?? null;
  66. $before = $args['before'] ?? null;
  67. if ($restPage !== null || $restPerPage !== null) {
  68. // REST 分页
  69. $perPage = $restPerPage !== null ? min(max(1, $restPerPage), 50) : 30;
  70. $page = $restPage ?? 1;
  71. $offset = ($page - 1) * $perPage;
  72. } else {
  73. // GraphQL 游标分页
  74. $perPage = $first ?? $last ?? 30;
  75. $offset = 0;
  76. if ($after) {
  77. $decoded = base64_decode($after, true);
  78. $offset = ctype_digit((string) $decoded) ? ((int) $decoded + 1) : 0;
  79. }
  80. if ($before) {
  81. $decoded = base64_decode($before, true);
  82. $cursor = ctype_digit((string) $decoded) ? (int) $decoded : 0;
  83. $offset = max(0, $cursor - $perPage);
  84. }
  85. }
  86. // 排序:sort=newest(按创建时间倒序)或 sort=oldest(默认,按 id 升序)
  87. $sort = $args['sort'] ?? null;
  88. /*if ($sort !== null && strtolower((string) $sort) === 'newest') {
  89. $query->orderBy('created_at', 'desc')->orderBy('id', 'desc');
  90. } else {
  91. $query->orderBy('id', 'asc');
  92. }*/
  93. $query->orderBy('created_at', 'desc')->orderBy('id', 'desc');
  94. $total = (clone $query)->count();
  95. if ($offset > $total) {
  96. $offset = max(0, $total - $perPage);
  97. }
  98. $items = $query
  99. ->offset($offset)
  100. ->limit($perPage)
  101. ->get();
  102. $currentPage = $total > 0 ? (int) floor($offset / $perPage) + 1 : 1;
  103. return new Paginator(
  104. new LengthAwarePaginator(
  105. $items,
  106. $total,
  107. $perPage,
  108. $currentPage,
  109. ['path' => request()->url()]
  110. )
  111. );
  112. }
  113. }