Skip to content

Commit f5cc09f

Browse files
committed
Merge remote-tracking branch 'origin/spartans_pr_21082025_AC-15054' into spartans_pr_26082025
2 parents 6ce5994 + e082d0a commit f5cc09f

File tree

5 files changed

+830
-17
lines changed

5 files changed

+830
-17
lines changed
Lines changed: 120 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,154 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
55
*/
66

77
declare(strict_types=1);
88

99
namespace Magento\Quote\Plugin;
1010

11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\Framework\Exception\NoSuchEntityException;
1113
use Magento\Framework\Webapi\Rest\Request as RestRequest;
14+
use Magento\Quote\Api\CartRepositoryInterface;
1215
use Magento\Quote\Api\Data\CartItemInterface;
1316
use Magento\Quote\Api\GuestCartItemRepositoryInterface;
17+
use Magento\Quote\Model\QuoteIdMaskFactory;
18+
use Magento\Store\Model\StoreManagerInterface;
19+
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
20+
use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink;
1421

1522
/**
16-
* Update cart id from request param
23+
* Plugin to update cart ID from request and validate product website assignment
1724
*/
1825
class UpdateCartId
1926
{
20-
/**
21-
* @var RestRequest $request
22-
*/
23-
private $request;
24-
2527
/**
2628
* @param RestRequest $request
29+
* @param StoreManagerInterface $storeManager
30+
* @param QuoteIdMaskFactory $quoteIdMaskFactory
31+
* @param CartRepositoryInterface $cartRepository
32+
* @param ProductResource $productResource
33+
* @param ProductWebsiteLink $productWebsiteLink
2734
*/
28-
public function __construct(RestRequest $request)
29-
{
30-
$this->request = $request;
35+
public function __construct(
36+
private readonly RestRequest $request,
37+
private readonly StoreManagerInterface $storeManager,
38+
private readonly QuoteIdMaskFactory $quoteIdMaskFactory,
39+
private readonly CartRepositoryInterface $cartRepository,
40+
private readonly ProductResource $productResource,
41+
private readonly ProductWebsiteLink $productWebsiteLink
42+
) {
3143
}
3244

3345
/**
34-
* Update id from request if param cartId exist
46+
* Before saving a guest cart item, set quote ID from request and validate website assignment
3547
*
36-
* @param GuestCartItemRepositoryInterface $guestCartItemRepository
48+
* @param GuestCartItemRepositoryInterface $subject
3749
* @param CartItemInterface $cartItem
3850
* @return void
51+
* @throws LocalizedException
52+
*
3953
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
4054
*/
4155
public function beforeSave(
42-
GuestCartItemRepositoryInterface $guestCartItemRepository,
56+
GuestCartItemRepositoryInterface $subject,
4357
CartItemInterface $cartItem
4458
): void {
45-
$cartId = $this->request->getParam('cartId');
46-
47-
if ($cartId) {
59+
if ($cartId = $this->request->getParam('cartId')) {
4860
$cartItem->setQuoteId($cartId);
4961
}
62+
63+
$this->validateProductWebsiteAssignment($cartItem);
64+
}
65+
66+
/**
67+
* Validate product's website assignment for guest cart item
68+
*
69+
* @param CartItemInterface $cartItem
70+
* @return void
71+
* @throws LocalizedException
72+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
73+
*/
74+
private function validateProductWebsiteAssignment(CartItemInterface $cartItem): void
75+
{
76+
$sku = $cartItem->getSku();
77+
if (!$sku) {
78+
return;
79+
}
80+
81+
// Skip validation on update; website was already validated on add
82+
if ($cartItem->getItemId()) {
83+
return;
84+
}
85+
86+
$storeId = (int)($cartItem->getStoreId() ?? 0);
87+
88+
if (!$storeId) {
89+
try {
90+
$storeId = (int)$this->storeManager->getStore()->getId();
91+
} catch (\Throwable $e) {
92+
$storeId = 0;
93+
}
94+
}
95+
96+
if (!$storeId) {
97+
try {
98+
$maskedQuoteId = $cartItem->getQuoteId();
99+
if ($maskedQuoteId) {
100+
$quoteIdMask = $this->quoteIdMaskFactory->create()->load($maskedQuoteId, 'masked_id');
101+
$quoteId = (int)$quoteIdMask->getQuoteId();
102+
if ($quoteId) {
103+
$storeId = (int)$this->cartRepository->get($quoteId)->getStoreId();
104+
}
105+
}
106+
} catch (NoSuchEntityException) {
107+
throw new LocalizedException(__('Product that you are trying to add is not available.'));
108+
}
109+
}
110+
111+
if ($storeId) {
112+
$this->validateWebsiteAssignmentBySku($sku, $storeId);
113+
}
114+
}
115+
116+
/**
117+
* Validate by SKU for new items
118+
*
119+
* @param string $sku
120+
* @param int $storeId
121+
* @return void
122+
* @throws LocalizedException
123+
*/
124+
private function validateWebsiteAssignmentBySku(string $sku, int $storeId): void
125+
{
126+
$productId = (int)$this->productResource->getIdBySku($sku);
127+
if (!$productId) {
128+
throw new LocalizedException(__('Product that you are trying to add is not available.'));
129+
}
130+
$websiteIds = $this->productWebsiteLink->getWebsiteIdsByProductId($productId);
131+
if (empty($websiteIds)) {
132+
return;
133+
}
134+
$this->checkProductInWebsite($websiteIds, $storeId);
135+
}
136+
137+
/**
138+
* Validate by product ID for existing items
139+
*
140+
* @param array|null $websiteIds
141+
* @param int $storeId
142+
* @return void
143+
* @throws LocalizedException
144+
* @throws NoSuchEntityException
145+
*/
146+
private function checkProductInWebsite(?array $websiteIds, int $storeId): void
147+
{
148+
$websiteId = $this->storeManager->getStore($storeId)->getWebsiteId();
149+
150+
if (empty($websiteIds) || !in_array($websiteId, $websiteIds, true)) {
151+
throw new LocalizedException(__('Product that you are trying to add is not available.'));
152+
}
50153
}
51154
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Quote\Plugin\Webapi;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Quote\Api\CartItemRepositoryInterface;
13+
use Magento\Quote\Api\CartRepositoryInterface;
14+
use Magento\Quote\Api\Data\CartItemInterface;
15+
use Magento\Store\Model\StoreManagerInterface;
16+
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
17+
use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink;
18+
19+
/**
20+
* Plugin to validate product website assignment for REST API cart operations
21+
*/
22+
class ValidateProductWebsiteAssignment
23+
{
24+
/**
25+
* @param StoreManagerInterface $storeManager
26+
* @param CartRepositoryInterface $cartRepository
27+
* @param ProductResource $productResource
28+
* @param ProductWebsiteLink $productWebsiteLink
29+
*/
30+
public function __construct(
31+
private readonly StoreManagerInterface $storeManager,
32+
private readonly CartRepositoryInterface $cartRepository,
33+
private readonly ProductResource $productResource,
34+
private readonly ProductWebsiteLink $productWebsiteLink
35+
) {
36+
}
37+
38+
/**
39+
* Validate product website assignment before saving cart item
40+
*
41+
* @param CartItemRepositoryInterface $subject
42+
* @param CartItemInterface $cartItem
43+
* @return void
44+
* @throws LocalizedException
45+
*
46+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
47+
*/
48+
public function beforeSave(
49+
CartItemRepositoryInterface $subject,
50+
CartItemInterface $cartItem
51+
): void {
52+
$sku = $cartItem->getSku();
53+
if (!$sku) {
54+
return;
55+
}
56+
57+
// Skip validation on update; website was already validated on add
58+
if ($cartItem->getItemId()) {
59+
return;
60+
}
61+
62+
try {
63+
$quote = $this->cartRepository->getActive($cartItem->getQuoteId());
64+
$this->checkProductWebsiteAssignmentBySku($sku, $quote->getStoreId());
65+
66+
} catch (NoSuchEntityException $e) {
67+
throw new LocalizedException(__('Product that you are trying to add is not available.'));
68+
}
69+
}
70+
71+
/**
72+
* Check product website assignment by SKU
73+
*
74+
* @param string $sku
75+
* @param int $storeId
76+
* @throws LocalizedException
77+
*/
78+
private function checkProductWebsiteAssignmentBySku(string $sku, int $storeId): void
79+
{
80+
$productId = (int)$this->productResource->getIdBySku($sku);
81+
if (!$productId) {
82+
throw new LocalizedException(__('Product that you are trying to add is not available.'));
83+
}
84+
$websiteIds = $this->productWebsiteLink->getWebsiteIdsByProductId($productId);
85+
if (empty($websiteIds)) {
86+
return;
87+
}
88+
$this->validateWebsiteAssignment($websiteIds, $storeId);
89+
}
90+
91+
/**
92+
* Validate product website assignment
93+
*
94+
* @param array|null $websiteIds
95+
* @param int $storeId
96+
* @throws LocalizedException
97+
*/
98+
private function validateWebsiteAssignment(?array $websiteIds, int $storeId): void
99+
{
100+
$websiteId = $this->storeManager->getStore($storeId)->getWebsiteId();
101+
if (empty($websiteIds) || !in_array($websiteId, $websiteIds, true)) {
102+
throw new LocalizedException(__('Product that you are trying to add is not available.'));
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)