From 343134b3bad7995f986c5152af459f2c5c5c74a3 Mon Sep 17 00:00:00 2001 From: Bartosz Juraszewski Date: Sun, 3 Jul 2022 10:26:45 +0000 Subject: [PATCH] Feature/sk 323 --- .env.example | 1 + app/Providers/AppServiceProvider.php | 3 + .../Contracts/DiscountServiceContract.php | 2 +- app/Services/DiscountService.php | 81 +++++++++++-------- app/Services/ProductService.php | 2 +- app/Services/ProductSetService.php | 10 +-- docker-compose.yaml | 12 +++ tests/Feature/ProductSearchDatabaseTest.php | 63 ++++----------- 8 files changed, 83 insertions(+), 91 deletions(-) diff --git a/.env.example b/.env.example index a265b1235..f853b020f 100644 --- a/.env.example +++ b/.env.example @@ -29,6 +29,7 @@ REDIS_CACHE_PASSWORD=null REDIS_CACHE_PORT=6379 SCOUT_DRIVER=database +SCOUT_QUEUE=false EMAIL_HOST=mail.example.com EMAIL_PORT=587 diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index aff4934d8..bb07ea047 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Models\Product; use App\Services\AnalyticsService; use App\Services\AppService; use App\Services\AttributeOptionService; @@ -167,5 +168,7 @@ public function boot(): void return $this; }); + + Product::disableSearchSyncing(); } } diff --git a/app/Services/Contracts/DiscountServiceContract.php b/app/Services/Contracts/DiscountServiceContract.php index 281a00d08..d75d6c5e1 100644 --- a/app/Services/Contracts/DiscountServiceContract.php +++ b/app/Services/Contracts/DiscountServiceContract.php @@ -52,7 +52,7 @@ public function applyDiscountOnProduct( public function applyDiscountsOnProducts(Collection $products): void; - public function applyDiscountsOnProduct(Product $product): void; + public function applyDiscountsOnProduct(Product $product, bool $reindex = true): void; public function applyDiscountOnOrderProduct(OrderProduct $orderProduct, Discount $discount): OrderProduct; diff --git a/app/Services/DiscountService.php b/app/Services/DiscountService.php index b4d4c3d20..9ce430d80 100644 --- a/app/Services/DiscountService.php +++ b/app/Services/DiscountService.php @@ -297,10 +297,7 @@ public function calcCartDiscounts(CartDto $cart, Collection $products): CartReso $price += $schema->getPrice($value, $cartItem->getSchemas()); } $cartValue += $price * $cartItem->getQuantity(); - array_push( - $cartItems, - new CartItemResponse($cartItem->getCartItemId(), $price, $price, $cartItem->getQuantity()), - ); + $cartItems[] = new CartItemResponse($cartItem->getCartItemId(), $price, $price, $cartItem->getQuantity()); } $cartShippingTimeAndDate = $this->shippingTimeDateService->getTimeAndDateForCart($cart, $products); @@ -379,10 +376,10 @@ public function applyDiscountsOnProducts(Collection $products): void } } - public function applyDiscountsOnProduct(Product $product): void + public function applyDiscountsOnProduct(Product $product, bool $reindex = true): void { $salesWithBlockList = $this->getSalesWithBlockList(); - $this->applyAllDiscountsOnProduct($product, $salesWithBlockList); + $this->applyAllDiscountsOnProduct($product, $salesWithBlockList, $reindex); } public function applyDiscountOnOrderProduct(OrderProduct $orderProduct, Discount $discount): OrderProduct @@ -526,13 +523,10 @@ public function calculateDiscount(Discount $discount, bool $updated): void // if job is called after update, then calculate discount for all products, // because it may change the list of related products or target_is_allow_list value if (!$updated) { - $products = $this->allDiscountProducts($discount); + $products = $this->allDiscountProductsIds($discount); } $this->applyDiscountsOnProductsLazy($products, $salesWithBlockList); - - // @phpstan-ignore-next-line - Product::whereIn('id', $products)->searchable(); } public function checkActiveSales(): void @@ -542,28 +536,36 @@ public function checkActiveSales(): void $oldActiveSales = Collection::make($activeSales); - $products = Collection::make(); - $activeSalesIds = $this->activeSales()->pluck('id'); - $sales = $activeSalesIds->diff($oldActiveSales)->merge($oldActiveSales->diff($activeSalesIds)); + $sales = $activeSalesIds + ->diff($oldActiveSales) + ->merge($oldActiveSales->diff($activeSalesIds)); - $sales = Discount::whereIn('id', $sales)->with(['products', 'productSets', 'productSets.products'])->get(); + $sales = Discount::query() + ->whereIn('id', $sales) + ->with(['products', 'productSets', 'productSets.products']) + ->get(); + + $products = Collection::make(); foreach ($sales as $sale) { - $products = $products->merge($this->allDiscountProducts($sale))->unique(); + $products = $products->merge($this->allDiscountProductsIds($sale)); } - $salesWithBlockList = $this->getSalesWithBlockList(); - $this->applyDiscountsOnProductsLazy($products, $salesWithBlockList); - - // @phpstan-ignore-next-line - Product::whereIn('id', $products)->searchable(); + $products = $products->unique(); + $this->applyDiscountsOnProductsLazy( + $products, + $this->getSalesWithBlockList(), + ); Cache::put('sales.active', $activeSalesIds); } - public function applyAllDiscountsOnProduct(Product $product, Collection $salesWithBlockList): void - { + public function applyAllDiscountsOnProduct( + Product $product, + Collection $salesWithBlockList, + bool $reindex = true, + ): void { $sales = $this->sortDiscounts($product->allProductSales($salesWithBlockList)); // prevent error when price_min or price_max is null @@ -598,15 +600,18 @@ public function applyAllDiscountsOnProduct(Product $product, Collection $salesWi // detach and attach only add 2 queries to database, sync add 1 query for every element in given array, $product->sales()->detach(); $product->sales()->attach($productSales->pluck('id')); - $product->searchable(); + + if ($reindex) { + $product->searchable(); + } } - private function allDiscountProducts(Discount $discount): Collection + private function allDiscountProductsIds(Discount $discount): Collection { $products = $discount->allProductsIds(); if (!$discount->target_is_allow_list) { - $products = Product::whereNotIn('id', $products)->pluck('id'); + $products = Product::query()->whereNotIn('id', $products)->pluck('id'); } // Return only ids of products, that should be discounted @@ -883,7 +888,7 @@ private function applyDiscountOnCartItems(Discount $discount, CartDto $cartDto, $cartItem = $this->applyDiscountOnCartItem($discount, $item, $cart); - array_push($cartItems, $cartItem); + $cartItems[] = $cartItem; $cartValue += $cartItem->price_discounted * $item->getQuantity(); } @@ -925,7 +930,7 @@ private function applyDiscountOnCartCheapestItem( if ($price !== $minimalProductPrice) { $newPrice = round($price - $this->calc($price, $discount), 2, PHP_ROUND_HALF_UP); - $cartItem->price_discounted = $newPrice < $minimalProductPrice ? $minimalProductPrice : $newPrice; + $cartItem->price_discounted = max($newPrice, $minimalProductPrice); $cart->cart_total -= ($price - $cartItem->price_discounted) * $cartItem->quantity; } @@ -967,7 +972,7 @@ private function calcPrice(float $price, string $productId, Discount $discount): if ($price !== $minimalProductPrice && $this->checkIsProductInDiscount($productId, $discount)) { $price -= $this->calc($price, $discount); - $price = $price < $minimalProductPrice ? $minimalProductPrice : $price; + $price = max($price, $minimalProductPrice); } return round($price, 2, PHP_ROUND_HALF_UP); @@ -1036,7 +1041,7 @@ private function createConditionGroupsToAttach(array $conditions): array { $result = []; foreach ($conditions as $condition) { - array_push($result, $this->createConditionGroup($condition)); + $result[] = $this->createConditionGroup($condition); } return Collection::make($result)->pluck('id')->all(); } @@ -1293,16 +1298,24 @@ private function applyDiscountsOnProductsLazy(Collection $products, Collection $ 'sets', 'sets.discounts', 'sets.parent', - ]) - ->lazyById(100); + ])->lazyById(100); if ($products->count() > 0) { - $lazyProducts = $lazyProducts - ->filter(fn ($product) => $products->contains($product->getKey())); + $lazyProducts = $lazyProducts->filter( + fn ($product) => $products->contains($product->getKey()), + ); } foreach ($lazyProducts as $product) { - $this->applyAllDiscountsOnProduct($product, $salesWithBlockList); + $this->applyAllDiscountsOnProduct($product, $salesWithBlockList, false); + } + + if ($products->count() > 0) { + // @phpstan-ignore-next-line + Product::query()->whereIn('id', $products->pluck('id'))->searchable(); + } else { + // @phpstan-ignore-next-line + Product::query()->searchable(); } } } diff --git a/app/Services/ProductService.php b/app/Services/ProductService.php index c4a67be8d..f12f8e9d3 100644 --- a/app/Services/ProductService.php +++ b/app/Services/ProductService.php @@ -72,7 +72,7 @@ private function setup(Product $product, ProductCreateDto|ProductUpdateDto $dto) $product->price_min_initial = $priceMin; $product->price_max_initial = $priceMax; $product->available = $this->availabilityService->isProductAvailable($product); - $this->discountService->applyDiscountsOnProduct($product); + $this->discountService->applyDiscountsOnProduct($product, false); return $product; } diff --git a/app/Services/ProductSetService.php b/app/Services/ProductSetService.php index d66a63ea0..70923fa1d 100644 --- a/app/Services/ProductSetService.php +++ b/app/Services/ProductSetService.php @@ -274,15 +274,13 @@ public function flattenSetsTree(Collection $sets, string $relation): Collection */ public function flattenParentsSetsTree(Collection $sets): Collection { - $subsets = Collection::make(); + $parents = $sets->map(fn ($set) => $set->parent)->filter(fn ($set) => $set !== null); - foreach ($sets as $set) { - if ($set->parent) { - $subsets = $subsets->merge($this->flattenParentsSetsTree(Collection::make([$set->parent]))); - } + if ($parents->count() === 0) { + return $sets; } - return $subsets->flatten()->concat($sets->toArray()); + return $sets->merge($this->flattenParentsSetsTree($parents)); } public function reorderProducts(ProductSet $set, ProductsReorderDto $dto): void diff --git a/docker-compose.yaml b/docker-compose.yaml index 36679750c..25243bb16 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -35,6 +35,18 @@ services: - app - mysql_service - redis + schedule: + build: + context: ./docker + dockerfile: Dockerfile-dev + restart: unless-stopped + volumes: + - .:/usr/src/app + command: php artisan schedule:work + depends_on: + - app + - mysql_service + - redis mysql_service: image: mariadb:10.5 restart: unless-stopped diff --git a/tests/Feature/ProductSearchDatabaseTest.php b/tests/Feature/ProductSearchDatabaseTest.php index 8ae0aa1cd..5c26f426e 100644 --- a/tests/Feature/ProductSearchDatabaseTest.php +++ b/tests/Feature/ProductSearchDatabaseTest.php @@ -984,16 +984,11 @@ public function testSearchByAttributeNotId($user): void ) ->assertOk() ->assertJsonCount(1, 'data') - ->assertJsonFragment([ - 'name' => $attribute->name, - ]) ->assertJsonMissing([ - 'id' => $options[0]->getKey(), - 'name' => $options[0]->name, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $options[1]->getKey(), - 'name' => $options[1]->name, + 'id' => $products[1]->getKey(), ]); } @@ -1049,16 +1044,11 @@ public function testSearchByAttributeNotNumber($user): void ) ->assertOk() ->assertJsonCount(1, 'data') - ->assertJsonFragment([ - 'name' => $attribute->name, - ]) ->assertJsonMissing([ - 'id' => $option1->getKey(), - 'value_number' => $option1->value_number, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $option2->getKey(), - 'value_number' => $option2->value_number, + 'id' => $products[1]->getKey(), ]); $this @@ -1093,15 +1083,10 @@ public function testSearchByAttributeNotNumber($user): void ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonFragment([ - 'name' => $attribute->name, - ]) - ->assertJsonFragment([ - 'id' => $option1->getKey(), - 'value_number' => $option1->value_number, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $option2->getKey(), - 'value_number' => $option2->value_number, + 'id' => $products[1]->getKey(), ]); $this @@ -1120,15 +1105,10 @@ public function testSearchByAttributeNotNumber($user): void ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonFragment([ - 'name' => $attribute->name, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $option1->getKey(), - 'value_number' => $option1->value_number, - ]) - ->assertJsonFragment([ - 'id' => $option2->getKey(), - 'value_number' => $option2->value_number, + 'id' => $products[1]->getKey(), ]); } @@ -1184,16 +1164,11 @@ public function testSearchByAttributeNotDate($user): void ) ->assertOk() ->assertJsonCount(1, 'data') - ->assertJsonFragment([ - 'name' => $attribute->name, - ]) ->assertJsonMissing([ - 'id' => $option1->getKey(), - 'value_date' => $option1->value_date, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $option2->getKey(), - 'value_date' => $option2->value_date, + 'id' => $products[1]->getKey(), ]); $this @@ -1229,15 +1204,10 @@ public function testSearchByAttributeNotDate($user): void ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonFragment([ - 'name' => $attribute->name, - ]) - ->assertJsonFragment([ - 'id' => $option1->getKey(), - 'value_date' => $option1->value_date, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $option2->getKey(), - 'value_date' => $option2->value_date, + 'id' => $products[1]->getKey(), ]); $this @@ -1256,15 +1226,10 @@ public function testSearchByAttributeNotDate($user): void ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonFragment([ - 'name' => $attribute->name, - ]) - ->assertJsonFragment([ - 'id' => $option1->getKey(), - 'value_date' => $option1->value_date, + 'id' => $products[0]->getKey(), ]) ->assertJsonFragment([ - 'id' => $option2->getKey(), - 'value_date' => $option2->value_date, + 'id' => $products[1]->getKey(), ]); }