diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index f08d3ed..7a884dbe 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -26,20 +26,24 @@ public function index(): Response $products = Product::getActiveWithVariantsAndCategories(); - $products = $products->map(function ($product) { - $product->variants->each(function ($variant) { + $allVariantIds = $products + ->flatMap(fn ($product) => $product->variants->pluck('id')) + ->all(); + + $allPricesByVariant = $this->cuttingResultPriceResolver->latestPricesForVariants($allVariantIds); + + $products = $products->map(function ($product) use ($allPricesByVariant) { + $product->variants->each(function ($variant) use ($allPricesByVariant) { $variant->setAttribute('images', MediaPresenter::collection($variant, 'images')); + $variantPrices = $allPricesByVariant->get($variant->id, collect()); $variant->setAttribute( 'prices', - collect($this->cuttingResultPriceResolver->latestPricesForVariant($variant->id)) - ->map(fn ($price) => [ - 'type' => $price->price_type->value, - 'type_label' => $price->price_type->label(), - 'price' => (int) $price->price, - 'price_formatted' => $price->price_formatted, - ]) - ->values() - ->all(), + $variantPrices->map(fn ($price) => [ + 'type' => $price->price_type->value, + 'type_label' => $price->price_type->label(), + 'price' => (int) $price->price, + 'price_formatted' => $price->price_formatted, + ])->values()->all(), ); }); diff --git a/app/Services/Manage/CuttingResultPriceResolver.php b/app/Services/Manage/CuttingResultPriceResolver.php index a522b07..3abf190 100644 --- a/app/Services/Manage/CuttingResultPriceResolver.php +++ b/app/Services/Manage/CuttingResultPriceResolver.php @@ -4,6 +4,7 @@ use App\Enums\PriceType; use App\Models\CuttingResultPrice; +use Illuminate\Support\Collection; class CuttingResultPriceResolver { @@ -36,4 +37,31 @@ public function latestPricesForVariant(int $productVariantId): array return $prices; } + + /** + * Batch-fetch latest prices for multiple variant IDs in a single query. + * Returns a Collection keyed by product_variant_id, where each value is + * a Collection of CuttingResultPrice (one per price type, the latest). + * + * @param list $variantIds + * @return Collection> + */ + public function latestPricesForVariants(array $variantIds): Collection + { + if (empty($variantIds)) { + return collect(); + } + + return CuttingResultPrice::query() + ->whereIn('product_variant_id', $variantIds) + ->whereHas('cutting', fn ($query) => $query->verified()) + ->join('cuttings', 'cutting_result_prices.cutting_id', '=', 'cuttings.id') + ->orderByDesc('cuttings.created_at') + ->select('cutting_result_prices.*') + ->get() + ->groupBy(fn (CuttingResultPrice $price) => $price->product_variant_id.'-'.$price->price_type->value) + ->map(fn (Collection $group) => $group->first()) + ->values() + ->groupBy('product_variant_id'); + } } diff --git a/app/Services/Manage/OrderService.php b/app/Services/Manage/OrderService.php index 8655e43..9f18b49 100644 --- a/app/Services/Manage/OrderService.php +++ b/app/Services/Manage/OrderService.php @@ -136,7 +136,7 @@ public function catalogItems(?Order $order = null, ?User $user = null): Collecti ? $order->items()->pluck('product_variant_id')->all() : ($user ? $this->draftItemsQuery($user)->pluck('product_variant_id')->all() : []); - return Product::query() + $products = Product::query() ->with([ 'variants' => fn ($query) => $query ->with('media') @@ -153,19 +153,29 @@ public function catalogItems(?Order $order = null, ?User $user = null): Collecti } }) ->orderBy('name') - ->get() - ->each(function (Product $product): void { - $product->variants->each(function (ProductVariant $variant): void { - $variant->setAttribute( - 'images', - MediaPresenter::collection($variant, 'images'), - ); - $variant->setAttribute( - 'prices', - $this->presentVariantPrices($variant->id), - ); - }); + ->get(); + + $allVariantIds = $products + ->flatMap(fn (Product $product) => $product->variants->pluck('id')) + ->all(); + + $allPricesByVariant = $this->cuttingResultPriceResolver->latestPricesForVariants($allVariantIds); + + return $products->each(function (Product $product) use ($allPricesByVariant): void { + $product->variants->each(function (ProductVariant $variant) use ($allPricesByVariant): void { + $variant->setAttribute( + 'images', + MediaPresenter::collection($variant, 'images'), + ); + $variant->setAttribute( + 'prices', + $this->presentVariantPricesFromCollection( + $variant->id, + $allPricesByVariant, + ), + ); }); + }); } public function findForEdit(Order $order): Order @@ -175,7 +185,14 @@ public function findForEdit(Order $order): Order 'items.productVariant.media', ]); - $order->items->each(function (OrderItem $item): void { + $variantIds = $order->items + ->filter(fn (OrderItem $item) => $item->productVariant !== null) + ->map(fn (OrderItem $item) => $item->productVariant->id) + ->all(); + + $allPricesByVariant = $this->cuttingResultPriceResolver->latestPricesForVariants($variantIds); + + $order->items->each(function (OrderItem $item) use ($allPricesByVariant): void { $variant = $item->productVariant; if ($variant) { @@ -187,7 +204,10 @@ public function findForEdit(Order $order): Order ); $variant->setAttribute( 'prices', - $this->presentVariantPrices($variant->id), + $this->presentVariantPricesFromCollection( + $variant->id, + $allPricesByVariant, + ), ); } }); @@ -767,4 +787,21 @@ private function presentVariantPrices(int $variantId): array ->values() ->all(); } + + private function presentVariantPricesFromCollection(int $variantId, Collection $allPricesByVariant): array + { + return $allPricesByVariant + ->get($variantId, collect()) + ->map(fn ($price) => [ + 'type' => $price->price_type->value, + 'type_label' => $price->price_type->label(), + 'price' => (int) $price->price, + 'price_formatted' => $price->price_formatted, + 'price_input' => (string) $price->price, + 'cost_per_unit' => (int) $price->cost_per_unit, + 'cost_per_unit_formatted' => $price->cost_per_unit_formatted, + ]) + ->values() + ->all(); + } }