Channel.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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'), 'home_button' => __('Home Button'), 'footer_button' => __('Footer Button')];
  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 getSecondaryList()
  173. {
  174. return ['normal' => __('Normal'), 'hidden' => __('Hidden')];
  175. }
  176. public static function getListtypeList()
  177. {
  178. return ['0' => __('自已和所有子级'), '1' => __('自己和一级子级'), '2' => __('仅自己'), '3' => __('仅包含一级子级(不含自己)'), '4' => __('仅包含所有子级(不含自己)')];
  179. }
  180. public function getTypeTextAttr($value, $data)
  181. {
  182. $value = $value ? $value : $data['type'];
  183. $list = $this->getTypeList();
  184. return isset($list[$value]) ? $list[$value] : '';
  185. }
  186. public function getStatusTextAttr($value, $data)
  187. {
  188. $value = $value ? $value : $data['status'];
  189. $list = $this->getStatusList();
  190. return isset($list[$value]) ? $list[$value] : '';
  191. }
  192. public function getLinkdataAttr($value, $data)
  193. {
  194. $result = [];
  195. if (isset($data['linktype']) && isset($data['linkid']) && $data['linktype'] && $data['linkid']) {
  196. $model = Service::getModelByType($data['linktype'], $data['linkid']);
  197. if ($model) {
  198. $result = [
  199. 'type' => $data['linktype'],
  200. 'source_id' => $data['linkid'],
  201. 'title' => $model['title'] ?? ($model['name'] ?? '未知'),
  202. 'url' => $model['url'] ?? '',
  203. ];
  204. }
  205. }
  206. return $result;
  207. }
  208. /**
  209. * 获取栏目的所有子节点ID,无缓存
  210. * @param int $id 栏目ID
  211. * @param bool $withself 是否包含自身
  212. * @return array
  213. */
  214. public static function getChildrenIds($id, $withself = false)
  215. {
  216. static $tree;
  217. if (!$tree) {
  218. $tree = \fast\Tree::instance();
  219. $tree->init(collection(Channel::order('weigh desc,id desc')->field('id,parent_id,name,type,diyname,status')->select())->toArray(), 'parent_id');
  220. }
  221. $childIds = $tree->getChildrenIds($id, $withself);
  222. return $childIds;
  223. }
  224. /**
  225. * 获取栏目的所有父节点ID,无缓存
  226. * @param int $id 栏目ID
  227. * @param bool $withself 是否包含自身
  228. * @return array
  229. */
  230. public static function getParentsIds($id, $withself = false)
  231. {
  232. static $tree;
  233. if (!$tree) {
  234. $tree = \fast\Tree::instance();
  235. $tree->init(collection(Channel::order('weigh desc,id desc')->field('id,parent_id,name,type,diyname,status')->select())->toArray(), 'parent_id');
  236. }
  237. $childIds = $tree->getParentsIds($id, $withself);
  238. return $childIds;
  239. }
  240. /**
  241. * 刷新栏目统计数据
  242. * @param mixed $ids 栏目ID集合
  243. * @return bool
  244. */
  245. public static function refreshItems($ids)
  246. {
  247. $ids = is_array($ids) ? $ids : explode(',', $ids);
  248. $ids = array_filter(array_unique($ids));
  249. try {
  250. $channelList = self::where('id', 'in', $ids)->select();
  251. foreach ($channelList as $index => $channel) {
  252. if ($channel['parent_id']) {
  253. $ids = array_merge($ids, self::getParentsIds($channel['id']));
  254. }
  255. }
  256. $ids = array_filter(array_unique($ids));
  257. $channelList = self::where('id', 'in', $ids)->select();
  258. foreach ($channelList as $index => $channel) {
  259. $count = Archives::where(function ($query) use ($channel) {
  260. //只统计当前栏目
  261. $query->where('channel_id', $channel['id']);
  262. //按栏目列表类型
  263. //$query->where(function ($query) use ($channel) {
  264. // if ($channel['listtype'] <= 2) {
  265. // $query->whereOr("channel_id", $channel['id']);
  266. // }
  267. // if ($channel['listtype'] == 1 || $channel['listtype'] == 3) {
  268. // $query->whereOr('channel_id', 'in', function ($query) use ($channel) {
  269. // $query->name("cms_channel")->where('parent_id', $channel['id'])->field("id");
  270. // });
  271. // }
  272. // if ($channel['listtype'] == 0 || $channel['listtype'] == 4) {
  273. // $childrenIds = self::getChildrenIds($channel['id'], false);
  274. // if ($childrenIds) {
  275. // $query->whereOr('channel_id', 'in', $childrenIds);
  276. // }
  277. // }
  278. //});
  279. //副栏目
  280. //$query->whereOr("(`channel_ids`!='' AND FIND_IN_SET('{$channel['id']}', `channel_ids`))");
  281. })
  282. ->where('status', 'normal')
  283. ->whereNull('deletetime')
  284. ->count();
  285. $channel->save(['items' => $count]);
  286. }
  287. } catch (\Exception $e) {
  288. \think\Log::record($e->getMessage());
  289. return false;
  290. }
  291. return true;
  292. }
  293. public function model()
  294. {
  295. return $this->belongsTo('Modelx', 'model_id')->setEagerlyType(0);
  296. }
  297. public function getSettingAttr($value, $data)
  298. {
  299. return is_array($value) ? $value : (array)json_decode($data['setting'], true);
  300. }
  301. }