66declare (strict_types=1 );
77namespace Magento \Sales \Model \Reorder ;
88
9+ use Magento \Catalog \Api \Data \ProductInterface ;
910use Magento \Catalog \Api \ProductRepositoryInterface ;
1011use Magento \Catalog \Model \Product ;
12+ use Magento \Catalog \Model \ResourceModel \Product \Collection ;
1113use Magento \Framework \Exception \InputException ;
1214use Magento \Framework \Exception \NoSuchEntityException ;
1315use Magento \Quote \Api \CartRepositoryInterface ;
1416use Magento \Quote \Api \Data \CartInterface ;
1517use Magento \Quote \Model \Cart \CustomerCartResolver ;
18+ use Magento \Quote \Model \Quote as Quote ;
19+ use Magento \Sales \Api \Data \OrderItemInterface ;
1620use Magento \Sales \Helper \Reorder as ReorderHelper ;
1721use Magento \Sales \Model \Order \Item ;
1822use Magento \Sales \Model \OrderFactory ;
23+ use Magento \Sales \Model \ResourceModel \Order \Item \Collection as ItemCollection ;
24+ use Magento \Catalog \Model \ResourceModel \Product \CollectionFactory as ProductCollectionFactory ;
1925
2026/**
2127 * Allows customer quickly to reorder previously added products and put them to the Cart
@@ -81,28 +87,36 @@ class Reorder
8187 */
8288 private $ customerCartProvider ;
8389
90+ /**
91+ * @var ProductCollectionFactory
92+ */
93+ private $ productCollectionFactory ;
94+
8495 /**
8596 * @param OrderFactory $orderFactory
8697 * @param CustomerCartResolver $customerCartProvider
8798 * @param CartRepositoryInterface $cartRepository
8899 * @param ProductRepositoryInterface $productRepository
89100 * @param ReorderHelper $reorderHelper
90101 * @param \Psr\Log\LoggerInterface $logger
102+ * @param ProductCollectionFactory $productCollectionFactory
91103 */
92104 public function __construct (
93105 OrderFactory $ orderFactory ,
94106 CustomerCartResolver $ customerCartProvider ,
95107 CartRepositoryInterface $ cartRepository ,
96108 ProductRepositoryInterface $ productRepository ,
97109 ReorderHelper $ reorderHelper ,
98- \Psr \Log \LoggerInterface $ logger
110+ \Psr \Log \LoggerInterface $ logger ,
111+ ProductCollectionFactory $ productCollectionFactory
99112 ) {
100113 $ this ->orderFactory = $ orderFactory ;
101114 $ this ->cartRepository = $ cartRepository ;
102115 $ this ->productRepository = $ productRepository ;
103116 $ this ->reorderHelper = $ reorderHelper ;
104117 $ this ->logger = $ logger ;
105118 $ this ->customerCartProvider = $ customerCartProvider ;
119+ $ this ->productCollectionFactory = $ productCollectionFactory ;
106120 }
107121
108122 /**
@@ -133,10 +147,7 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu
133147 return $ this ->prepareOutput ($ cart );
134148 }
135149
136- $ items = $ order ->getItemsCollection ();
137- foreach ($ items as $ item ) {
138- $ this ->addOrderItem ($ cart , $ item );
139- }
150+ $ this ->addItemsToCart ($ cart , $ order ->getItemsCollection (), $ storeId );
140151
141152 try {
142153 $ this ->cartRepository ->save ($ cart );
@@ -145,52 +156,109 @@ public function execute(string $orderNumber, string $storeId): Data\ReorderOutpu
145156 $ this ->addError ($ e ->getMessage ());
146157 }
147158
148- $ cart = $ this ->cartRepository ->get ($ cart ->getId ());
159+ $ savedCart = $ this ->cartRepository ->get ($ cart ->getId ());
149160
150- return $ this ->prepareOutput ($ cart );
161+ return $ this ->prepareOutput ($ savedCart );
151162 }
152163
153164 /**
154- * Convert order item to quote item
165+ * Add collections of order items to cart.
155166 *
156- * @param \Magento\Quote\Model\Quote $cart
157- * @param Item $orderItem
167+ * @param Quote $cart
168+ * @param ItemCollection $orderItems
169+ * @param string $storeId
158170 * @return void
159171 */
160- private function addOrderItem ( \ Magento \ Quote \ Model \ Quote $ cart , $ orderItem ): void
172+ private function addItemsToCart ( Quote $ cart , ItemCollection $ orderItems , string $ storeId ): void
161173 {
162- /* @var $orderItem Item */
163- if ($ orderItem ->getParentItem () === null ) {
164- $ info = $ orderItem ->getProductOptionByCode ('info_buyRequest ' );
165- $ info = new \Magento \Framework \DataObject ($ info );
166- $ info ->setQty ($ orderItem ->getQtyOrdered ());
167-
168- try {
169- /** @var Product $product */
170- $ product = $ this ->productRepository ->getById ($ orderItem ->getProductId (), false , null , true );
171- } catch (NoSuchEntityException $ e ) {
174+ $ orderItemProductIds = [];
175+ /** @var \Magento\Sales\Model\Order\Item[] $orderItemsByProductId */
176+ $ orderItemsByProductId = [];
177+
178+ /** @var \Magento\Sales\Model\Order\Item $item */
179+ foreach ($ orderItems as $ item ) {
180+ if ($ item ->getParentItem () === null ) {
181+ $ orderItemProductIds [] = $ item ->getProductId ();
182+ $ orderItemsByProductId [$ item ->getProductId ()][$ item ->getId ()] = $ item ;
183+ }
184+ }
185+
186+ $ products = $ this ->getOrderProducts ($ storeId , $ orderItemProductIds );
187+
188+ // compare founded products and throw an error if some product not exists
189+ $ productsNotFound = array_diff ($ orderItemProductIds , array_keys ($ products ));
190+ if (!empty ($ productsNotFound )) {
191+ foreach ($ productsNotFound as $ productId ) {
192+ /** @var \Magento\Sales\Model\Order\Item $orderItemProductNotFound */
172193 $ this ->addError (
173- (string )__ ('Could not find a product with ID "%1" ' , $ orderItem -> getProductId () ),
194+ (string )__ ('Could not find a product with ID "%1" ' , $ productId ),
174195 self ::ERROR_PRODUCT_NOT_FOUND
175196 );
176- return ;
177197 }
178- $ addProductResult = null ;
179- try {
180- $ addProductResult = $ cart ->addProduct ($ product , $ info );
181- } catch (\Magento \Framework \Exception \LocalizedException $ e ) {
182- $ this ->addError ($ this ->getCartItemErrorMessage ($ orderItem , $ product , $ e ->getMessage ()));
183- } catch (\Throwable $ e ) {
184- $ this ->logger ->critical ($ e );
185- $ this ->addError ($ this ->getCartItemErrorMessage ($ orderItem , $ product ), self ::ERROR_UNDEFINED );
198+ }
199+
200+ foreach ($ orderItemsByProductId as $ productId => $ orderItems ) {
201+ if (!isset ($ products [$ productId ])) {
202+ continue ;
203+ }
204+ $ product = $ products [$ productId ];
205+ foreach ($ orderItems as $ orderItem ) {
206+ $ this ->addItemToCart ($ orderItem , $ cart , clone $ product );
186207 }
208+ }
209+ }
210+
211+ /**
212+ * Get order products by store id and order item product ids.
213+ *
214+ * @param string $storeId
215+ * @param int[] $orderItemProductIds
216+ * @return Product[]
217+ * @throws \Magento\Framework\Exception\LocalizedException
218+ */
219+ private function getOrderProducts (string $ storeId , array $ orderItemProductIds ): array
220+ {
221+ /** @var Collection $collection */
222+ $ collection = $ this ->productCollectionFactory ->create ();
223+ $ collection ->setStore ($ storeId )
224+ ->addIdFilter ($ orderItemProductIds )
225+ ->addStoreFilter ()
226+ ->addAttributeToSelect ('* ' )
227+ ->joinAttribute ('status ' , 'catalog_product/status ' , 'entity_id ' , null , 'inner ' )
228+ ->joinAttribute ('visibility ' , 'catalog_product/visibility ' , 'entity_id ' , null , 'inner ' );
229+
230+ return $ collection ->getItems ();
231+ }
232+
233+ /**
234+ * Adds order item product to cart.
235+ *
236+ * @param OrderItemInterface $orderItem
237+ * @param Quote $cart
238+ * @param ProductInterface $product
239+ * @return void
240+ */
241+ private function addItemToCart (OrderItemInterface $ orderItem , Quote $ cart , ProductInterface $ product ): void
242+ {
243+ $ info = $ orderItem ->getProductOptionByCode ('info_buyRequest ' );
244+ $ info = new \Magento \Framework \DataObject ($ info );
245+ $ info ->setQty ($ orderItem ->getQtyOrdered ());
246+
247+ $ addProductResult = null ;
248+ try {
249+ $ addProductResult = $ cart ->addProduct ($ product , $ info );
250+ } catch (\Magento \Framework \Exception \LocalizedException $ e ) {
251+ $ this ->addError ($ this ->getCartItemErrorMessage ($ orderItem , $ product , $ e ->getMessage ()));
252+ } catch (\Throwable $ e ) {
253+ $ this ->logger ->critical ($ e );
254+ $ this ->addError ($ this ->getCartItemErrorMessage ($ orderItem , $ product ), self ::ERROR_UNDEFINED );
255+ }
187256
188- // error happens in case the result is string
189- if (is_string ($ addProductResult )) {
190- $ errors = array_unique (explode ("\n" , $ addProductResult ));
191- foreach ($ errors as $ error ) {
192- $ this ->addError ($ this ->getCartItemErrorMessage ($ orderItem , $ product , $ error ));
193- }
257+ // error happens in case the result is string
258+ if (is_string ($ addProductResult )) {
259+ $ errors = array_unique (explode ("\n" , $ addProductResult ));
260+ foreach ($ errors as $ error ) {
261+ $ this ->addError ($ this ->getCartItemErrorMessage ($ orderItem , $ product , $ error ));
194262 }
195263 }
196264 }
0 commit comments