Channel.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <?php
  2. namespace app\admin\model\cms;
  3. use addons\cms\library\Service;
  4. use think\Exception;
  5. use think\Model;
  6. class Channel extends Model
  7. {
  8. // 表名
  9. protected $name = 'cms_channel';
  10. // 自动写入时间戳字段
  11. protected $autoWriteTimestamp = 'int';
  12. // 定义时间戳字段名
  13. protected $createTime = 'createtime';
  14. protected $updateTime = 'updatetime';
  15. // 追加属性
  16. protected $append = [
  17. 'type_text',
  18. 'status_text',
  19. 'url',
  20. 'fullurl',
  21. 'outlink',
  22. ];
  23. protected static $config = [];
  24. public function getOriginData()
  25. {
  26. return $this->origin;
  27. }
  28. public function getUrlAttr($value, $data)
  29. {
  30. return $this->buildUrl($value, $data);
  31. }
  32. public function getFullurlAttr($value, $data)
  33. {
  34. return $this->buildUrl($value, $data, true);
  35. }
  36. private function buildUrl($value, $data, $domain = false)
  37. {
  38. $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id'];
  39. $cateid = $data['id'] ?? 0;
  40. $catename = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : 'all';
  41. $time = $data['createtime'] ?? time();
  42. $vars = [
  43. ':id' => $data['id'],
  44. ':diyname' => $diyname,
  45. ':channel' => $cateid,
  46. ':catename' => $catename,
  47. ':cateid' => $cateid,
  48. ':year' => date("Y", $time),
  49. ':month' => date("m", $time),
  50. ':day' => date("d", $time)
  51. ];
  52. if (isset($data['type']) && isset($data['outlink']) && $data['type'] == 'link') {
  53. return $this->getAttr('outlink');
  54. }
  55. return addon_url('cms/channel/index', $vars, static::$config['urlsuffix'], $domain);
  56. }
  57. public function getOutlinkAttr($value, $data)
  58. {
  59. $indexUrl = $view_replace_str = config('view_replace_str.__PUBLIC__');
  60. $indexUrl = rtrim($indexUrl, '/');
  61. return str_replace('__INDEX__', $indexUrl, $value);
  62. }
  63. protected static function init()
  64. {
  65. $config = static::$config = get_addon_config('cms');
  66. self::beforeInsert(function ($row) {
  67. if ($row->getData('type') == 'link') {
  68. $row->model_id = 0;
  69. }
  70. $diyname = $row['diyname'] ?? '';
  71. if ($diyname) {
  72. $exists = Channel::getByDiyname($diyname);
  73. if ($exists) {
  74. throw new Exception("自定义URL名称已经存在");
  75. }
  76. }
  77. });
  78. self::beforeUpdate(function ($row) {
  79. if (isset($row['parent_id']) && $row['parent_id']) {
  80. $childrenIds = self::getChildrenIds($row['id'], true);
  81. if (in_array($row['parent_id'], $childrenIds)) {
  82. throw new Exception("上级栏目不能是其自身或子栏目");
  83. }
  84. }
  85. if (isset($row['diyname']) && $row['diyname']) {
  86. $exists = Channel::where('diyname', $row['diyname'])->where('id', '<>', $row['id'])->find();
  87. if ($exists) {
  88. throw new Exception("自定义URL名称已经存在");
  89. }
  90. }
  91. });
  92. self::beforeWrite(function ($row) {
  93. //在更新之前对数组进行处理
  94. foreach ($row->getData() as $k => $value) {
  95. if (is_array($value) && is_array(reset($value))) {
  96. $value = json_encode(self::getArrayData($value), JSON_UNESCAPED_UNICODE);
  97. } else {
  98. $value = is_array($value) ? implode(',', $value) : $value;
  99. }
  100. $row->setAttr($k, $value);
  101. }
  102. });
  103. self::afterInsert(function ($row) {
  104. //创建时自动添加权重值
  105. $pk = $row->getPk();
  106. $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]);
  107. });
  108. self::afterDelete(function ($row) {
  109. //删除时,删除子节点,同时将所有相关文档移入回收站
  110. $childIds = self::getChildrenIds($row['id']);
  111. if ($childIds) {
  112. Channel::destroy(function ($query) use ($childIds) {
  113. $query->where('id', 'in', $childIds);
  114. });
  115. }
  116. $childIds[] = $row['id'];
  117. db('cms_archives')->where('channel_id', 'in', $childIds)->update(['deletetime' => time()]);
  118. });
  119. self::afterWrite(function ($row) use ($config) {
  120. $changed = $row->getChangedData();
  121. //隐藏时判断是否有子节点,有则隐藏
  122. if (isset($changed['status']) && $changed['status'] == 'hidden') {
  123. $childIds = self::getChildrenIds($row['id']);
  124. db('cms_channel')->where('id', 'in', $childIds)->update(['status' => 'hidden']);
  125. }
  126. //隐藏栏目显示时判断是否有子节点
  127. if (isset($changed['isnav']) && !$changed['isnav']) {
  128. $childIds = self::getChildrenIds($row['id']);
  129. db('cms_channel')->where('id', 'in', $childIds)->update(['isnav' => 0]);
  130. }
  131. //推送到熊掌号+百度站长
  132. if (isset($changed['status']) && $changed['status'] == 'normal') {
  133. if ($config['baidupush']) {
  134. $urls = [$row->fullurl];
  135. \think\Hook::listen("baidupush", $urls);
  136. }
  137. }
  138. //刷新栏目统计数据
  139. if (isset($changed['listtype']) || isset($changed['parent_id'])) {
  140. $origined = $row->getOriginData();
  141. $refreshIds = [$origined['parent_id'] ?? 0, $row['parent_id'] ?? 0, $row['id'] ?? 0];
  142. self::refreshItems($refreshIds);
  143. }
  144. //同步配置到子栏目
  145. if (isset($row['syncconfig'])) {
  146. $childIds = self::getChildrenIds($row['id']);
  147. $data = [
  148. 'channeltpl' => $row['channeltpl'],
  149. 'listtpl' => $row['listtpl'],
  150. 'showtpl' => $row['showtpl'],
  151. 'listtype' => $row['listtype'],
  152. 'pagesize' => $row['pagesize'],
  153. 'vip' => $row['vip'],
  154. ];
  155. db('cms_channel')->where('id', 'in', $childIds)->update($data);
  156. }
  157. });
  158. }
  159. public static function getTypeList()
  160. {
  161. return ['channel' => __('Channel'), 'list' => __('List'), 'link' => __('Link')];
  162. }
  163. public function getFlagList()
  164. {
  165. $config = get_addon_config('cms');
  166. return $config['flagtype'];
  167. }
  168. public static function getStatusList()
  169. {
  170. return ['normal' => __('Normal'), 'hidden' => __('Hidden')];
  171. }
  172. public static function getListtypeList()
  173. {
  174. return ['0' => __('自已和所有子级'), '1' => __('自己和一级子级'), '2' => __('仅自己'), '3' => __('仅包含一级子级(不含自己)'), '4' => __('仅包含所有子级(不含自己)')];
  175. }
  176. public function getTypeTextAttr($value, $data)
  177. {
  178. $value = $value ? $value : $data['type'];
  179. $list = $this->getTypeList();
  180. return isset($list[$value]) ? $list[$value] : '';
  181. }
  182. public function getStatusTextAttr($value, $data)
  183. {
  184. $value = $value ? $value : $data['status'];
  185. $list = $this->getStatusList();
  186. return isset($list[$value]) ? $list[$value] : '';
  187. }
  188. public function getLinkdataAttr($value, $data)
  189. {
  190. $result = [];
  191. if (isset($data['linktype']) && isset($data['linkid']) && $data['linktype'] && $data['linkid']) {
  192. $model = Service::getModelByType($data['linktype'], $data['linkid']);
  193. if ($model) {
  194. $result = [
  195. 'type' => $data['linktype'],
  196. 'source_id' => $data['linkid'],
  197. 'title' => $model['title'] ?? ($model['name'] ?? '未知'),
  198. 'url' => $model['url'] ?? '',
  199. ];
  200. }
  201. }
  202. return $result;
  203. }
  204. /**
  205. * 获取栏目的所有子节点ID,无缓存
  206. * @param int $id 栏目ID
  207. * @param bool $withself 是否包含自身
  208. * @return array
  209. */
  210. public static function getChildrenIds($id, $withself = false)
  211. {
  212. static $tree;
  213. if (!$tree) {
  214. $tree = \fast\Tree::instance();
  215. $tree->init(collection(Channel::order('weigh desc,id desc')->field('id,parent_id,name,type,diyname,status')->select())->toArray(), 'parent_id');
  216. }
  217. $childIds = $tree->getChildrenIds($id, $withself);
  218. return $childIds;
  219. }
  220. /**
  221. * 获取栏目的所有父节点ID,无缓存
  222. * @param int $id 栏目ID
  223. * @param bool $withself 是否包含自身
  224. * @return array
  225. */
  226. public static function getParentsIds($id, $withself = false)
  227. {
  228. static $tree;
  229. if (!$tree) {
  230. $tree = \fast\Tree::instance();
  231. $tree->init(collection(Channel::order('weigh desc,id desc')->field('id,parent_id,name,type,diyname,status')->select())->toArray(), 'parent_id');
  232. }
  233. $childIds = $tree->getParentsIds($id, $withself);
  234. return $childIds;
  235. }
  236. /**
  237. * 刷新栏目统计数据
  238. * @param mixed $ids 栏目ID集合
  239. * @return bool
  240. */
  241. public static function refreshItems($ids)
  242. {
  243. $ids = is_array($ids) ? $ids : explode(',', $ids);
  244. $ids = array_filter(array_unique($ids));
  245. try {
  246. $channelList = self::where('id', 'in', $ids)->select();
  247. foreach ($channelList as $index => $channel) {
  248. if ($channel['parent_id']) {
  249. $ids = array_merge($ids, self::getParentsIds($channel['id']));
  250. }
  251. }
  252. $ids = array_filter(array_unique($ids));
  253. $channelList = self::where('id', 'in', $ids)->select();
  254. foreach ($channelList as $index => $channel) {
  255. $count = Archives::where(function ($query) use ($channel) {
  256. //只统计当前栏目
  257. $query->where('channel_id', $channel['id']);
  258. //按栏目列表类型
  259. //$query->where(function ($query) use ($channel) {
  260. // if ($channel['listtype'] <= 2) {
  261. // $query->whereOr("channel_id", $channel['id']);
  262. // }
  263. // if ($channel['listtype'] == 1 || $channel['listtype'] == 3) {
  264. // $query->whereOr('channel_id', 'in', function ($query) use ($channel) {
  265. // $query->name("cms_channel")->where('parent_id', $channel['id'])->field("id");
  266. // });
  267. // }
  268. // if ($channel['listtype'] == 0 || $channel['listtype'] == 4) {
  269. // $childrenIds = self::getChildrenIds($channel['id'], false);
  270. // if ($childrenIds) {
  271. // $query->whereOr('channel_id', 'in', $childrenIds);
  272. // }
  273. // }
  274. //});
  275. //副栏目
  276. //$query->whereOr("(`channel_ids`!='' AND FIND_IN_SET('{$channel['id']}', `channel_ids`))");
  277. })
  278. ->where('status', 'normal')
  279. ->whereNull('deletetime')
  280. ->count();
  281. $channel->save(['items' => $count]);
  282. }
  283. } catch (\Exception $e) {
  284. \think\Log::record($e->getMessage());
  285. return false;
  286. }
  287. return true;
  288. }
  289. public function model()
  290. {
  291. return $this->belongsTo('Modelx', 'model_id')->setEagerlyType(0);
  292. }
  293. public function getSettingAttr($value, $data)
  294. {
  295. return is_array($value) ? $value : (array)json_decode($data['setting'], true);
  296. }
  297. }