Archives.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. <?php
  2. namespace addons\cms\model;
  3. use addons\cms\library\IntCode;
  4. use addons\cms\library\Service;
  5. use app\common\library\Auth;
  6. use Hashids\Hashids;
  7. use think\Cache;
  8. use think\Db;
  9. use think\Model;
  10. use traits\model\SoftDelete;
  11. /**
  12. * 文章模型
  13. */
  14. class Archives extends Model
  15. {
  16. protected $name = "cms_archives";
  17. // 开启自动写入时间戳字段
  18. protected $autoWriteTimestamp = 'int';
  19. // 定义时间戳字段名
  20. protected $createTime = 'createtime';
  21. protected $updateTime = 'updatetime';
  22. protected $deleteTime = 'deletetime';
  23. // 追加属性
  24. protected $append = [
  25. 'url',
  26. 'fullurl',
  27. 'likeratio',
  28. 'taglist',
  29. 'create_date',
  30. ];
  31. protected static $config = [];
  32. protected static $tagCount = 0;
  33. use SoftDelete;
  34. /**
  35. * 批量设置数据
  36. * @param $data
  37. * @return $this
  38. */
  39. public function setData($data)
  40. {
  41. if (is_object($data)) {
  42. $data = get_object_vars($data);
  43. }
  44. //替换付费内容标签
  45. if (isset($data['content'])) {
  46. $data['content'] = str_replace(['##paidbegin##', '##paidend##'], ['<paid>', '</paid>'], $data['content']);
  47. $data['content'] = str_replace(['$$paidbegin$$', '$$paidend$$'], ['<paid>', '</paid>'], $data['content']);
  48. }
  49. $this->data = array_merge($this->data, $data);
  50. $this->origin = $this->data;
  51. return $this;
  52. }
  53. protected static function init()
  54. {
  55. $config = get_addon_config('cms');
  56. self::$config = $config;
  57. }
  58. public function getAttr($name)
  59. {
  60. //获取自定义字段关联表数据
  61. if (!isset($this->data[$name]) && preg_match("/(.*)_value\$/i", $name, $matches)) {
  62. $key = $this->data[$matches[1]] ?? '';
  63. if (!$key) {
  64. return '';
  65. }
  66. return Service::getRelationFieldValue('model', $this->data['model_id'], $matches[1], $key);
  67. }
  68. return parent::getAttr($name);
  69. }
  70. /**
  71. * 获取加密后的ID
  72. * @param $value
  73. * @param $data
  74. * @return string
  75. */
  76. public function getEidAttr($value, $data)
  77. {
  78. $value = $data['id'];
  79. if (self::$config['archiveshashids']) {
  80. $value = IntCode::encode($value);
  81. }
  82. return $value;
  83. }
  84. public function getCreateDateAttr($value, $data)
  85. {
  86. return human_date($data['createtime']);
  87. }
  88. public function getHasimageAttr($value, $data)
  89. {
  90. return $this->getData("image") ? true : false;
  91. }
  92. public function getIscommentAttr($value, $data)
  93. {
  94. //优先判断全局评论开关
  95. $iscomment = self::$config['iscomment'] ?? 1;
  96. if ($iscomment) {
  97. $iscomment = $value ? $value : 0;
  98. }
  99. return $iscomment;
  100. }
  101. public function getImageAttr($value, $data)
  102. {
  103. $value = $value ? $value : self::$config['default_archives_img'];
  104. return cdnurl($value, true);
  105. }
  106. public function getImagesAttr($value, $data)
  107. {
  108. if (!$data['images']) {
  109. return '';
  110. }
  111. $images = explode(',', $data['images']);
  112. foreach ($images as $index => &$image) {
  113. $image = cdnurl($image, true);
  114. }
  115. return implode(',', $images);
  116. }
  117. public function getImagesListAttr($value, $data)
  118. {
  119. if (isset($data['images_list'])) {
  120. return $data['images_list'];
  121. }
  122. $images = $this->getAttr("images");
  123. return $images ? array_filter(explode(',', $images)) : [];
  124. }
  125. /**
  126. * 获取格式化的内容
  127. */
  128. public function getContentAttr($value, $data)
  129. {
  130. static $formatted = false;
  131. $value = $data['content'];
  132. if (!$formatted) {
  133. //如果内容中包含有付费标签
  134. $pattern = '/<paid>(.*?)<\/paid>/is';
  135. if (preg_match($pattern, $value) && !$this->getAttr('ispaid')) {
  136. $paytype = static::$config['defaultpaytype'];
  137. $payurl = addon_url('cms/order/submit', ['id' => $data['id'], 'paytype' => $paytype]);
  138. if (!isset($data['price']) || $data['price'] <= 0) {
  139. $value = preg_replace($pattern, "<div class='alert alert-warning alert-paid'><a href='javascript:' class=''>内容已经隐藏</a></div>", $value);
  140. } else {
  141. $value = preg_replace($pattern, "<div class='alert alert-warning alert-paid'><a href='{$payurl}' class='btn-paynow' data-price='{$data['price']}' data-paytype='{$paytype}'>内容已经隐藏,点击付费后查看</a></div>", $value);
  142. }
  143. }
  144. //实时关键字内链替换
  145. $config = get_addon_config('cms');
  146. if (isset($config['realtimereplacelink']) && $config['realtimereplacelink']) {
  147. $value = Service::autolinks($value);
  148. }
  149. $formatted = true;
  150. }
  151. return $value;
  152. }
  153. /**
  154. * 获取金额
  155. */
  156. public function getPriceAttr($value, &$data)
  157. {
  158. if (isset($data['price'])) {
  159. return $data['price'];
  160. }
  161. $price = 0;
  162. if (isset($data['model_id'])) {
  163. $model = Modelx::get($data['model_id']);
  164. if ($model && in_array('price', $model['fields'])) {
  165. $price = Db::name($model['table'])->where('id', $data['id'])->value('price');
  166. }
  167. }
  168. $data['price'] = $price;
  169. return $price;
  170. }
  171. /**
  172. * 判断是否支付
  173. */
  174. public function getIspayAttr($value, &$data)
  175. {
  176. return $this->getAttr('ispaid');
  177. }
  178. /**
  179. * 判断是否支付
  180. */
  181. public function getIspaidAttr($value, &$data)
  182. {
  183. if (isset($data['ispaid'])) {
  184. return $data['ispaid'];
  185. }
  186. $channel = isset($this->channel) ? $this->channel : null;
  187. //只有当未设定VIP时才判断付费字段
  188. if ($channel && !$channel->vip) {
  189. //如果未定义price字段或price字段值为0
  190. if (!isset($data['price']) || $data['price'] == 0) {
  191. return true;
  192. }
  193. }
  194. $isvip = $channel && isset($channel['vip']) && $channel['vip'] && Auth::instance()->vip >= $channel->vip ? true : false;
  195. $data['ispaid'] = $isvip || \addons\cms\library\Order::check($data['id']);
  196. return $data['ispaid'];
  197. }
  198. /**
  199. * 判断是否是部分内容付费
  200. */
  201. public function getIsPaidPartOfContentAttr($value, $data)
  202. {
  203. if (isset($data['is_paid_part_of_content'])) {
  204. return $data['is_paid_part_of_content'];
  205. }
  206. $value = isset($this->origin['content']) ? $this->origin['content'] : '';
  207. $result = preg_match('/<paid>(.*?)<\/paid>/is', $value);
  208. $data['is_paid_part_of_content'] = $result;
  209. return $result;
  210. }
  211. /**
  212. * 获取下载地址列表
  213. */
  214. public function getDownloadurlListAttr($value, $data)
  215. {
  216. $titleArr = isset(self::$config['downloadtype']) ? self::$config['downloadtype'] : [];
  217. $downloadurl = is_array($data['downloadurl']) ? $data['downloadurl'] : (array)json_decode($data['downloadurl'], true);
  218. $downloadurl = array_filter($downloadurl);
  219. $list = [];
  220. foreach ($downloadurl as $index => $item) {
  221. if (!is_array($item)) {
  222. $urlArr = explode(' ', $item);
  223. $result['name'] = $index;
  224. $result['title'] = isset($titleArr[$index]) ? $titleArr[$index] : '其它';
  225. $result['url'] = cdnurl($urlArr[0], true);
  226. $result['password'] = isset($urlArr[1]) ? $urlArr[1] : '';
  227. $list[] = $result;
  228. } elseif (is_array($item) && isset($item['name']) && isset($item['url']) && $item['url']) {
  229. $item['url'] = cdnurl($item['url'], true);
  230. $result = $item;
  231. $result['title'] = isset($titleArr[$item['name']]) ? $titleArr[$item['name']] : '其它';
  232. $result['password'] = $result['password'] ?? '';
  233. $list[] = $result;
  234. }
  235. }
  236. return $list;
  237. }
  238. public function getTaglistAttr($value, &$data)
  239. {
  240. if (isset($data['taglist'])) {
  241. return $data['taglist'];
  242. }
  243. $tags = array_filter(explode(",", $data['tags']));
  244. $tagList = [];
  245. if (stripos(self::$config['rewrite']['tag/index'], ":id") !== false) {
  246. $tagList = Tag::where('name', 'in', $tags)->column('name,id');
  247. }
  248. $tagList = array_change_key_case($tagList, CASE_LOWER);
  249. $time = $data['createtime'] ?? time();
  250. $list = [];
  251. foreach ($tags as $k => $v) {
  252. $v_lower = strtolower($v);
  253. $vars = [':name' => $v, ':diyname' => $v, ':id' => isset($tagList[$v_lower]) ? $tagList[$v_lower] : '0', ':year' => date("Y", $time), ':month' => date("m", $time), ':day' => date("d", $time)];
  254. $list[] = ['name' => $v, 'url' => addon_url('cms/tag/index', $vars)];
  255. }
  256. $data['taglist'] = $list;
  257. return $list;
  258. }
  259. public function getUrlAttr($value, $data)
  260. {
  261. return $this->buildUrl($value, $data);
  262. }
  263. public function getFullurlAttr($value, $data)
  264. {
  265. return $this->buildUrl($value, $data, true);
  266. }
  267. private function buildUrl($value, $data, $domain = false)
  268. {
  269. $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id'];
  270. $catename = isset($this->channel) && $this->channel ? $this->channel->diyname : 'all';
  271. $cateid = isset($this->channel) && $this->channel ? $this->channel->id : 0;
  272. $time = $data['publishtime'] ?? time();
  273. $vars = [
  274. ':id' => $data['id'],
  275. ':eid' => $this->getEidAttr($data['id'], $data),
  276. ':diyname' => $diyname,
  277. ':channel' => $data['channel_id'],
  278. ':catename' => $catename,
  279. ':cateid' => $cateid,
  280. ':year' => date("Y", $time),
  281. ':month' => date("m", $time),
  282. ':day' => date("d", $time),
  283. ];
  284. $suffix = static::$config['moduleurlsuffix']['archives'] ?? static::$config['urlsuffix'];
  285. return addon_url('cms/archives/index', $vars, $suffix, $domain);
  286. }
  287. public function getLikeratioAttr($value, $data)
  288. {
  289. return ($data['dislikes'] > 0 ? min(1, $data['likes'] / ($data['dislikes'] + $data['likes'])) : ($data['likes'] ? 1 : 0.5)) * 100;
  290. }
  291. public function getStyleTextAttr($value, $data)
  292. {
  293. $color = $this->getAttr("style_color");
  294. $color = $color ? $color : "inherit";
  295. $color = str_replace(['#', ' '], '', $color);
  296. $bold = $this->getAttr("style_bold") ? "bold" : "normal";
  297. $attr = ["font-weight:{$bold};"];
  298. if (stripos($color, ',') !== false) {
  299. list($first, $second) = explode(',', $color);
  300. $attr[] = "background-image: -webkit-linear-gradient(0deg, #{$first} 0%, #{$second} 100%);background-image: linear-gradient(90deg, #{$first} 0%, #{$second} 100%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;";
  301. } else {
  302. $attr[] = "color:#{$color};";
  303. }
  304. return implode('', $attr);
  305. }
  306. public function getStyleBoldAttr($value, $data)
  307. {
  308. return in_array('b', explode('|', $data['style']));
  309. }
  310. public function getStyleColorAttr($value, $data)
  311. {
  312. $styleArr = explode('|', $data['style']);
  313. foreach ($styleArr as $index => $item) {
  314. if (preg_match('/\,|#/', $item)) {
  315. return $item;
  316. }
  317. }
  318. return '';
  319. }
  320. /**
  321. * 获取内容页分页HTML
  322. */
  323. public function getPagerHTML($page, $total, $simple = false)
  324. {
  325. if ($total <= 1) {
  326. return '';
  327. }
  328. $result = \addons\cms\library\Bootstrap::make([], 1, $page, $total, $simple, ['path' => $this->url, 'simple' => $simple]);
  329. return "<div class='pager'>" . $result->render() . "</div>";
  330. }
  331. /**
  332. * 获取文档列表
  333. * @param $params
  334. * @return array|false|\PDOStatement|string|\think\Collection
  335. */
  336. public static function getArchivesList($params)
  337. {
  338. $type = empty($params['type']) ? '' : $params['type'];
  339. $model = !isset($params['model']) ? '' : $params['model'];
  340. $channel = !isset($params['channel']) ? '' : $params['channel'];
  341. $special = !isset($params['special']) ? '' : $params['special'];
  342. $tags = empty($params['tags']) ? '' : $params['tags'];
  343. $condition = empty($params['condition']) ? '' : $params['condition'];
  344. $field = empty($params['field']) ? '*' : $params['field'];
  345. $flag = empty($params['flag']) ? '' : $params['flag'];
  346. $row = empty($params['row']) ? 10 : (int)$params['row'];
  347. $orderby = empty($params['orderby']) ? 'weigh' : $params['orderby'];
  348. $orderway = empty($params['orderway']) ? 'desc' : strtolower($params['orderway']);
  349. $limit = empty($params['limit']) ? $row : $params['limit'];
  350. $imgwidth = empty($params['imgwidth']) ? '' : $params['imgwidth'];
  351. $imgheight = empty($params['imgheight']) ? '' : $params['imgheight'];
  352. $addon = empty($params['addon']) ? false : $params['addon'];
  353. $orderway = in_array($orderway, ['asc', 'desc']) ? $orderway : 'desc';
  354. $paginate = !isset($params['paginate']) ? false : $params['paginate'];
  355. $page = !isset($params['page']) ? 1 : (int)$params['page'];
  356. $with = !isset($params['with']) ? 'channel' : $params['with'];
  357. $where = ['status' => 'normal'];
  358. list($cacheKey, $cacheExpire) = Service::getCacheKeyExpire('arclist', $params);
  359. $where['deletetime'] = ['exp', Db::raw('IS NULL')]; //by erastudio
  360. if ($model !== '') {
  361. $where['model_id'] = ['in', $model];
  362. }
  363. self::$tagCount++;
  364. $archivesModel = self::with($with)->alias('a');
  365. if ($channel !== '') {
  366. if ($type === 'son') {
  367. $subQuery = Channel::where('parent_id', 'in', $channel)->field('id')->buildSql();
  368. //子级
  369. $where['channel_id'] = ['exp', Db::raw(' IN ' . '(' . $subQuery . ')')];
  370. } elseif ($type === 'sons') {
  371. //所有子级
  372. $where['channel_id'] = ['in', Channel::getChannelChildrenIds($channel)];
  373. } else {
  374. $where['channel_id'] = ['in', $channel];
  375. }
  376. }
  377. //如果有设置标志,则拆分标志信息并构造condition条件
  378. if ($flag !== '') {
  379. if (stripos($flag, '&') !== false) {
  380. $arr = [];
  381. foreach (explode('&', $flag) as $k => $v) {
  382. $arr[] = "FIND_IN_SET('{$v}', flag)";
  383. }
  384. if ($arr) {
  385. $condition .= "(" . implode(' AND ', $arr) . ")";
  386. }
  387. } else {
  388. $condition .= ($condition ? ' AND ' : '');
  389. $arr = [];
  390. foreach (explode(',', str_replace('|', ',', $flag)) as $k => $v) {
  391. $arr[] = "FIND_IN_SET('{$v}', flag)";
  392. }
  393. if ($arr) {
  394. $condition .= "(" . implode(' OR ', $arr) . ")";
  395. }
  396. }
  397. }
  398. if ($special) {
  399. $specialModel = Special::get($special, [], true);
  400. if ($specialModel && $specialModel['tag_ids']) {
  401. $archivesModel->where("a.id", "in", function ($query) use ($specialModel) {
  402. return $query->name("cms_taggable")->where("tag_id", "in", $specialModel['tag_ids'])->field("archives_id");
  403. });
  404. }
  405. }
  406. $order = $orderby == 'rand' ? Db::raw('rand()') : (preg_match("/\,|\s/", $orderby) ? $orderby : "{$orderby} {$orderway}");
  407. $order = $orderby == 'weigh' ? $order . ',publishtime DESC' : $order;
  408. // 如果有筛选标签,则采用子查询
  409. if ($tags) {
  410. $tagIds = Tag::where('name', 'in', explode(',', $tags))->cache(true)->column("id");
  411. $archivesModel->where("a.id", "in", function ($query) use ($tagIds) {
  412. return $query->name("cms_taggable")->where("tag_id", "in", $tagIds)->field("archives_id");
  413. });
  414. }
  415. $modelInfo = null;
  416. $prefix = config('database.prefix');
  417. $archivesModel
  418. ->where($where)
  419. ->where($condition)
  420. ->field($field, false, $prefix . "cms_archives", "a")
  421. ->orderRaw($order);
  422. if ($addon && (is_numeric($model) || $channel)) {
  423. if ($channel) {
  424. //如果channel设置了多个值则只取第一个作为判断
  425. $channelArr = explode(',', $channel);
  426. $channelinfo = Channel::get($channelArr[0], [], true);
  427. $model = $channelinfo ? $channelinfo['model_id'] : $model;
  428. }
  429. // 查询相关联的模型信息
  430. $modelInfo = Modelx::get($model, [], true);
  431. if ($modelInfo) {
  432. $archivesModel->join($modelInfo['table'] . ' n', 'a.id=n.id', 'LEFT');
  433. if ($addon == 'true') {
  434. $archivesModel->field('id,content', true, $prefix . $modelInfo['table'], 'n');
  435. } else {
  436. $archivesModel->field($addon, false, $prefix . $modelInfo['table'], 'n');
  437. }
  438. }
  439. }
  440. // var_dump(request()->url());
  441. // if(request()->url()) {
  442. //
  443. // }
  444. if ($paginate) {
  445. list($listRows, $simple, $config) = Service::getPaginateParams((isset($params['page']) ? 'page' : 'apage' . self::$tagCount), $params);
  446. if (isset($params['page'])) {
  447. $config['page'] = $page;
  448. }
  449. $list = $archivesModel->paginate($listRows, $simple, $config);
  450. } else {
  451. $list = $archivesModel->limit($limit)->cache($cacheKey, $cacheExpire)->select();
  452. }
  453. if ($modelInfo && $modelInfo->fields) {
  454. Service::appendTextAndList('model', $modelInfo->id, $list, true);
  455. }
  456. self::render($list, $imgwidth, $imgheight);
  457. return $list;
  458. }
  459. /**
  460. * 标题高亮搜索结果
  461. */
  462. public function highlight($title, $keywords = '')
  463. {
  464. if ($keywords == '') {
  465. return $title;
  466. }
  467. $re = '/(' . str_replace(" ", '|', preg_quote($keywords)) . ')/i';
  468. return preg_replace($re, '<span class="highlight">$0</span>', $title);
  469. }
  470. /**
  471. * 渲染数据
  472. * @param array $list
  473. * @param int $imgwidth
  474. * @param int $imgheight
  475. * @return array
  476. */
  477. public static function render(&$list, $imgwidth, $imgheight)
  478. {
  479. $width = $imgwidth ? 'width="' . $imgwidth . '"' : '';
  480. $height = $imgheight ? 'height="' . $imgheight . '"' : '';
  481. foreach ($list as $k => &$v) {
  482. $v['textlink'] = '<a href="' . $v['url'] . '">' . $v['title'] . '</a>';
  483. $v['channellink'] = '<a href="' . ($v['channel']['url'] ?? '#') . '">' . ($v['channel']['name'] ?? '未知') . '</a>';
  484. $v['imglink'] = '<a href="' . $v['url'] . '"><img src="' . $v['image'] . '" ' . $width . ' ' . $height . ' /></a>';
  485. $v['img'] = '<img src="' . $v['image'] . '" ' . $width . ' ' . $height . ' />';
  486. }
  487. return $list;
  488. }
  489. /**
  490. * 获取分页列表
  491. * @param array $list
  492. * @param array $params
  493. * @return array
  494. */
  495. public static function getPageList($list, $params)
  496. {
  497. $imgwidth = empty($params['imgwidth']) ? '' : $params['imgwidth'];
  498. $imgheight = empty($params['imgheight']) ? '' : $params['imgheight'];
  499. return self::render($list, $imgwidth, $imgheight);
  500. }
  501. /**
  502. * 获取分页过滤
  503. * @param array $list
  504. * @param array $params
  505. * @return array
  506. */
  507. public static function getPageFilter($list, $params)
  508. {
  509. $exclude = empty($params['exclude']) ? '' : $params['exclude'];
  510. return $list;
  511. }
  512. /**
  513. * 获取分页排序
  514. * @param array $list
  515. * @param array $params
  516. * @return array
  517. */
  518. public static function getPageOrder($list, $params)
  519. {
  520. $exclude = empty($params['exclude']) ? '' : $params['exclude'];
  521. return $list;
  522. }
  523. /**
  524. * 获取上一页下一页
  525. * @param array $params
  526. * @return array
  527. */
  528. public static function getPrevNext($params = [])
  529. {
  530. $type = isset($params['type']) ? $params['type'] : 'prev';
  531. $channel = isset($params['channel']) ? $params['channel'] : '';
  532. $archives = isset($params['archives']) ? $params['archives'] : '';
  533. $condition = isset($params['condition']) ? $params['condition'] : '';
  534. $model = self::where('id', $type === 'prev' ? '<' : '>', $archives)->where('status', 'normal');
  535. if ($channel !== '') {
  536. $model->where('channel_id', 'in', $channel);
  537. }
  538. if (isset($condition)) {
  539. $model->where($condition);
  540. }
  541. $model->order($type === 'prev' ? 'id desc' : 'id asc');
  542. $row = $model->find();
  543. return $row;
  544. }
  545. /**
  546. * 获取SQL查询结果
  547. */
  548. public static function getQueryList($params)
  549. {
  550. $sql = isset($params['sql']) ? $params['sql'] : '';
  551. $bind = isset($params['bind']) ? explode(',', $params['bind']) : [];
  552. list($cacheKey, $cacheExpire) = Service::getCacheKeyExpire('sql', $params);
  553. $list = Cache::get($cacheKey);
  554. if (!$list) {
  555. $list = Db::query($sql, $bind);
  556. Cache::set($cacheKey, $list, $cacheExpire);
  557. }
  558. return $list;
  559. }
  560. /**
  561. * 关联模型
  562. */
  563. public function user()
  564. {
  565. return $this->belongsTo("\app\common\model\User", 'user_id', 'id', [], 'LEFT')->setEagerlyType(1);
  566. }
  567. /**
  568. * 关联模型
  569. */
  570. public function model()
  571. {
  572. return $this->belongsTo("Modelx", 'model_id')->setEagerlyType(1);
  573. }
  574. /**
  575. * 关联栏目模型
  576. */
  577. public function channel()
  578. {
  579. return $this->belongsTo("Channel", 'channel_id', 'id', [], 'LEFT')->setEagerlyType(1);
  580. }
  581. }