文章摘要
白衣 DeepSeek

一.初步尝试

在 index.php 的$this->title(); 前面加上$this->sticky();,如下:

1
2
3
<h2 class="title"><a href="<?php $this->permalink() ?>"><?php $this->sticky(); $this->title() ?></a></h2>
//其实就是下面这行
<?php $this->sticky(); $this->title() ?>

然后下面代码放在主题的文章列表输出的index.php中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/** 文章置顶 */
$sticky = '1'; //置顶的文章id,多个用|隔开
if($sticky){
$sticky_cids = explode('|',$sticky); //分割文本
$sticky_html = "<span style='color:red'>[置顶] </span>"; //置顶标题的 html
$db = Typecho_Db::get();
$pageSize = $this->options->pageSize;
$select1 = $this->select()->where('type = ?', 'post');
$select2 = $this->select()->where('type = ? && status = ? && created < ?', 'post','publish',time());
//清空原有文章的列队
$this->row = [];
$this->stack = [];
$this->length = 0;
$order = '';
foreach($sticky_cids as $i => $cid) {
if($i == 0) $select1->where('cid = ?', $cid);
else $select1->orWhere('cid = ?', $cid);
$order .= " when $cid then $i";
$select2->where('table.contents.cid != ?', $cid); //避免重复
}
if ($order) $select1->order(null,"(case cid$order end)"); //置顶文章的顺序 按 $sticky 中 文章ID顺序
if ($this->_currentPage == 1) foreach($db->fetchAll($select1) as $sticky_post){ //首页第一页才显示
$sticky_post['sticky'] = $sticky_html;
$this->push($sticky_post); //压入列队
}
$uid = $this->user->uid; //登录时,显示用户各自的私密文章
if($uid) $select2->orWhere('authorId = ? && status = ?',$uid,'private');
$sticky_posts = $db->fetchAll($select2->order('table.contents.created', Typecho_Db::SORT_DESC)->page($this->_currentPage, $this->parameter->pageSize));
foreach($sticky_posts as $sticky_post) $this->push($sticky_post); //压入列队
$this->setTotal($this->getTotal()-count($sticky_cids)); //置顶文章不计算在所有文章内
}

二.进阶集成

上面那种方式,每次需要配置的时候还需要手动去更改index.php文件,有点麻烦。正好之前自己也在写Typecho的主题,就想着把这个文章置顶集成到主题里,通过主题后台直接设置。话不多说,直接开搞,我们先来实现后台填写置顶cid

1.创建后台设置

在主题的functions.php文件中添加以下代码,创建一个新的后台设置选项来存储置顶文章的cid

1
2
3
4
5
// 在主题的 functions.php 文件中
$stickyCids = new Typecho_Widget_Helper_Form_Element_Text('stickyCids', NULL, NULL, _t('置顶文章CID'), _t('在这里填入置顶文章CID,支持多填,用英文逗号隔开'));
$form->addInput($stickyCids);
$stickyCids->setAttribute('class', 'j-setting-content j-setting-global');

2.首页index.php

就当我以为一切顺利的时候,发现了个bug:置顶一直显示少一个,按理说最后一个数字后边是不需要加逗号的(比如69,63,59)但是这样就只会显示69和63两个。经过多次折腾分析,发现就是逗号出了问题,那我们需要确保无论末尾有无逗号,都能正确获取所有的cid

下面是我修改完善后的代码,放进index.php即可,配合上边后台设置的语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
$stickyCids = $this->options->stickyCids;
if ($stickyCids) {
// 用英文逗号分割
$sticky_cids = explode(',', $stickyCids);
// 去除每个元素的空格并过滤掉空元素
$sticky_cids = array_filter(array_map('trim', $sticky_cids), 'trim');
$sticky_html = "<span style='color:red'>[置顶] </span>";
$db = Typecho_Db::get();
$pageSize = $this->options->pageSize;
$select1 = $this->select()->where('type =?', 'post');
$select2 = $this->select()->where('type =? && status =? && created <?', 'post', 'publish', time());
$this->row = [];
$this->stack = [];
$this->length = 0;
$order = '';
$stickyCidMap = [];
$whereClauses = [];
$whereParams = [];
foreach ($sticky_cids as $i => $cid) {
$whereClauses[] = 'cid =?';
$whereParams[] = $cid;
$order.= " when ". $cid. " then ". $i;
$select2->where('table.contents.cid!=?', $cid);
$stickyCidMap[$cid] = $i;
}
if (!empty($whereClauses)) {
$select1->where(implode(' OR ', $whereClauses),...$whereParams);
}
if (empty(trim($order))) {
$orderBy = 'table.contents.created';
$sort = Typecho_Db::SORT_DESC;
} else {
$orderBy = null;
$sort = "(case cid$order end)";
}
if ($orderBy!== null) {
$select1->order($orderBy, $sort);
}
if ($this->_currentPage == 1) {
$stickyPosts = $db->fetchAll($select1);
// 对获取到的置顶文章按照设置的顺序排序
usort($stickyPosts, function ($a, $b) use ($stickyCidMap) {
return $stickyCidMap[$a['cid']] - $stickyCidMap[$b['cid']];
});
foreach ($stickyPosts as $sticky_post) {
$sticky_post['sticky'] = $sticky_html;
$this->push($sticky_post);
}
}
$uid = $this->user->uid;
if ($uid) {
$select2->orWhere('authorId =? && status =?', $uid, 'private');
}
$sticky_posts = $db->fetchAll($select2->order('table.contents.created', Typecho_Db::SORT_DESC)->page($this->_currentPage, $this->parameter->pageSize));
foreach ($sticky_posts as $sticky_post) {
$this->push($sticky_post);
}
$this->setTotal($this->getTotal() - count($sticky_cids));
}
?>

由此,不依赖插件并且植入主题设置的置顶文章功能就完美实现了。或许这就是码代码的乐趣吧,需求驱动、编写代码、发现问题,然后不断尝试去解决问题,最后守得云开见月明!