Channel.php 12 KB

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