QuoteItem.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /*
  3. * FecShop file.
  4. *
  5. * @link http://www.fecshop.com/
  6. * @copyright Copyright (c) 2016 FecShop Software LLC
  7. * @license http://www.fecshop.com/license/
  8. */
  9. namespace fecshop\services\cart;
  10. use fecshop\services\Service;
  11. use Yii;
  12. /**
  13. * Cart services. 对购物车产品操作的具体实现部分。
  14. * @author Terry Zhao <2358269014@qq.com>
  15. * @since 1.0
  16. */
  17. class QuoteItem extends Service
  18. {
  19. public $itemDefaultActiveStatus = 1;
  20. public $activeStatus = 1;
  21. public $noActiveStatus = 2;
  22. protected $_my_cart_item; // 购物车cart item 对象
  23. protected $_cart_product_info;
  24. protected $_itemModelName = '\fecshop\models\mysqldb\cart\Item';
  25. /**
  26. * @var \fecshop\models\mysqldb\cart\Item
  27. */
  28. protected $_itemModel;
  29. public function init()
  30. {
  31. parent::init();
  32. list($this->_itemModelName, $this->_itemModel) = Yii::mapGet($this->_itemModelName);
  33. }
  34. /**
  35. * 将某个产品加入到购物车中。
  36. * 在添加到 cart_item 表后,更新购物车中产品的总数。
  37. * @param array $item
  38. * @return mixed
  39. * example:
  40. * $item = [
  41. * 'product_id' => 22222,
  42. * 'custom_option_sku' => red-xxl,
  43. * 'qty' => 22,
  44. * 'sku' => 'xxxx',
  45. * ];
  46. */
  47. public function addItem($item)
  48. {
  49. $cart_id = Yii::$service->cart->quote->getCartId();
  50. if (!$cart_id) {
  51. Yii::$service->cart->quote->createCart();
  52. $cart_id = Yii::$service->cart->quote->getCartId();
  53. }
  54. // 查看是否存在此产品,如果存在,则相加个数
  55. if (!isset($item['product_id']) || empty($item['product_id'])) {
  56. Yii::$service->helper->errors->add('add to cart error, product id is empty');
  57. return false;
  58. }
  59. $where = [
  60. 'cart_id' => $cart_id,
  61. 'product_id' => $item['product_id'],
  62. ];
  63. if (isset($item['custom_option_sku']) && !empty($item['custom_option_sku'])) {
  64. $where['custom_option_sku'] = $item['custom_option_sku'];
  65. }
  66. /** @var \fecshop\models\mysqldb\cart\Item $item_one */
  67. $item_one = $this->_itemModel->find()->where($where)->one();
  68. if ($item_one['cart_id']) {
  69. $item_one->active = $this->itemDefaultActiveStatus;
  70. $item_one->qty = $item['qty'] + $item_one['qty'];
  71. $item_one->save();
  72. // 重新计算购物车的数量
  73. Yii::$service->cart->quote->computeCartInfo();
  74. } else {
  75. $item_one = new $this->_itemModelName;
  76. $item_one->store = Yii::$service->store->currentStore;
  77. $item_one->cart_id = $cart_id;
  78. $item_one->created_at = time();
  79. $item_one->updated_at = time();
  80. $item_one->product_id = $item['product_id'];
  81. $item_one->qty = $item['qty'];
  82. $item_one->active = $this->itemDefaultActiveStatus;
  83. $item_one->custom_option_sku = ($item['custom_option_sku'] ? $item['custom_option_sku'] : '');
  84. $item_one->save();
  85. // 重新计算购物车的数量,并写入 sales_flat_cart 表存储
  86. Yii::$service->cart->quote->computeCartInfo();
  87. }
  88. $item['afterAddQty'] = $item_one->qty;
  89. $this->sendTraceAddToCartInfoByApi($item);
  90. return true;
  91. }
  92. /**
  93. * @param $item | Array, example:
  94. * $item = [
  95. * 'product_id' => 22222,
  96. * 'custom_option_sku' => red-xxl,
  97. * 'qty' => 22, // 添加购物车的产品个数
  98. * 'sku' => 'xxxx',
  99. * 'afterAddQty' => 33, // 添加后,该产品在sku中的个数,这个个数是为了计算购物车中产品的价格
  100. * ];
  101. * 将加入购物车的操作,加入trace
  102. */
  103. public function sendTraceAddToCartInfoByApi($item)
  104. {
  105. if (Yii::$service->page->trace->traceJsEnable) {
  106. $product_price_arr = Yii::$service->product->price->getCartPriceByProductId($item['product_id'], $item['afterAddQty'], $item['custom_option_sku'], 2);
  107. $base_product_price = isset($product_price_arr['base_price']) ? $product_price_arr['base_price'] : 0;
  108. // $price = $base_product_price * $item['qty'];
  109. $trace_cart_info = [
  110. [
  111. 'sku' => $item['sku'],
  112. 'price' => $base_product_price,
  113. 'qty' => $item['qty'],
  114. ]
  115. ];
  116. Yii::$service->page->trace->sendTraceAddToCartInfoByApi($trace_cart_info);
  117. }
  118. }
  119. /**
  120. * @param $item | Array, example:
  121. * $item = [
  122. * 'product_id' => 22222,
  123. * 'custom_option_sku' => red-xxl,
  124. * 'qty' => 22,
  125. * ];
  126. * @return boolean;
  127. * 将购物车中的某个产品更改个数,更改后的个数就是上面qty的值。
  128. * @deprecated 该函数已经被遗弃
  129. */
  130. /*
  131. public function changeItemQty($item)
  132. {
  133. $cart_id = Yii::$service->cart->quote->getCartId();
  134. // 查看是否存在此产品,如果存在,则更改
  135. $item_one = $this->_itemModel->find()->where([
  136. 'cart_id' => $cart_id,
  137. 'product_id' => $item['product_id'],
  138. 'custom_option_sku' => $item['custom_option_sku'],
  139. ])->one();
  140. if ($item_one['cart_id']) {
  141. $item_one->qty = $item['qty'];
  142. $item_one->save();
  143. // 重新计算购物车的数量
  144. Yii::$service->cart->quote->computeCartInfo();
  145. return true;
  146. } else {
  147. Yii::$service->helper->errors->add('This product is not available in the shopping cart');
  148. return false;
  149. }
  150. }
  151. */
  152. /**
  153. * 通过quoteItem表,计算得到所有产品的总数
  154. * 得到购物车中产品的总数,不要使用这个函数,这个函数的作用:
  155. * 在购物车中产品有变动后,使用这个函数得到产品总数,更新购物车中
  156. * 的产品总数。
  157. */
  158. public function getItemAllQty()
  159. {
  160. $cart_id = Yii::$service->cart->quote->getCartId();
  161. $item_qty = 0;
  162. if ($cart_id) {
  163. $data = $this->_itemModel->find()->asArray()->where([
  164. 'cart_id' => $cart_id,
  165. ])->all();
  166. if (is_array($data) && !empty($data)) {
  167. foreach ($data as $one) {
  168. $item_qty += $one['qty'];
  169. }
  170. }
  171. }
  172. return $item_qty;
  173. }
  174. /**
  175. * 通过quoteItem表,计算得到所有产品的总数
  176. * 得到购物车中产品的总数,不要使用这个函数,这个函数的作用:
  177. * 在购物车中产品有变动后,使用这个函数得到产品总数,更新购物车中
  178. * 的产品总数。
  179. */
  180. public function getActiveItemQty()
  181. {
  182. $cart_id = Yii::$service->cart->quote->getCartId();
  183. $item_qty = 0;
  184. if ($cart_id) {
  185. $data = $this->_itemModel->find()->asArray()->where([
  186. 'cart_id' => $cart_id,
  187. 'active' => $this->activeStatus,
  188. ])->all();
  189. if (is_array($data) && !empty($data)) {
  190. foreach ($data as $one) {
  191. $item_qty += $one['qty'];
  192. }
  193. }
  194. }
  195. return $item_qty;
  196. }
  197. /**
  198. * @param $activeProduct | boolean , 是否只要active的产品
  199. * @return array , foramt:
  200. * [
  201. * 'products' => $products, # 产品详细信息,详情参看代码中的$products。
  202. * 'product_total' => $product_total, # 产品的当前货币总额
  203. * 'base_product_total' => $base_product_total,# 产品的基础货币总额
  204. * 'product_weight'=> $product_weight, # 蟾皮的总重量、
  205. * ]
  206. * 得到当前购物车的产品信息,具体参看上面的example array。
  207. */
  208. public function getCartProductInfo($activeProduct = true)
  209. {
  210. $cart_id = Yii::$service->cart->quote->getCartId();
  211. $products = [];
  212. $product_total = 0;
  213. $product_weight = 0;
  214. $product_volume_weight = 0;
  215. $base_product_total = 0;
  216. $product_volume = 0;
  217. $product_qty_total = 0;
  218. if ($cart_id) {
  219. if (!isset($this->_cart_product_info[$cart_id])) {
  220. $data = $this->_itemModel->find()->where([
  221. 'cart_id' => $cart_id,
  222. ])->all();
  223. if (is_array($data) && !empty($data)) {
  224. foreach ($data as $one) {
  225. $active = $one['active'];
  226. if ($activeProduct && ($active != $this->activeStatus)) {
  227. continue;
  228. }
  229. $product_id = $one['product_id'];
  230. $product_one = Yii::$service->product->getByPrimaryKey($product_id);
  231. if ($product_one['_id']) {
  232. $qty = $one['qty'];
  233. $custom_option_sku = $one['custom_option_sku'];
  234. $product_price_arr = Yii::$service->product->price->getCartPriceByProductId($product_id, $qty, $custom_option_sku, 2);
  235. $curr_product_price = isset($product_price_arr['curr_price']) ? $product_price_arr['curr_price'] : 0;
  236. $base_product_price = isset($product_price_arr['base_price']) ? $product_price_arr['base_price'] : 0;
  237. $product_price = isset($curr_product_price['value']) ? $curr_product_price['value'] : 0;
  238. $product_row_price = $product_price * $qty;
  239. $base_product_row_price = $base_product_price * $qty;
  240. $volume = Yii::$service->shipping->getVolume($product_one['long'], $product_one['width'], $product_one['high']);
  241. $p_pv = $volume * $qty;
  242. $p_wt = $product_one['weight'] * $qty;
  243. $p_vwt = $product_one['volume_weight'] * $qty;
  244. if ($active == $this->activeStatus) {
  245. $product_total += $product_row_price;
  246. $base_product_total += $base_product_row_price;
  247. $product_weight += $p_wt;
  248. $product_volume_weight += $p_vwt;
  249. $product_volume += $p_pv;
  250. $product_qty_total += $qty;
  251. }
  252. $productSpuOptions = $this->getProductSpuOptions($product_one);
  253. $products[] = [
  254. 'item_id' => $one['item_id'],
  255. 'active' => $active,
  256. 'product_id' => $product_id,
  257. 'sku' => $product_one['sku'],
  258. 'name' => Yii::$service->store->getStoreAttrVal($product_one['name'], 'name'),
  259. 'qty' => $qty,
  260. 'custom_option_sku' => $custom_option_sku,
  261. 'product_price' => $product_price,
  262. 'product_row_price' => $product_row_price,
  263. 'base_product_price' => $base_product_price,
  264. 'base_product_row_price'=> $base_product_row_price,
  265. 'product_name' => $product_one['name'],
  266. 'product_weight' => $product_one['weight'],
  267. 'product_row_weight'=> $p_wt,
  268. 'product_volume_weight' => $product_one['volume_weight'],
  269. 'product_row_volume_weight' => $p_vwt,
  270. 'product_volume' => $volume,
  271. 'product_row_volume' => $p_pv,
  272. 'product_url' => $product_one['url_key'],
  273. 'product_image' => $product_one['image'],
  274. 'custom_option' => $product_one['custom_option'],
  275. 'spu_options' => $productSpuOptions,
  276. ];
  277. }
  278. }
  279. $this->_cart_product_info[$cart_id] = [
  280. 'products' => $products,
  281. 'product_qty_total' => $product_qty_total,
  282. 'product_total' => $product_total,
  283. 'base_product_total' => $base_product_total,
  284. 'product_weight' => $product_weight,
  285. 'product_volume_weight' => $product_volume_weight,
  286. 'product_volume' => $product_volume,
  287. ];
  288. }
  289. }
  290. return $this->_cart_product_info[$cart_id];
  291. }
  292. }
  293. /**
  294. * @param $productOb | Object,类型:\fecshop\models\mongodb\Product
  295. * 得到产品的spu对应的属性以及值。
  296. * 概念 - spu options:当多个产品是同一个spu,但是不同的sku的时候,他们的产品表里面的
  297. * spu attr 的值是不同的,譬如对应鞋子,size 和 color 就是spu attr,对于同一款鞋子,他们
  298. * 是同一个spu,对于尺码,颜色不同的鞋子,是不同的sku,他们的spu attr 就是 color 和 size。
  299. */
  300. protected function getProductSpuOptions($productOb)
  301. {
  302. $custom_option_info_arr = [];
  303. if (isset($productOb['attr_group']) && !empty($productOb['attr_group'])) {
  304. $productAttrGroup = $productOb['attr_group'];
  305. Yii::$service->product->addGroupAttrs($productAttrGroup);
  306. $productOb = Yii::$service->product->getByPrimaryKey((string) $productOb['_id']);
  307. $spuArr = Yii::$service->product->getSpuAttr($productAttrGroup);
  308. if (is_array($spuArr) && !empty($spuArr)) {
  309. foreach ($spuArr as $spu_attr) {
  310. if (isset($productOb[$spu_attr]) && !empty($productOb[$spu_attr])) {
  311. // 进行翻译。
  312. $spu_attr_label = Yii::$service->page->translate->__($spu_attr);
  313. $spu_attr_val = Yii::$service->page->translate->__($productOb[$spu_attr]);
  314. $custom_option_info_arr[$spu_attr_label] = $spu_attr_val;
  315. }
  316. }
  317. }
  318. }
  319. return $custom_option_info_arr;
  320. }
  321. /**
  322. * @param $item_id | Int , quoteItem表的id
  323. * @return bool
  324. * 将这个item_id对应的产品个数+1.
  325. */
  326. public function addOneItem($item_id)
  327. {
  328. $cart_id = Yii::$service->cart->quote->getCartId();
  329. if ($cart_id) {
  330. $one = $this->_itemModel->find()->where([
  331. 'cart_id' => $cart_id,
  332. 'item_id' => $item_id,
  333. ])->one();
  334. $product_id = $one['product_id'];
  335. if ($one['item_id'] && $product_id) {
  336. $product = Yii::$service->product->getByPrimaryKey($product_id);
  337. $changeQty = Yii::$service->cart->getCartQty($product['package_number'], 1);
  338. $one['qty'] = $one['qty'] + $changeQty;
  339. $one->save();
  340. // 重新计算购物车的数量
  341. Yii::$service->cart->quote->computeCartInfo();
  342. $item = [
  343. 'product_id' => $product_id,
  344. 'custom_option_sku' => $one['custom_option_sku'],
  345. 'qty' => $changeQty,
  346. 'sku' => $product['sku'],
  347. 'afterAddQty' => $one['qty'],
  348. ];
  349. // 购物车数据加1
  350. $this->sendTraceAddToCartInfoByApi($item);
  351. return true;
  352. }
  353. }
  354. return false;
  355. }
  356. /**
  357. * @param $item_id | Int , quoteItem表的id
  358. * @return bool
  359. * 将这个item_id对应的产品个数-1.
  360. */
  361. public function lessOneItem($item_id)
  362. {
  363. $cart_id = Yii::$service->cart->quote->getCartId();
  364. if ($cart_id) {
  365. $one = $this->_itemModel->find()->where([
  366. 'cart_id' => $cart_id,
  367. 'item_id' => $item_id,
  368. ])->one();
  369. $product_id = $one['product_id'];
  370. $product = Yii::$service->product->getByPrimaryKey($one['product_id']);
  371. $changeQty = Yii::$service->cart->getCartQty($product['package_number'], 1);
  372. $lessedQty = $one['qty'] - $changeQty;
  373. $min_sales_qty = 1;
  374. if ($product['min_sales_qty'] && $product['min_sales_qty'] >= 2) {
  375. $min_sales_qty = $product['min_sales_qty'];
  376. }
  377. if ($lessedQty < $min_sales_qty) {
  378. Yii::$service->helper->errors->add('product less buy qty is {min_sales_qty}', ['min_sales_qty' => $product['min_sales_qty']]);
  379. return false;
  380. }
  381. if ($one['item_id']) {
  382. if ($one['qty'] > 1) {
  383. $one['qty'] = $lessedQty;
  384. $one->save();
  385. // 重新计算购物车的数量
  386. Yii::$service->cart->quote->computeCartInfo();
  387. return true;
  388. }
  389. }
  390. }
  391. return false;
  392. }
  393. /**
  394. * @param $item_id | Int , quoteItem表的id
  395. * @return bool
  396. * 将这个item_id对应的产品删除
  397. */
  398. public function removeItem($item_id)
  399. {
  400. $cart_id = Yii::$service->cart->quote->getCartId();
  401. if ($cart_id) {
  402. $one = $this->_itemModel->find()->where([
  403. 'cart_id' => $cart_id,
  404. 'item_id' => $item_id,
  405. ])->one();
  406. if ($one['item_id']) {
  407. $one->delete();
  408. // 重新计算购物车的数量
  409. Yii::$service->cart->quote->computeCartInfo();
  410. return true;
  411. }
  412. }
  413. return false;
  414. }
  415. /**
  416. * @param $item_id | Int , quoteItem表的id
  417. * @return bool
  418. * 将这个item_id对应的产品个数+1.
  419. */
  420. public function selectOneItem($item_id, $checked)
  421. {
  422. $cart_id = Yii::$service->cart->quote->getCartId();
  423. if ($cart_id) {
  424. $one = $this->_itemModel->find()->where([
  425. 'cart_id' => $cart_id,
  426. 'item_id' => $item_id,
  427. ])->one();
  428. $product_id = $one['product_id'];
  429. if ($one['item_id'] && $product_id) {
  430. //$product = Yii::$service->product->getByPrimaryKey($product_id);
  431. //$changeQty = Yii::$service->cart->getCartQty($product['package_number'], 1);
  432. //$one['qty'] = $one['qty'] + $changeQty;
  433. if ($checked == true) {
  434. $one->active = $this->activeStatus;
  435. } else {
  436. $one->active = $this->noActiveStatus;
  437. }
  438. $one->save();
  439. // 重新计算购物车的数量
  440. Yii::$service->cart->quote->computeCartInfo();
  441. return true;
  442. }
  443. }
  444. return false;
  445. }
  446. /**
  447. * @param $item_id | Int , quoteItem表的id
  448. * @return bool
  449. * 将这个item_id对应的产品个数+1.
  450. */
  451. public function selectAllItem($checked)
  452. {
  453. $cart_id = Yii::$service->cart->quote->getCartId();
  454. if ($cart_id) {
  455. $active = $this->noActiveStatus;
  456. if ($checked == true) {
  457. $active = $this->activeStatus;
  458. }
  459. $updateCount = $this->_itemModel->updateAll(
  460. ['active' => $active],
  461. ['cart_id' => $cart_id]
  462. );
  463. if ($updateCount > 0) {
  464. Yii::$service->cart->quote->computeCartInfo();
  465. }
  466. return true;
  467. }
  468. return false;
  469. }
  470. /**
  471. * @param $cart_id | int 购物车id
  472. * 删除购物车中的所有的active产品。对于noActive产品保留
  473. * 注意:清空购物车并不是清空所有信息,仅仅是清空用户购物车中的产品。
  474. * 另外,购物车的数目更改后,需要更新cart中产品个数的信息。
  475. */
  476. public function removeNoActiveItemsByCartId($cart_id = '')
  477. {
  478. if (!$cart_id) {
  479. $cart_id = Yii::$service->cart->quote->getCartId();
  480. }
  481. if ($cart_id) {
  482. $columns = $this->_itemModel->deleteAll([
  483. 'cart_id' => $cart_id,
  484. 'active' => $this->activeStatus,
  485. ]);
  486. if ($columns > 0) {
  487. // 重新计算购物车的数量
  488. Yii::$service->cart->quote->computeCartInfo();
  489. return true;
  490. }
  491. }
  492. }
  493. /** 废弃,改为 removeNoActiveItemsByCartId(),因为购物车改为勾选下单方式。
  494. * @param $cart_id | int 购物车id
  495. * 删除购物车中的所有产品。
  496. * 注意:清空购物车并不是清空所有信息,仅仅是清空用户购物车中的产品。
  497. * 另外,购物车的数目更改后,需要更新cart中产品个数的信息。
  498. */
  499. public function removeItemByCartId($cart_id = '')
  500. {
  501. if (!$cart_id) {
  502. $cart_id = Yii::$service->cart->quote->getCartId();
  503. }
  504. if ($cart_id) {
  505. $items = $this->_itemModel->deleteAll([
  506. 'cart_id' => $cart_id,
  507. //'item_id' => $item_id,
  508. ]);
  509. // 重新计算购物车的数量
  510. Yii::$service->cart->quote->computeCartInfo(0);
  511. }
  512. return true;
  513. }
  514. /**
  515. * @param $new_cart_id | int 更新后的cart_id
  516. * @param $cart_id | int 更新前的cart_id
  517. * 删除购物车中的所有产品。
  518. * 这里仅仅更改cart表的cart_id, 而不会做其他任何事情。
  519. */
  520. public function updateCartId($new_cart_id, $cart_id)
  521. {
  522. if ($cart_id && $new_cart_id) {
  523. $this->_itemModel->updateAll(
  524. ['cart_id' => $new_cart_id], // $attributes
  525. ['cart_id' => $cart_id] // $condition
  526. );
  527. // 重新计算购物车的数量
  528. //Yii::$service->cart->quote->computeCartInfo();
  529. return true;
  530. }
  531. return false;
  532. }
  533. }