本文章涉及对主题代码的更改,会导致每次升级主题后都需要重新按照下面的修改方式修改一遍,否则会没有效果。所以如你所见,我的主题版本还停留在20230131版本。
📦 请务必做好备份
在自己操作前请备份好主题文件以及数据库文件,避免操作失误而不知道怎么恢复到原来的状态。
🍦 对小白友好
本文章面向代码小白,将会比较详细地介绍整个思考流程,如果你有一定代码水平,可以直接跳过文章的流程部分直接查看代码。
目前我的博客首页有普通文章和『说一下』这两种不同的东西。『说一下』可以很好地处理一些日常吐槽或者没有必要 懒得 写成文章的事情。接下来会以 Cuteen 主题为例,让你能够给自己的主题加上属于自己的『说一下』。
你刚完成了一篇简短文章,但是文章的长度还有类型都似乎和你以前写过的文章不一样,这篇文章与其叫文章,更像是朋友圈或者空间的说说。所以你想给博客的首页新增加一种类型,用来显示这种别具一格的文章。你理了理思路,知道要做的只有两件事情,就是怎么让 Typecho 能够知道刚完成的这篇文章究竟是什么? 还有 怎样根据文章的类型去渲染不同的样子?。那就开始吧。
让 Typecho 能够知道刚完成的这篇文章究竟是什么
刚好 Typecho 提供了这一功能,它叫自定义字段。
自定义字段
在 Typecho 中,自定义字段是官方为用户自定义留下的一个接口。通过在文章编写页面选择/填写对应的信息,可以使得 Typecho 能够根据这些信息来决定怎样去处理这篇文章的某些东西。
你觉得这东西简直就是为了你上面的想法量身定做的功能,只需要在发表文章的时候选择是文章还是说说不就可以让 Typecho 知道以什么样的风格来渲染了吗。
经过在主题文件夹的一轮寻找,你终于在 core 文件夹下的 Fields.php 找到了 themeFields 函数,这个函数可以给文章添加自定义字段。
🔔请注意:不同主题添加自定义字段文件的位置还有名字都可能不一样,例如 Sunny主题的
themeFields函数在functions.php文件的第 1292 行。这个文件叫什么在哪里可以询问主题的作者。当然你也像我当初一样根据后台文件编辑器下已有的自定义字段名字去主题文件夹 Ctrl + F 一个一个文件找。
你对函数做出了下面的修改:
/* ... */
function themeFields(Typecho\Widget\Helper\Layout $layout)
{
$excerpt = new Typecho\Widget\Helper\Form\Element\Textarea('excerpt', null, null, '文章摘要', '输入自定义摘要。留空自动从文章截取。');
$layout->addItem($excerpt);
$imgst = new Typecho\Widget\Helper\Form\Element\Text('imgst', NULL, NULL, _t('文章缩略图'), _t('在这里填入一个图片URL地址, 以在文章列表加上一张图片'));
$layout->addItem($imgst);
$catalog = new Typecho\Widget\Helper\Form\Element\Radio(
'catalog',
array(
true => _t('启用'),
false => _t('关闭')
),
false,
_t('文章目录'),
_t('默认关闭,启用则会在文章内显示“文章目录”(自动匹配H1~H6标签)')
);
$layout->addItem($catalog);
/* 你上面的代码可能会跟我的不一样,上面代码不用动,在函数的末尾追加下面的代码 */
$isSpeak=new Typecho_Widget_Helper_Form_Element_Select('isSpeak',['0'=>'哒咩','1'=>'是'],'0','是否是说说');
$layout->addItem($isSpeak);
}其中 Typecho_Widget_Helper_Form_Element_Select 类提供了创建下拉选择框的方法,这里创建一个名为 $isSpeak 的 下拉选择框表单字段,选项有 哒咩 和 是 两种,初始值为 0,即默认选中 哒咩 ,同时会在后台编辑器的自定义字段中显示该字段的名称为 是否是说说。
请注意:这里初始值建议选择为 0 ,即文章默认为普通文章。否则会将以前的文章全部变成了说说类型。
添加完之后重新进入后台编辑器能看到下面这个自定义字段:
到此,你给这篇文章选择了『是』这个选项。发表之后 Typecho 就知道你的这篇文章应该是一篇说说了。
怎样根据文章的类型去渲染不同的样子?
接下来你需要对首页代码进行修改,让它能根据刚才定义的字段来作出不同的渲染。你打开 index.php ,看见这样的一段代码:
<?php while ($this->next()): ?>
<?= Context::IndexList($this) ?>
<?php endwhile; ?>这是一段用于循环显示文章列表的代码,由此可知,调用 Context 类的 IndexList 方法会返回带有本次循环文章信息的结构。而这个 Context 类在 core 文件夹下的 Context.php 中。你需要修改这个 IndexList 函数,给它加上一个 if 来判断是不是说说类型的文章:
public static function IndexList($ctx): string
{
if ($ctx->fields->isSpeak === '1') {
//说说类型,你可以自己修改结构
$str = "
<article class='article". ($ctx->sequence % 2 == 0 ? ' flex-row-reverse ': ' ') ."speak'>
<a style='width:100%' href='" . $ctx->permalink . "'>
<div class='speakContent'>
<div class='speakNav'>📖 说一下</div>
<div class='speakDesc'>" . Context::ArticleExcerpt(1000 , $ctx) ."</div>
</div>
</a>
</article>";
}else if ($ctx->fields->isSpeak === '0' || $ctx->fields->isSpeak === null){
# 这里包裹原来的 return $str; 之前的代码:
# $img = self::ImageEcho($ctx);
# ...
# $str .= '<a class="article-description" href = "' . $ctx->permalink . '" > ' . Context::ArticleExcerpt(100, $ctx) . '</a ></div ></article > ';
}
return $str;
}其中 Context::ArticleExcerpt(1000 , $ctx) 是调用 Cuteen 主题的一个方法来限制在首页最多输出 1000 个字,防止过长而不美观,你可以按照自己的需求修改这个值。
请注意:非 Cuteen 主题这里需要将所有
$ctx->改成$this->。(因为 Cuteen 主题调用Context::IndexList($this)将$this作为实参传给了形参$ctx,所以在方法内需要使用$ctx->获取参数)。同样的非 Cuteen 主题是不能使用Context::ArticleExcerpt(1000 , $ctx)的,这里可以使用mb_substr($this->fields->excerpt, 0, 1000, 'UTF-8')输出文章前 1000 个字符。
如果你的主题的 index.php 找不到 while ($this->next()) 的话,可以尝试打开 F12 定位到包裹文章的元素,这里是 <div class="postlist_out ">,然后在 index.php 里搜索这个 class 名字 postlist_out。如果找不到就继续找到这个元素的父元素,即 <main class="main_body">,再在 index.php 里搜索,以此类推。
这里搜索 main_body 成功找到了输出文章列表的文件 article.php
然后进入 article.php,搜索 while ($this->next()),就可以找到文章结构了:
保存之后你便获得一个属于你自己的『说一下』了。当然现在的『说一下』因为没有对应的 CSS ,所以需要你自己来编写对应的 CSS 规则,这里就不展开了。





