1414
1515class ProductController extends Controller
1616{
17+ private const PESTS = [
18+ "aphids " , "thrips " , "spider mites " , "lead miners " , "scale " ,
19+ "whiteflies " , "earwigs " , "cutworms " , "mealybugs " , "fungus gnats "
20+ ];
21+
22+ private const NORMAL_SLOW_PROFILE = 2 ; // seconds
23+ private const EXTREMELY_SLOW_PROFILE = 24 ;
24+
1725 public function __construct (
1826 private OrderService $ orderService
1927 ) {}
@@ -22,8 +30,13 @@ public function __construct(
2230 * Get all products with reviews
2331 * Extracted from the original /products route
2432 */
25- public function index (): JsonResponse
33+ public function index (Request $ request ): JsonResponse
2634 {
35+ $ fetchPromotions = $ request ->query ('fetch_promotions ' );
36+ $ inStockOnly = $ request ->query ('in_stock_only ' );
37+ $ runSlowProfile = env ('RUN_SLOW_PROFILE ' , true );
38+ $ timeoutSeconds = $ fetchPromotions ? self ::EXTREMELY_SLOW_PROFILE : self ::NORMAL_SLOW_PROFILE ;
39+
2740 // Generate random number to determine cache key strategy
2841 $ randomValue = rand (0 , 99 );
2942
@@ -43,12 +56,16 @@ public function index(): JsonResponse
4356 }
4457
4558 // Cache miss - fetch from database
46- $ products = Product::with ('reviews ' )->get ();
47-
59+ // N+1 query: first get all products, then query reviews for each product individually
60+ $ products = Product::all ();
61+
4862 // Transform to match original format
63+ // This causes N+1 queries - for each product, a separate query is made to fetch reviews
4964 $ completeProductsWithReviews = $ products ->map (function ($ product ) {
5065 $ productArray = $ product ->toArray ();
51- $ productArray ['reviews ' ] = $ product ->reviews ->map (function ($ review ) {
66+ // Accessing $product->reviews triggers a lazy load query for each product
67+ $ reviews = DB ::table ('reviews ' )->where ('productid ' , $ product ->id )->get ();
68+ $ productArray ['reviews ' ] = $ reviews ->map (function ($ review ) {
5269 return [
5370 'id ' => $ review ->id ,
5471 'productid ' => $ review ->productid ,
@@ -58,10 +75,35 @@ public function index(): JsonResponse
5875 'created ' => $ review ->created ,
5976 ];
6077 })->toArray ();
61-
78+
6279 return $ productArray ;
6380 })->toArray ();
64-
81+
82+ // Simulate computation-heavy work when RUN_SLOW_PROFILE is enabled
83+ if ($ runSlowProfile ) {
84+ $ startTime = microtime (true );
85+ $ descriptions = array_column ($ completeProductsWithReviews , 'description ' );
86+ $ loop = count ($ descriptions ) * 6 + ($ fetchPromotions ? 2 : -1 );
87+
88+ for ($ i = 0 ; $ i < $ loop * 10 ; $ i ++) {
89+ $ timeDelta = microtime (true ) - $ startTime ;
90+ if ($ timeDelta > $ timeoutSeconds ) {
91+ break ;
92+ }
93+
94+ foreach ($ descriptions as $ index => $ description ) {
95+ foreach (self ::PESTS as $ pest ) {
96+ if ($ inStockOnly && !isset ($ completeProductsWithReviews [$ index ])) {
97+ continue ;
98+ }
99+ if (str_contains ($ description ?? '' , $ pest )) {
100+ array_splice ($ completeProductsWithReviews , $ index , 1 );
101+ }
102+ }
103+ }
104+ }
105+ }
106+
65107 // Store in cache (1 hour TTL for fixed key, random keys will never be retrieved anyway)
66108 Cache::put ($ cacheKey , $ completeProductsWithReviews , 3600 );
67109
0 commit comments