perf: implement batch-fetching for variant prices to optimize query performance in OrderService and HomeController
This commit is contained in:
parent
747836be91
commit
f83def0e8a
@ -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(),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -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<int> $variantIds
|
||||
* @return Collection<int, Collection<int, CuttingResultPrice>>
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user