From bd51f9abe027b27a15d6ae04e81847e5eafdff43 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Mon, 6 Jul 2026 15:47:46 +0700 Subject: [PATCH] refactor: update price handling in CuttingResultPriceResolver and HomepageService for improved data structure and consistency --- .../Manage/CuttingResultPriceResolver.php | 36 +++++++++----- app/Services/System/HomepageService.php | 48 +++++++++++++------ 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/app/Services/Manage/CuttingResultPriceResolver.php b/app/Services/Manage/CuttingResultPriceResolver.php index 0fb84cf..a3b3d93 100644 --- a/app/Services/Manage/CuttingResultPriceResolver.php +++ b/app/Services/Manage/CuttingResultPriceResolver.php @@ -53,7 +53,11 @@ public function latestPricesForVariant(int $productVariantId): array $price = $this->resolve($productVariantId, $priceType); if ($price !== null) { - $prices[] = $price; + $prices[] = [ + 'price_type' => $price->price_type->value, + 'price' => (int) $price->price, + 'price_formatted' => $price->price_formatted, + ]; } } @@ -63,9 +67,9 @@ public function latestPricesForVariant(int $productVariantId): array public function latestPricesForVariants(array $variantIds): Collection { - return $this->cacheRemember('prices:variants:'.md5(implode(',', $variantIds)), 900, function () use ($variantIds) { + $cached = $this->cacheRemember('prices:variants:'.md5(implode(',', $variantIds)), 900, function () use ($variantIds) { if (empty($variantIds)) { - return collect(); + return []; } $cuttingPrices = CuttingResultPrice::query() @@ -83,24 +87,34 @@ public function latestPricesForVariants(array $variantIds): Collection ->get() ->groupBy(fn (ProductPrice $price) => $price->variant_id.'-'.$price->type->value); - $results = collect(); + // Store only plain arrays — never Eloquent models — to avoid + // __PHP_Incomplete_Class when Redis deserializes across requests. + $results = []; foreach ($variantIds as $variantId) { foreach (PriceType::cases() as $priceType) { $key = $variantId.'-'.$priceType->value; if ($cuttingPrices->has($key)) { - $results->push($cuttingPrices->get($key)); + $cp = $cuttingPrices->get($key); + $results[$variantId][] = [ + 'price_type' => $cp->price_type->value, + 'price' => (int) $cp->price, + 'price_formatted' => $cp->price_formatted, + ]; } elseif ($productPrices->has($key)) { $pp = $productPrices->get($key)->first(); - $cp = new CuttingResultPrice; - $cp->product_variant_id = $variantId; - $cp->price_type = $priceType; - $cp->price = $pp->price; - $results->push($cp); + $results[$variantId][] = [ + 'price_type' => $priceType->value, + 'price' => (int) $pp->price, + 'price_formatted' => 'Rp '.number_format((int) $pp->price, 0, ',', '.'), + ]; } } } - return $results->groupBy('product_variant_id'); + return $results; }); + + // Rebuild as a Collection keyed by variant ID (matching original contract) + return collect(is_array($cached) ? $cached : [])->map(fn ($prices) => collect($prices)); } } diff --git a/app/Services/System/HomepageService.php b/app/Services/System/HomepageService.php index dbc22a2..7457d40 100644 --- a/app/Services/System/HomepageService.php +++ b/app/Services/System/HomepageService.php @@ -2,6 +2,7 @@ namespace App\Services\System; +use App\Enums\PriceType; use App\Models\Category; use App\Models\Product; use App\Models\SystemConfiguration; @@ -51,7 +52,7 @@ public function pageData(): array }); } - private function getProducts() + private function getProducts(): array { $products = Product::getActiveWithVariantsAndCategories(); @@ -61,22 +62,39 @@ private function getProducts() $allPricesByVariant = $this->cuttingResultPriceResolver->latestPricesForVariants($allVariantIds); + // Convert entirely to plain arrays — never store Eloquent models in + // Redis, as PHP serialize/unserialize can produce __PHP_Incomplete_Class + // and json_encode may produce {} instead of [] for keyed collections. return $products->map(function ($product) use ($allPricesByVariant) { - $product->variants->each(function ($variant) use ($allPricesByVariant) { - $variant->setAttribute('images', MediaPresenter::collection($variant, 'images')); + $variants = $product->variants->map(function ($variant) use ($allPricesByVariant) { $variantPrices = $allPricesByVariant->get($variant->id, collect()); - $variant->setAttribute( - 'prices', - $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(), - ); - }); - return $product; - })->values(); + return [ + 'id' => $variant->id, + 'name' => $variant->name, + 'stock' => $variant->stock, + 'images' => MediaPresenter::collection($variant, 'images'), + 'prices' => $variantPrices->map(fn ($price) => [ + 'type' => $price['price_type'], + 'type_label' => PriceType::from($price['price_type'])->label(), + 'price' => $price['price'], + 'price_formatted' => $price['price_formatted'], + ])->values()->all(), + ]; + })->values()->all(); + + return [ + 'id' => $product->id, + 'name' => $product->name, + 'description' => $product->description, + 'slug' => $product->slug ?? null, + 'categories' => $product->categories->map(fn ($cat) => [ + 'id' => $cat->id, + 'name' => $cat->name, + 'slug' => $cat->slug, + ])->values()->all(), + 'variants' => $variants, + ]; + })->values()->all(); } }