-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #261 from BitBagCommerce/op-282-bulk-actions-fix
OP-282: Bulk actions fix
- Loading branch information
Showing
5 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
<?php | ||
|
||
/* | ||
* This file has been created by developers from BitBag. | ||
* Feel free to contact us once you face any issues or want to start | ||
* You can find more information about us on https://bitbag.io and write us | ||
* an email on [email protected]. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace spec\BitBag\SyliusWishlistPlugin\Validator; | ||
|
||
use BitBag\SyliusWishlistPlugin\Validator\InStockWishlistValidator; | ||
use Doctrine\Common\Collections\ArrayCollection; | ||
use PhpSpec\ObjectBehavior; | ||
use Prophecy\Argument; | ||
use stdClass; | ||
use Sylius\Bundle\CoreBundle\Validator\Constraints\CartItemAvailability; | ||
use Sylius\Bundle\OrderBundle\Controller\AddToCartCommandInterface; | ||
use Sylius\Component\Core\Model\OrderItemInterface; | ||
use Sylius\Component\Core\Model\ProductVariantInterface; | ||
use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; | ||
use Sylius\Component\Order\Model\OrderInterface; | ||
use Symfony\Bundle\FrameworkBundle\Routing\Router; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\Context\ExecutionContextInterface; | ||
|
||
final class InStockWishlistValidatorSpec extends ObjectBehavior | ||
{ | ||
public function let( | ||
AvailabilityCheckerInterface $availabilityChecker, | ||
RequestStack $requestStack, | ||
Router $router, | ||
): void { | ||
$this->beConstructedWith($availabilityChecker, $requestStack, $router); | ||
} | ||
|
||
public function it_is_initializable(): void | ||
{ | ||
$this->shouldHaveType(InStockWishlistValidator::class); | ||
} | ||
|
||
public function it_should_throw_exception_if_value_is_not_instance_of_command_interface( | ||
Constraint $constraint, | ||
stdClass $value, | ||
): void { | ||
$this->shouldThrow(\Exception::class)->during( | ||
'validate', | ||
[$value, $constraint], | ||
); | ||
} | ||
|
||
public function it_should_throw_exception_if_constraint_is_not_instance_of_command_interface( | ||
Constraint $constraint, | ||
AddToCartCommandInterface $value, | ||
): void { | ||
$this->shouldThrow(\Exception::class)->during( | ||
'validate', | ||
[$value, $constraint], | ||
); | ||
} | ||
|
||
public function it_should_throw_exception_if_request_is_null( | ||
RequestStack $requestStack, | ||
AddToCartCommandInterface $value, | ||
): void { | ||
$constraint = new CartItemAvailability(); | ||
|
||
$requestStack->getCurrentRequest()->willReturn(null); | ||
|
||
$this->shouldThrow(\Exception::class)->during( | ||
'validate', | ||
[$value, $constraint], | ||
); | ||
} | ||
|
||
public function it_should_do_nothing_if_the_request_is_not_add_to_cart( | ||
RequestStack $requestStack, | ||
AddToCartCommandInterface $value, | ||
Request $request, | ||
Router $router, | ||
): void { | ||
$constraint = new CartItemAvailability(); | ||
$exampleArray = [ | ||
'_route' => 'bitbag_sylius_wishlist_plugin_shop_locale_wishlist_remove_selected_products', | ||
'_controller' => 'bitbag_sylius_wishlist_plugin.controller.action.remove_selected_products_from_wishlist', | ||
'_locale' => 'en_US', | ||
'wishlistId' => '4', | ||
]; | ||
|
||
$requestStack->getCurrentRequest()->willReturn($request); | ||
$request->getPathInfo()->willReturn('/en_US/wishlist/4/products/delete'); | ||
$router->match('/en_US/wishlist/4/products/delete')->willReturn($exampleArray); | ||
|
||
$this->validate($value, $constraint)->shouldReturn(null); | ||
} | ||
|
||
public function it_should_do_nothing_if_current_stock_is_not_empty( | ||
AvailabilityCheckerInterface $availabilityChecker, | ||
RequestStack $requestStack, | ||
AddToCartCommandInterface $value, | ||
Request $request, | ||
Router $router, | ||
OrderItemInterface $cartItem, | ||
ExecutionContextInterface $context, | ||
ProductVariantInterface $variant, | ||
OrderInterface $order, | ||
): void { | ||
$constraint = new CartItemAvailability(); | ||
$exampleArray = ['_route' => 'bitbag_sylius_wishlist_plugin_shop_locale_wishlist_add_selected_products', | ||
'_controller' => 'bitbag_sylius_wishlist_plugin.controller.action.add_selected_products_to_cart', | ||
'_locale' => 'en_US', | ||
'wishlistId' => '4', | ||
]; | ||
|
||
$requestStack->getCurrentRequest()->willReturn($request); | ||
$request->getPathInfo()->willReturn('/en_US/wishlist/4/products/add'); | ||
$router->match('/en_US/wishlist/4/products/add')->willReturn($exampleArray); | ||
$value->getCartItem()->willReturn($cartItem); | ||
$cartItem->getVariant()->willReturn($variant); | ||
$cartItem->getQuantity()->willReturn(5); | ||
$value->getCart()->willReturn($order); | ||
$itemsCollection = new ArrayCollection([$cartItem->getWrappedObject()]); | ||
$order->getItems()->willReturn($itemsCollection); | ||
$cartItem->equals(Argument::any())->willReturn(false); | ||
$availabilityChecker->isStockSufficient($variant, 5)->willReturn(true); | ||
|
||
$context->addViolation(Argument::any(), Argument::any())->shouldNotBeCalled(); | ||
|
||
$this->validate($value, $constraint)->shouldReturn(null); | ||
} | ||
|
||
public function it_should_do_build_violation_if_current_stock_is_empty_and_user_added_to_cart_item_from_wishlist( | ||
AvailabilityCheckerInterface $availabilityChecker, | ||
RequestStack $requestStack, | ||
AddToCartCommandInterface $value, | ||
Request $request, | ||
Router $router, | ||
OrderItemInterface $cartItem, | ||
OrderInterface $order, | ||
ProductVariantInterface $variant, | ||
ExecutionContextInterface $context, | ||
): void { | ||
$constraint = new CartItemAvailability(); | ||
$constraint->message = 'sylius.cart_item.not_available'; | ||
$exampleArray = ['_route' => 'bitbag_sylius_wishlist_plugin_shop_locale_wishlist_add_selected_products', | ||
'_controller' => 'bitbag_sylius_wishlist_plugin.controller.action.add_selected_products_to_cart', | ||
'_locale' => 'en_US', | ||
'wishlistId' => '4', | ||
]; | ||
|
||
$requestStack->getCurrentRequest()->willReturn($request); | ||
$request->getPathInfo()->willReturn('/en_US/wishlist/4/products/add'); | ||
$router->match('/en_US/wishlist/4/products/add')->willReturn($exampleArray); | ||
$value->getCartItem()->willReturn($cartItem); | ||
$cartItem->getVariant()->willReturn($variant); | ||
$cartItem->getQuantity()->willReturn(5); | ||
$value->getCart()->willReturn($order); | ||
$itemsCollection = new ArrayCollection([$cartItem->getWrappedObject()]); | ||
$order->getItems()->willReturn($itemsCollection); | ||
$cartItem->equals(Argument::any())->willReturn(false); | ||
$availabilityChecker->isStockSufficient($variant, 5)->willReturn(false); | ||
$variant->getInventoryName()->willReturn('Red T-shirt'); | ||
|
||
$context->addViolation( | ||
$constraint->message, | ||
['%itemName%' => 'Red T-shirt'], | ||
)->shouldBeCalled(); | ||
|
||
$this->initialize($context); | ||
$this->validate($value, $constraint); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<container xmlns="http://symfony.com/schema/dic/services" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/services | ||
http://symfony.com/schema/dic/services/services-1.0.xsd"> | ||
|
||
<services> | ||
<service class="BitBag\SyliusWishlistPlugin\Validator\InStockWishlistValidator" | ||
id="bitbag.sylius_wishlist_plugin.validator.wishlist_in_stock_validator" | ||
decorates="sylius.validator.cart_item_availability"> | ||
<argument type="service" id="sylius.availability_checker.default" /> | ||
<argument type="service" id="request_stack" /> | ||
<argument type="service" id="router" /> | ||
<tag name="validator.constraint_validator" alias="bitbag_sylius_wishlist_plugin_validator_wishlist_in_stock_validator"/> | ||
</service> | ||
</services> | ||
|
||
</container> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd"> | ||
<class name="Sylius\Bundle\OrderBundle\Controller\AddToCartCommand"> | ||
<constraint name="BitBag\SyliusWishlistPlugin\Validator\Constraints\InStockWishlistConstraint"> | ||
<option name="message">sylius.cart_item.not_available</option> | ||
<option name="groups"> | ||
<value>wishlist_in_stock</value> | ||
</option> | ||
</constraint> | ||
</class> | ||
</constraint-mapping> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
/* | ||
* This file has been created by developers from BitBag. | ||
* Feel free to contact us once you face any issues or want to start | ||
* You can find more information about us on https://bitbag.io and write us | ||
* an email on [email protected]. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BitBag\SyliusWishlistPlugin\Validator\Constraints; | ||
|
||
use Symfony\Component\Validator\Constraint; | ||
|
||
final class InStockWishlistConstraint extends Constraint | ||
{ | ||
public string $message = 'sylius.cart_item.not_available'; | ||
|
||
public const ADD_PRODUCTS_ROUTE = 'bitbag_sylius_wishlist_plugin_shop_locale_wishlist_add_selected_products'; | ||
|
||
public function validatedBy(): string | ||
{ | ||
return 'bitbag_sylius_wishlist_plugin_validator_wishlist_in_stock_validator'; | ||
} | ||
|
||
public function getTargets(): string | ||
{ | ||
return self::CLASS_CONSTRAINT; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php | ||
|
||
/* | ||
* This file has been created by developers from BitBag. | ||
* Feel free to contact us once you face any issues or want to start | ||
* You can find more information about us on https://bitbag.io and write us | ||
* an email on [email protected]. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BitBag\SyliusWishlistPlugin\Validator; | ||
|
||
use BitBag\SyliusWishlistPlugin\Validator\Constraints\InStockWishlistConstraint; | ||
use Sylius\Bundle\CoreBundle\Validator\Constraints\CartItemAvailability; | ||
use Sylius\Bundle\OrderBundle\Controller\AddToCartCommandInterface; | ||
use Sylius\Component\Core\Model\OrderItemInterface; | ||
use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; | ||
use Sylius\Component\Inventory\Model\StockableInterface; | ||
use Sylius\Component\Order\Model\OrderInterface; | ||
use Symfony\Bundle\FrameworkBundle\Routing\Router; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||
use Symfony\Component\Routing\Exception\ResourceNotFoundException; | ||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\ConstraintValidator; | ||
use Webmozart\Assert\Assert; | ||
|
||
final class InStockWishlistValidator extends ConstraintValidator | ||
{ | ||
public function __construct( | ||
private AvailabilityCheckerInterface $availabilityChecker, | ||
private RequestStack $requestStack, | ||
private Router $router, | ||
) { | ||
} | ||
|
||
public function validate(mixed $value, Constraint $constraint): void | ||
{ | ||
Assert::isInstanceOf($value, AddToCartCommandInterface::class); | ||
|
||
Assert::isInstanceOf($constraint, CartItemAvailability::class); | ||
|
||
$request = $this->requestStack->getCurrentRequest(); | ||
|
||
Assert::notNull($request); | ||
|
||
try { | ||
$route = $this->router->match($request->getPathInfo()); | ||
} catch (ResourceNotFoundException $exception) { | ||
throw new AccessDeniedHttpException('Access denied'); | ||
} | ||
|
||
if (array_key_exists('_route', $route) && | ||
InStockWishlistConstraint::ADD_PRODUCTS_ROUTE !== $route['_route']) { | ||
return; | ||
} | ||
|
||
/** @var OrderItemInterface $cartItem */ | ||
$cartItem = $value->getCartItem(); | ||
|
||
/** @var StockableInterface $variant */ | ||
$variant = $cartItem->getVariant(); | ||
|
||
$isStockSufficient = $this->availabilityChecker->isStockSufficient( | ||
$variant, | ||
$cartItem->getQuantity() + $this->getExistingCartItemQuantityFromCart($value->getCart(), $cartItem), | ||
); | ||
|
||
if (!$isStockSufficient) { | ||
$this->context->addViolation( | ||
$constraint->message, | ||
['%itemName%' => $variant->getInventoryName()], | ||
); | ||
} | ||
} | ||
|
||
private function getExistingCartItemQuantityFromCart(OrderInterface $cart, OrderItemInterface $cartItem): int | ||
{ | ||
foreach ($cart->getItems() as $existingCartItem) { | ||
if ($existingCartItem->equals($cartItem)) { | ||
return $existingCartItem->getQuantity(); | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
} |