PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
← All changes | app/Http/Controllers/ShopController.php +40 -7 1.4.2 → 1.6.5 View file →
@@ -41,9 +41,17 @@
41 41 $status = ["post_status" => ["column" => "post_status", "operator" => "in", "value" => ["publish"]]];
42 42 $allowOutOfStock = $request->get('allow_out_of_stock', false) == true;
43 43 $cursor = $request->get('cursor', null);
44 44 $orderType = $request->get('order_type', 'DESC');
45 - $with = $request->get('with', []);
45 + // The storefront never chooses its own eager loads. This route is public
46 + // (frontend_routes.php, PublicPolicy), and forwarding the request's `with`
47 + // let an anonymous caller walk arbitrary relation chains — e.g.
48 + // `?with[]=orderItems.order.customer.wpUser` reached the WordPress users
49 + // table. The two relations below are the ones the appends applied after
50 + // this call already touch (`thumbnail` reads `detail`, `has_subscription`
51 + // reads `variants`), so the response shape is unchanged and they are now
52 + // eager loaded instead of lazily fetched per row.
53 + $with = ['detail', 'variants'];
46 54
47 55 // Shortcode filter params from AJAX
48 56 $includeIds = array_slice(array_values(array_filter(array_map('intval', (array) $request->get('include_ids', [])), function ($id) { return $id > 0; })), 0, 100);
49 57 $excludeIds = array_slice(array_values(array_filter(array_map('intval', (array) $request->get('exclude_ids', [])), function ($id) { return $id > 0; })), 0, 100);
@@ -70,15 +78,37 @@
70 78 ];
71 79
72 80 $products = ShopResource::get($params);
73 81
82 + $collection = $products['products']->getCollection();
83 +
84 + // The appended has_subscription accessor reads $product->variants during
85 + // serialization, so variants reach the response even without with[]=variants.
86 + // Eager-load them once for the whole page (single query) so the sensitive
87 + // fields can be hidden before serialization.
88 + $collection->loadMissing('variants');
89 +
74 90 $products['products']->setCollection(
75 - $products['products']->getCollection()->transform(function ($product) {
91 + $collection->transform(function ($product) {
76 92 $product->setAppends(['view_url', 'has_subscription', 'thumbnail']);
77 93 $product->makeHidden(['post_content']);
78 94 if ($product->detail !== null) {
79 95 $product->detail->makeHidden(['item_cost', 'editing_stage', 'stock', 'manage_stock', 'manage_cost', 'settings']);
80 96 }
97 + $product->variants->each(function ($variant) {
98 + $variant->makeHidden(['item_cost', 'manage_cost', 'manage_stock', 'total_stock', 'available', 'committed', 'on_hold']);
99 +
100 + // other_info is a JSON blob that makeHidden cannot reach into; strip its
101 + // admin-only keys while keeping the storefront display fields (payment_type,
102 + // repeat_interval, trial_days, billing_summary, weight/dimensions).
103 + $info = $variant->other_info;
104 + if (is_array($info)) {
105 + foreach (['tax_class', 'tax_exempt', 'package_slug', 'bundle_child_ids', 'variation_type', 'is_bundle_product'] as $internalKey) {
106 + unset($info[$internalKey]);
107 + }
108 + $variant->other_info = $info;
109 + }
110 + });
81 111 return $product;
82 112 })
83 113 );
84 114
@@ -195,11 +225,12 @@
195 225
196 226 $perPage = $request->get('per_page', 10);
197 227 $products['total'] = $total;
198 228 $products['last_page'] = max((int)ceil($total / $perPage), 1);
229 + $hideExcerpt = filter_var($request->get('hide_excerpt', false), FILTER_VALIDATE_BOOLEAN);
199 230 ob_start();
200 231 if (($products['total'])) {
201 - (new ProductListRenderer(Arr::get($products, 'products.products')))->renderProductList();
232 + (new ProductListRenderer(Arr::get($products, 'products.products'), null, null, ['hide_excerpt' => $hideExcerpt]))->renderProductList();
202 233 } else {
203 234 ProductRenderer::renderNoProductFound();
204 235 }
205 236
@@ -286,8 +317,9 @@
286 317
287 318 $searchValue = $request->getSafe('post_title', 'sanitize_text_field');
288 319 $urlMode = $request->getSafe('url_mode', 'sanitize_text_field');
289 320 $termId = $request->getSafe('termId', 'intval');
321 + $showThumbnail = filter_var($request->get('show_thumbnail', true), FILTER_VALIDATE_BOOLEAN);
290 322
291 323 $defaultFilters =
292 324 [
293 325 "wildcard" => $searchValue,
@@ -295,10 +327,10 @@
295 327
296 328 $status = ["post_status" => ["column" => "post_status", "operator" => "in", "value" => ["publish"]]];
297 329
298 330 $params = [
299 - "select" => ['guid', 'post_title'],
300 - "with" => ['wpTerms'],
331 + "select" => ['ID', 'guid', 'post_title'],
332 + "with" => ['wpTerms', 'detail.galleryImage'],
301 333 "selected_status" => true,
302 334 "status" => $status,
303 335 "default_filters" => $defaultFilters,
304 336 ];
@@ -313,13 +345,14 @@
313 345 $products = $results['products'];
314 346 ob_start();
315 347
316 348 (new SearchBarRenderer([
317 - 'url_mode' => $urlMode
349 + 'url_mode' => $urlMode,
350 + 'show_thumbnail' => $showThumbnail,
318 351 ]))->renderResultItems($products);
319 352
320 353 $view = ob_get_clean();
321 - return $this->response->json([
354 + return $this->response->sendSuccess([
322 355 'htmlView' => $view
323 356 ]);
324 357 }
325 358 }