核心难点是避免踩坑:Model层应统一用继承think\Model的NewsModel类而非Db原生查询,UEditor上传需校准php.ini限制、serverUrl路径及目录权限,分页卡顿主因是count(*)未走索引,SEO需手动assign标题与strip_tags后截取描述。

直接用 ThinkPHP 开发新闻发布系统,核心难点不在「能不能做」,而在「怎么避免踩坑」——比如 Model 层写 SQL 混用原生查询和 ORM 导致事务失效、UEditor 上传路径没配对导致图片 404、后台路由没加权限中间件被未登录用户绕过。这些问题在本地跑通不等于上线可用。
ThinkPHP 6.x 中 News 模型该用 Db 还是 Model?
多数人一上来就写 Db::name('news')->select(),看似简单,但后续加软删除、状态过滤、关联分类时会反复补逻辑。推荐统一走 NewsModel 类,继承 think\Model,并在其中定义:
-
protected $table = 'news';显式指定表名,避免命名约定失效 -
protected $type = ['publish_time' => 'datetime'];确保时间字段自动转 Carbon 实例 -
protected $autoWriteTimestamp = true;配合created_at/updated_at字段省去手动赋值 - 在
scopeStatus方法里封装where('status', 1),前台列表直接链式调用NewsModel::scopeStatus()->select()
混用 Db 和 Model 容易导致事件钩子(如 afterInsert)不触发,尤其在批量导入新闻时数据不一致。
UEditor 图片上传失败的三个真实原因
不是配置文件写错,而是环境细节没对齐。常见失败点:
立即学习“PHP免费学习笔记(深入)”;
-
php.ini中upload_max_filesize和post_max_size小于图片实际大小,报错信息是undefined index: upfile,但日志里看不到具体限制值 -
ueditor.config.js的serverUrl写成相对路径(如/public/ueditor/php/controller.php),而 ThinkPHP 路由已重写,必须用完整 URL 或 ThinkPHP 路由地址index.php?s=/admin/news/upload - 上传目录权限不足,Linux 下
runtime和public/uploads目录需保证 webserver 用户(如 www-data)有写权限,仅755不够,得是775
验证方式:直接浏览器访问 index.php?s=/admin/news/upload,返回 JSON 格式 {"state":"SUCCESS"} 才算通路正确。
后台新闻列表分页卡顿,不是数据量问题
当新闻表超过 5 万条,NewsModel::paginate(20) 会变慢,但真正瓶颈常在 count(*) 查询没走索引。检查三点:
- 确保
status字段有单独索引,或与publish_time组合索引(KEY idx_status_time (status,publish_time)) - 禁用
total自动统计:改用paginate(20, false, ['query' => request()->param()]),自己控制总数来源(比如缓存预估) - 避免在
select()前调用with('category')再分页,应先分页查 ID 列表,再用whereIn('id', [...])查详情和关联
TP6 默认分页会执行两条 SQL(count + limit),如果 count 走全表扫描,比 limit 本身还慢三倍。
新闻详情页 SEO 友好需要手动补什么
ThinkPHP 不自动生成 <meta> 标签。关键动作只有两个:
- 在详情控制器里,用
$this->assign('seo_title', $news['title'].' - '.$site_name),然后视图中输出<title><?=$seo_title?></title> - 在
view/news/detail.html顶部插入:<meta name="description" content="<?=mb_substr(strip_tags($news['content']), 0, 120, 'utf-8')?>...">,注意strip_tags必须手工加,否则 UEditor 插入的 HTML 标签会污染 description
别依赖模板引擎自动截取,mb_substr 对富文本里的换行、空格、图片标签毫无感知,必须先 strip_tags 再截,否则首页源码里 description 可能出现半截 <img src=...。



















