QuoteItem.php 23 KB

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