2024年01月19日 21:24
在设置文章之前,这个字段值是没有的,可以isset判断一下这个字段是否存在,如果存在且值为设置的说说值,就显示说说,else就是普通文章,这样就好啦
2024年01月04日 11:07
“这里初始值建议选择为 0 ,即文章默认为普通文章。否则会将以前的文章全部变成了说说类型。”——这个描述我认为是不准确的。
假如博客已有一些文章,然后在
themeFields中新增了字段(假设新增了文中的isSpeak字段), 那么对在此之前已经创建的文章(下面称为存量文章),它们的$post->fields->isSpeak的值会是 null,而不是themeFields中定义的默认值。这是因为
fields是从数据库读取的,如果是存量文章,它们最后一次保存时fields不会包含刚刚新增的自定义字段。只有在打开管理后台的编辑页面时,才会将新增的自定义字段的默认值合并、显示,此时需要保存一次文章,新增自定义字段的默认值才会被写入并生效。因此,在修改 post.php (或者其他模板代码)的时候,实际上是需要分为 2 类取值,分别为:
(1)非说说(默认情况):isSpeak === "0" || isSpeak === null
(2)说说:isSpeak === "1"
(和具体的可选值关系不大,把 "0" 和 "1" 替换成其他的值,比如 "foo" 和 "bar",也是一样的)
当然,自定义字段如果默认值为 "0",在和 null 合并判断的时候会比较方便,因为它们在 PHP 中都 == false,所以可以直接用
if ($post->fields->isSpeak)分支来判断。2024年01月04日 11:46
感谢提醒(/ω\),这个确实是有点问题。等我今晚回家改一下文章
2024年01月06日 01:47
感觉还是不太对……
我的意思是,新增自定义字段对存量文章的影响,关键点不在于默认值,而在于模板中的判断逻辑。在编写判断逻辑时,需要将存量文章的值为 null 的情况,视为值为默认值。因此:
正确的写法:
错误的写法:(注意这里 null 会落入 else 分支导致判断和预期不符)
2024年01月07日 13:28
最近忙死我了😣,你的意思是当用户如果选择以
"0"作为默认值的时候,仅仅按照文章中的代码修改成$ctx->fields->isSpeak === '0'判断,会导致存量文章的 null 被判断为 正常文章类型。所以文章中更保险的做法是:可以这样理解嘛
2023年12月18日 19:28
说一下这个样式挺好的,我有时候也有种冲动弄一个,其实handsome有不一样的文章样式,但是我没用,感觉这样打破了我首页的美感,所以就没弄😂
2023年12月18日 19:47
哈哈哈哈哈,我是因为有一些东西写成文章的话太水了,就搞了这个东西出来哈哈哈哈