1<?php 2/** 3 * Copyright since 2007 PrestaShop SA and Contributors 4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA 5 * 6 * NOTICE OF LICENSE 7 * 8 * This source file is subject to the Open Software License (OSL 3.0) 9 * that is bundled with this package in the file LICENSE.md. 10 * It is also available through the world-wide-web at this URL: 11 * https://opensource.org/licenses/OSL-3.0 12 * If you did not receive a copy of the license and are unable to 13 * obtain it through the world-wide-web, please send an email 14 * to license@prestashop.com so we can send you a copy immediately. 15 * 16 * DISCLAIMER 17 * 18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer 19 * versions in the future. If you wish to customize PrestaShop for your 20 * needs please refer to https://devdocs.prestashop.com/ for more information. 21 * 22 * @author PrestaShop SA and Contributors <contact@prestashop.com> 23 * @copyright Since 2007 PrestaShop SA and Contributors 24 * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) 25 */ 26 27declare(strict_types=1); 28 29namespace PrestaShop\PrestaShop\Adapter\Cart\Comparator; 30 31use Cart; 32 33/** 34 * This class saves a cart's products when it's created, you can then ask for the difference 35 * that happened on this cart. 36 */ 37class CartProductsComparator 38{ 39 /** 40 * @var Cart 41 */ 42 private $cart; 43 44 /** 45 * @var array 46 */ 47 private $savedProducts; 48 49 /** 50 * @var CartProductUpdate[] 51 */ 52 private $knownUpdates = []; 53 54 /** 55 * @param Cart $cart 56 */ 57 public function __construct(Cart $cart) 58 { 59 $this->cart = $cart; 60 $this->savedProducts = $cart->getProducts(true); 61 } 62 63 /** 64 * @param array $knownUpdates 65 * 66 * @return CartProductsComparator 67 */ 68 public function setKnownUpdates(array $knownUpdates): self 69 { 70 $this->knownUpdates = $knownUpdates; 71 72 return $this; 73 } 74 75 /** 76 * Returns a list of products that were strictly updated (not created) compared to the state of the cart 77 * when this object was created, it removes the already known modified products provided as argument. 78 * 79 * @return CartProductUpdate[] 80 */ 81 public function getUpdatedProducts(): array 82 { 83 $newProducts = $this->cart->getProducts(true); 84 $allUpdateProducts = $this->getAllUpdatedProducts($newProducts); 85 86 return $this->filterKnownUpdates($allUpdateProducts); 87 } 88 89 /** 90 * Returns a list of products that were strictly created (not updated) compared to the state of the cart 91 * when this object was created, it removes the already known modified products provided as argument. 92 * 93 * @return CartProductUpdate[] 94 */ 95 public function getAdditionalProducts(): array 96 { 97 $newProducts = $this->cart->getProducts(true); 98 $allAdditionalProducts = $this->getAllAdditionalProducts($newProducts); 99 100 return $this->filterKnownUpdates($allAdditionalProducts); 101 } 102 103 /** 104 * Returns a list of products that were modified (created and/or updated) compared to the state of the cart 105 * when this object was created, it removes the already known modified products provided as argument. 106 * 107 * @return CartProductUpdate[] 108 */ 109 public function getModifiedProducts(): array 110 { 111 $newProducts = $this->cart->getProducts(true); 112 $modifiedProducts = array_merge($this->getAllUpdatedProducts($newProducts), $this->getAllAdditionalProducts($newProducts)); 113 114 return $this->filterKnownUpdates($modifiedProducts); 115 } 116 117 /** 118 * Returns the list of updates for products that were not in the cart previously 119 * 120 * @param array[] $newProducts 121 * 122 * @return array 123 */ 124 private function getAllAdditionalProducts(array $newProducts): array 125 { 126 $additionalProducts = []; 127 foreach ($newProducts as $newProduct) { 128 // Then try and find the product in new products 129 $oldProduct = $this->getMatchingProduct($this->savedProducts, $newProduct); 130 if (null === $oldProduct) { 131 $additionalProducts[] = new CartProductUpdate( 132 (int) $newProduct['id_product'], 133 (int) $newProduct['id_product_attribute'], 134 (int) $newProduct['cart_quantity'], 135 true, 136 (int) $newProduct['id_customization'] 137 ); 138 } 139 } 140 141 return $additionalProducts; 142 } 143 144 /** 145 * Returns a list of all products that were updated compared to the creation of this object. 146 * 147 * @param array[] $newProducts 148 * 149 * @return CartProductUpdate[] 150 */ 151 private function getAllUpdatedProducts(array $newProducts): array 152 { 153 $updatedProducts = []; 154 foreach ($this->savedProducts as $oldProduct) { 155 // Then try and find the product in new products 156 $newProduct = $this->getMatchingProduct($newProducts, $oldProduct); 157 if (null === $newProduct) { 158 $deltaQuantity = -(int) $oldProduct['cart_quantity']; 159 } else { 160 $deltaQuantity = (int) $newProduct['cart_quantity'] - (int) $oldProduct['cart_quantity']; 161 } 162 163 if ($deltaQuantity) { 164 $updatedProducts[] = new CartProductUpdate( 165 (int) $oldProduct['id_product'], 166 (int) $oldProduct['id_product_attribute'], 167 $deltaQuantity, 168 false, 169 (int) $oldProduct['id_customization'] 170 ); 171 } 172 } 173 174 return $updatedProducts; 175 } 176 177 /** 178 * @param CartProductUpdate[] $updates 179 * 180 * @return CartProductUpdate[] 181 */ 182 private function filterKnownUpdates(array $updates): array 183 { 184 $filteredUpdates = []; 185 foreach ($updates as $updateProduct) { 186 foreach ($this->knownUpdates as $knownUpdate) { 187 if ($knownUpdate->productMatches($updateProduct)) { 188 $updateProduct->setDeltaQuantity( 189 $updateProduct->getDeltaQuantity() - $knownUpdate->getDeltaQuantity() 190 ); 191 192 break; 193 } 194 } 195 if (0 !== $updateProduct->getDeltaQuantity()) { 196 $filteredUpdates[] = $updateProduct; 197 } 198 } 199 200 return $filteredUpdates; 201 } 202 203 /** 204 * @param array $products 205 * @param array $searchedProduct 206 * 207 * @return array|null 208 */ 209 private function getMatchingProduct(array $products, array $searchedProduct): ?array 210 { 211 return array_reduce($products, function ($carry, $item) use ($searchedProduct) { 212 if (null !== $carry) { 213 return $carry; 214 } 215 216 $productMatch = $item['id_product'] == $searchedProduct['id_product']; 217 $combinationMatch = $item['id_product_attribute'] == $searchedProduct['id_product_attribute']; 218 $customizationMatch = $item['id_customization'] == $searchedProduct['id_customization']; 219 220 return $productMatch && $combinationMatch && $customizationMatch ? $item : null; 221 }); 222 } 223} 224