Product.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <?php
  2. namespace app\common\model\product;
  3. use app\common\library\helper;
  4. use app\common\model\BaseModel;
  5. /**
  6. * 商品模型
  7. */
  8. class Product extends BaseModel
  9. {
  10. protected $name = 'product';
  11. protected $pk = 'product_id';
  12. protected $append = ['product_sales'];
  13. /**
  14. * 计算显示销量 (初始销量 + 实际销量)
  15. */
  16. public function getProductSalesAttr($value, $data)
  17. {
  18. return $data['sales_initial'] + $data['sales_actual'];
  19. }
  20. /**
  21. * 获取器:单独设置折扣的配置
  22. */
  23. public function getAloneGradeEquityAttr($json)
  24. {
  25. return json_decode($json, true);
  26. }
  27. /**
  28. * 修改器:单独设置折扣的配置
  29. */
  30. public function setAloneGradeEquityAttr($data)
  31. {
  32. return json_encode($data);
  33. }
  34. /**
  35. * 关联商品分类表
  36. */
  37. public function category()
  38. {
  39. return $this->belongsTo('app\\common\\model\\product\\Category');
  40. }
  41. /**
  42. * 关联商品规格表
  43. */
  44. public function sku()
  45. {
  46. return $this->hasMany('ProductSku')->order(['product_sku_id' => 'asc']);
  47. }
  48. /**
  49. * 关联商品定时上下架表
  50. */
  51. public function timer()
  52. {
  53. return $this->hasOne('app\\common\\model\\product\\ProductTimer', 'product_id', 'product_id');
  54. }
  55. /**
  56. * 关联商品规格关系表
  57. */
  58. public function specRel()
  59. {
  60. return $this->belongsToMany('SpecValue', 'ProductSpecRel')->order(['id' => 'asc']);
  61. }
  62. /**
  63. * 关联商品图片表
  64. */
  65. public function image()
  66. {
  67. return $this->hasMany('app\\common\\model\\product\\ProductImage')->where('image_type', '=', 0)->order(['id' => 'asc']);
  68. }
  69. /**
  70. * 关联商品详情图片表
  71. */
  72. public function contentImage()
  73. {
  74. return $this->hasMany('app\\common\\model\\product\\ProductImage')->where('image_type', '=', 1)->order(['id' => 'asc']);
  75. }
  76. /**
  77. * 关联运费模板表
  78. */
  79. public function delivery()
  80. {
  81. return $this->BelongsTo('app\\common\\model\\settings\\Delivery');
  82. }
  83. /**
  84. * 关联订单评价表
  85. */
  86. public function commentData()
  87. {
  88. return $this->hasMany('app\\common\\model\\product\\Comment', 'product_id', 'product_id');
  89. }
  90. /**
  91. * 关联视频
  92. */
  93. public function video()
  94. {
  95. return $this->hasOne('app\\common\\model\\file\\UploadFile', 'file_id', 'video_id');
  96. }
  97. /**
  98. * 关联视频封面
  99. */
  100. public function poster()
  101. {
  102. return $this->hasOne('app\\common\\model\\file\\UploadFile', 'file_id', 'poster_id');
  103. }
  104. /**
  105. * 计费方式
  106. */
  107. public function getProductStatusAttr($value)
  108. {
  109. $status = [10 => '上架', 20 => '下架', 30 => '草稿'];
  110. return ['text' => $status[$value], 'value' => $value];
  111. }
  112. /**
  113. * 获取商品列表
  114. */
  115. public function getList($param)
  116. {
  117. $param=array_filter($param);
  118. // 商品列表获取条件
  119. $params = array_merge([
  120. 'type' => 'sell', // 商品状态
  121. 'category_id' => 0, // 分类id
  122. 'sortType' => 'all', // 排序类型
  123. 'sortPrice' => false, // 价格排序 高低
  124. 'list_rows' => 15, // 每页数量
  125. ], $param);
  126. // 筛选条件
  127. $filter = [];
  128. $model = $this;
  129. if ($params['category_id'] > 0) {
  130. $arr = Category::getSubCategoryId($params['category_id']);
  131. $model = $model->where('category_id', 'IN', $arr);
  132. }
  133. if (!empty($params['product_name'])) {
  134. $model = $model->where('product_name', 'like', '%' . trim($params['product_name']) . '%');
  135. }
  136. if (!empty($params['product_no'])) {
  137. $model = $model->where('product_no', 'like', '%' . trim($params['product_no']) . '%');
  138. }
  139. if (!empty($params['search'])) {
  140. $model = $model->where('product_name', 'like', '%' . trim($params['search']) . '%');
  141. }
  142. if (isset($params['spec_type'])&&$params['spec_type']>0) {
  143. $model = $model->where('spec_type', '=', $params['spec_type']);
  144. }
  145. // 排序规则
  146. $sort = [];
  147. if ($params['sortType'] === 'all') {
  148. $sort = ['product_sort', 'product_id' => 'desc'];
  149. } else if ($params['sortType'] === 'sales') {
  150. $sort = ['product_sales' => 'desc'];
  151. } else if ($params['sortType'] === 'price') {
  152. $sort = $params['sortPrice'] ? ['product_max_price' => 'desc'] : ['product_min_price'];
  153. }
  154. if (isset($params['type'])) {
  155. //出售中
  156. if ($params['type'] == 'sell') {
  157. $model = $model->where('product_status', '=', 10);
  158. }
  159. //库存紧张
  160. if ($params['type'] == 'stock') {
  161. $model = $model->whereBetween('product_stock', [1, 10]);
  162. }
  163. //已售罄
  164. if ($params['type'] == 'over') {
  165. $model = $model->where('product_stock', '=', 0);
  166. }
  167. //仓库中
  168. if ($params['type'] == 'lower') {
  169. $model = $model->where('product_status', '=', 20);
  170. }
  171. //回收站
  172. if ($params['type'] == 'recovery') {
  173. $model = $model->where('product_status', '=', 30);
  174. }
  175. }
  176. // 商品表名称
  177. $tableName = $this->getTable();
  178. // 多规格商品 最高价与最低价
  179. $ProductSku = new ProductSku;
  180. $minPriceSql = $ProductSku->field(['MIN(product_price)'])
  181. ->where('product_id', 'EXP', "= `$tableName`.`product_id`")->buildSql();
  182. $maxPriceSql = $ProductSku->field(['MAX(product_price)'])
  183. ->where('product_id', 'EXP', "= `$tableName`.`product_id`")->buildSql();
  184. // 执行查询
  185. $list = $model
  186. ->field(['*', '(sales_initial + sales_actual) as product_sales',
  187. "$minPriceSql AS product_min_price",
  188. "$maxPriceSql AS product_max_price"
  189. ])
  190. ->with(['category', 'image.file', 'sku', 'timer'])
  191. ->where('is_delete', '=', 0)
  192. ->where($filter)
  193. ->order($sort)
  194. ->paginate($params);
  195. // 整理列表数据并返回
  196. return $this->setProductListData($list, true);
  197. }
  198. /**
  199. * 获取商品列表
  200. */
  201. public function getLists($param)
  202. {
  203. // 商品列表获取条件
  204. $params = array_merge([
  205. 'product_status' => 10, // 商品状态
  206. 'category_id' => 0, // 分类id
  207. ], $param);
  208. // 筛选条件
  209. $model = $this;
  210. if ($params['category_id'] > 0) {
  211. $arr = Category::getSubCategoryId($params['category_id']);
  212. $model = $model->where('category_id', 'IN', $arr);
  213. }
  214. if (!empty($params['product_name'])) {
  215. $model = $model->where('product_name', 'like', '%' . trim($params['product_name']) . '%');
  216. }
  217. if (!empty($params['search'])) {
  218. $model = $model->where('product_name', 'like', '%' . trim($params['search']) . '%');
  219. }
  220. $list = $model
  221. ->with(['category', 'image.file'])
  222. ->where('is_delete', '=', 0)
  223. ->where('product_status', '=', $params['product_status'])
  224. ->paginate($params);
  225. // 整理列表数据并返回
  226. return $this->setProductListData($list, true);
  227. }
  228. /**
  229. * 设置商品展示的数据
  230. */
  231. protected function setProductListData($data, $isMultiple = true, callable $callback = null)
  232. {
  233. if (!$isMultiple) $dataSource = [&$data]; else $dataSource = &$data;
  234. // 整理商品列表数据
  235. foreach ($dataSource as &$product) {
  236. // 商品主图
  237. $product['product_image'] = $product['image'][0]['file_path'];
  238. // 商品默认规格
  239. $product['product_sku'] = self::getShowSku($product);
  240. // 等级id转换成数组
  241. if(!is_array ($product['grade_ids'])){
  242. if($product['grade_ids'] != ''){
  243. $product['grade_ids'] = explode(',', $product['grade_ids']);
  244. } else {
  245. $product['grade_ids'] = [];
  246. }
  247. }
  248. // 回调函数
  249. is_callable($callback) && call_user_func($callback, $product);
  250. }
  251. return $data;
  252. }
  253. /**
  254. * 根据商品id集获取商品列表
  255. */
  256. public function getListByIds($productIds, $status = null)
  257. {
  258. $model = $this;
  259. $filter = [];
  260. // 筛选条件
  261. $status > 0 && $filter['product_status'] = $status;
  262. if (!empty($productIds)) {
  263. $model = $model->orderRaw('field(product_id, ' . implode(',', $productIds) . ')');
  264. }
  265. // 获取商品列表数据
  266. $data = $model->with(['category', 'image.file', 'sku'])
  267. ->where($filter)
  268. ->where('product_id', 'in', $productIds)
  269. ->select();
  270. // 整理列表数据并返回
  271. return $this->setProductListData($data, true);
  272. }
  273. /**
  274. * 根据商品id集获取商品列表
  275. */
  276. public function getListByCatIds($categoryIds, $status = null)
  277. {
  278. $model = $this;
  279. $filter = [];
  280. // 筛选条件
  281. $status > 0 && $filter['product_status'] = $status;
  282. // 获取商品列表数据
  283. $data = $model->with(['category', 'image.file', 'sku'])
  284. ->where($filter)
  285. ->where('category_id', 'in', $categoryIds)
  286. ->paginate();
  287. // 整理列表数据并返回
  288. return $this->setProductListData($data, true);
  289. }
  290. /**
  291. * 商品多规格信息
  292. */
  293. public function getManySpecData($specRel, $skuData)
  294. {
  295. // spec_attr
  296. $specAttrData = [];
  297. foreach ($specRel as $item) {
  298. if (!isset($specAttrData[$item['spec_id']])) {
  299. $specAttrData[$item['spec_id']] = [
  300. 'group_id' => $item['spec']['spec_id'],
  301. 'group_name' => $item['spec']['spec_name'],
  302. 'spec_items' => [],
  303. ];
  304. }
  305. $specAttrData[$item['spec_id']]['spec_items'][] = [
  306. 'item_id' => $item['spec_value_id'],
  307. 'spec_value' => $item['spec_value'],
  308. ];
  309. }
  310. // spec_list
  311. $specListData = [];
  312. foreach ($skuData as $item) {
  313. $image = (isset($item['image']) && !empty($item['image'])) ? $item['image'] : ['file_id' => 0, 'file_path' => ''];
  314. $specListData[] = [
  315. 'product_sku_id' => $item['product_sku_id'],
  316. 'spec_sku_id' => $item['spec_sku_id'],
  317. 'rows' => [],
  318. 'spec_form' => [
  319. 'image_id' => $image['file_id'],
  320. 'image_path' => $image['file_path'],
  321. 'product_no' => $item['product_no'],
  322. 'product_price' => $item['product_price'],
  323. 'pv' => $item['pv'],
  324. 'product_weight' => $item['product_weight'],
  325. 'line_price' => $item['line_price'],
  326. 'stock_num' => $item['stock_num'],
  327. ],
  328. ];
  329. }
  330. return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData];
  331. }
  332. /**
  333. * 多规格表格数据
  334. */
  335. public function getManySpecTable($product)
  336. {
  337. $specData = $this->getManySpecData($product['spec_rel'], $product['sku']);
  338. $totalRow = count($specData['spec_list']);
  339. foreach ($specData['spec_list'] as $i => &$sku) {
  340. $rowData = [];
  341. $rowCount = 1;
  342. foreach ($specData['spec_attr'] as $attr) {
  343. $skuValues = $attr['spec_items'];
  344. $rowCount *= count($skuValues);
  345. $anInterBankNum = ($totalRow / $rowCount);
  346. $point = (($i / $anInterBankNum) % count($skuValues));
  347. if (0 === ($i % $anInterBankNum)) {
  348. $rowData[] = [
  349. 'rowspan' => $anInterBankNum,
  350. 'item_id' => $skuValues[$point]['item_id'],
  351. 'spec_value' => $skuValues[$point]['spec_value']
  352. ];
  353. }
  354. }
  355. $sku['rows'] = $rowData;
  356. }
  357. return $specData;
  358. }
  359. /**
  360. * 获取商品详情
  361. */
  362. public static function detail($product_id)
  363. {
  364. $model = (new static())->with([
  365. // 'category',
  366. // 'image.file',
  367. // 'sku.image',
  368. 'spec_rel',
  369. // 'video',
  370. // 'poster',
  371. // 'contentImage.file',
  372. ])->where('product_id', '=', $product_id)
  373. ->find();
  374. if (empty($model)) {
  375. return $model;
  376. }
  377. // 整理商品数据并返回
  378. return $model->setProductListData($model, false);
  379. }
  380. /**
  381. * 指定的商品规格信息
  382. */
  383. public static function getProductSku($product, $specSkuId)
  384. {
  385. // 获取指定的sku
  386. $productSku = [];
  387. foreach ($product['sku'] as $item) {
  388. if ($item['spec_sku_id'] == $specSkuId) {
  389. $productSku = $item;
  390. break;
  391. }
  392. }
  393. if (empty($productSku)) {
  394. return false;
  395. }
  396. // 多规格文字内容
  397. $productSku['product_attr'] = '';
  398. if ($product['spec_type'] == 20) {
  399. $specRelData = helper::arrayColumn2Key($product['spec_rel'], 'spec_value_id');
  400. $attrs = explode('_', $productSku['spec_sku_id']);
  401. foreach ($attrs as $specValueId) {
  402. $productSku['product_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':'
  403. . $specRelData[$specValueId]['spec_value'] . '; ';
  404. }
  405. }
  406. return $productSku;
  407. }
  408. /**
  409. * 根据商品名称得到相关列表
  410. */
  411. public function getWhereData($product_name)
  412. {
  413. return $this->where('product_name', 'like', '%' . trim($product_name) . '%')->select();
  414. }
  415. /**
  416. * 显示的sku,目前取最低价
  417. */
  418. public static function getShowSku($product){
  419. //如果是单规格
  420. if($product['spec_type'] == 10){
  421. return $product['sku'][0];
  422. }else{
  423. //多规格返回最低价
  424. foreach ($product['sku'] as $sku){
  425. if($product['product_price'] == $sku['product_price']){
  426. return $sku;
  427. }
  428. }
  429. }
  430. // 兼容历史数据,如果找不到返回第一个
  431. return $product['sku'][0];
  432. }
  433. }