ThinkPHP新闻发布系统实战:MVC分层与后台管理完整实现

发布时间:2026/10/23 12:21:14
ThinkPHP新闻发布系统实战:MVC分层与后台管理完整实现 简介基于ThinkPHP框架的新闻发布系统完整源码面向PHP初学者与中小型Web项目开发者提供前台展示与后台管理一体化实现方案。系统采用MVC模式组织模型、视图与控制器集成百度UEditor富文本编辑器覆盖文章发布、分类管理、图片上传等典型功能适合课程设计、毕业设计、实战练习与快速二次开发。RAR压缩包共1117个文件压缩后仅6.43MB内容以414个PHP文件与503个GIF演示图为主辅以HTML/CSS/JS前端资源、SQL数据脚本以及UEditor服务端接口配置可分层查阅源码、界面演示与数据表结构。截至当前已有4735人学习下载通过该资源可完整理解ThinkPHP的路由、数据库操作、模板渲染与安全过滤机制并参照UEditor从部署到接口接入的完整用例配合大量动图演示降低上手门槛是一份易学易用的实战参考。1. 新闻发布系统不难做难的是你还没找到一个能照抄的架子刚接触 ThinkPHP 那阵子我最头疼的事不是写不出增删改查而是写完前台又要写后台写完列表又要搞编辑器好不容易跑通了一回过头来发现代码乱成一锅粥。这个新闻发布系统就是冲着这个痛点来的——它把前台展示、后台管理、编辑器接入、MVC 分层一次给齐学习的价值在于它的完整度刚刚好没有多余的企业级封装也没有省掉关键流程。你照着敲一遍理解了一个请求是怎么从浏览器跑到控制器、再落到模板渲染出来的比看十篇框架教程都有用。这套东西适合谁刚把 PHP 语法过了一遍想接触框架的初学者以及手头有个简单内容站点需求、不想从零开始重写后台的开发者。它跑起来以后的真实体验是后台登录、栏目管理、文章发布、图片上传、前台列表和详情页全流程是通的。你拿到手第一件事不是改需求而是先把它跑通看数据是怎么从数据库走到页面上的。有了这个底子二次开发最多头疼业务设计不会再在框架本身上翻车。2. 技术选型与目录结构ThinkPHP 三件套 MVC 分层怎么落到盘面上这个项目用的是 ThinkPHP 框架配合 PHP 原生 Web 运行环境前端集成 UEditor 富文本编辑器整体是一个标准的 MVC 结构。项目的易学性首先体现在目录组织上——ThinkPHP 的惯例是 Application 目录下按模块划分Home 模块管前台Admin 模块管后台公共代码放 Common这样前后台互不干扰学习的时候也方便对照。2.1 核心目录逻辑Home 模块与 Admin 模块的分工用 ThinkPHP 开发的第一个认知就是模块化目录不是摆设。这个项目的 Application 目录是这样的布局Application/ ├── Common/ // 公共配置和公共函数 │ └── Common/function.php ├── Home/ // 前台模块 │ ├── Controller/ │ │ ├── IndexController.class.php │ │ └── ArticleController.class.php │ ├── Model/ │ │ └── ArticleModel.class.php │ └── View/ │ ├── Index/ │ └── Article/ ├── Admin/ // 后台模块 │ ├── Controller/ │ │ ├── LoginController.class.php │ │ ├── IndexController.class.php │ │ └── ArticleController.class.php │ └── View/ │ ├── Login/ │ ├── Index/ │ └── Article/ └── Runtime/ // 运行时缓存目录前台模块和后台模块在 Controller 层各管各的Model 层可以共用同一张数据表。这个设计对学习最友好的地方在于你想看前台怎么查文章直接开 Home 模块的 ArticleController你想看后台怎么处理表单提交直接开 Admin 模块的 ArticleController。两个文件放在不同目录但功能边界非常清晰。2.2 数据库设计要点文章表和栏目的关联新闻系统的核心表通常是 news 和 category这个项目跑起来需要两张主表。设计上保持了最低成本的三字段核心关联文章表里存一个分类ID外键约束没做为的是降低初学者的理解成本靠模型层去保证数据一致性。CREATE TABLE news ( id int(11) NOT NULL AUTO_INCREMENT, cat_id int(11) NOT NULL DEFAULT 0 COMMENT 分类ID, title varchar(200) NOT NULL COMMENT 标题, content text COMMENT 正文内容, thumb varchar(255) DEFAULT NULL COMMENT 缩略图, create_time int(11) DEFAULT NULL COMMENT 创建时间, update_time int(11) DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE category ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名, sort int(11) DEFAULT 0 COMMENT 排序, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;create_time 用 int 类型存时间戳是 ThinkPHP 自动时间戳的约定写入的时候框架会自动填充不需要手动处理。news 表和 category 表通过 cat_id 关联这个字段在列表页做 JOIN 查询时要用到。缩略图字段 thumb 默认允许为空有些文章没有配图也不影响展示。2.3 UEditor 接入的位置它是一个独立上传入口UEditor 在这个项目里的角色值得单独拿出来说。很多初学者以为编辑器就是一个 textarea 换成了富文本其实它的核心在于图片上传。UEditor 前端是一个独立组件后端需要一个接收上传请求的入口。这个项目把 upload 处理放在 Admin 模块的 PublicController 里日志和权限校验都在那一个动作里完成。UEditor 的上传机制是编辑器把图片通过 HTTP POST 传到后端接口后端保存文件并返回一个 JSON 格式的地址编辑器再把地址回填到内容区。这个链路如果断了最典型的症状就是文字能录入、图片显示红叉。所以拆项目时优先搞懂这块的数据流向比纠结编辑器本身的配置项更有价值。3. 核心模块拆解前台列表、后台管理和编辑器上传的完整链路中间这些代码是这个项目最有营养的部分。三个关键场景串起整个系统前台文章列表和详情展示、后台登录与文章发布、UEditor 的图片上传。每个场景都走一遍请求链路的思路从路由到控制器到模板渲染。3.1 前台文章列表控制器怎么取数、模板怎么循环前台首页的逻辑是最简单也最经典的查询文章列表按时间倒序传到模板去循环。对应的 IndexController 里的代码大致是这个样子?php namespace Home\Controller; use Think\Controller; class IndexController extends Controller { public function index() { // 实例化文章模型 $articleModel M(news); // 查询已发布文章按创建时间倒序一次取10条 $list $articleModel-order(create_time DESC)-limit(10)-select(); // 把数据assign给模板变量 $this-assign(list, $list); $this-display(); } }这里的 M(news) 方法返回值是一个 Model 对象直接链式调用 order 和 limit 就能完成查询。select() 返回的是一个二维数组每一行是一条文章记录。display() 方法会自动匹配当前模块下对应控制器的同名模板——也就是 Application/Home/View/Index/index.html不需要在代码里写模板路径。模板这边的循环代码用的是 ThinkPHP 内置标签写起来很接近原生 PHP但多了些便利语法volist namelist idarticle div classnews-item h3 a href{:U(/article/ . $article[id])}{$article.title}/a /h3 p{$article.create_time|dateY-m-d H:i,###}/p /div /volistvolist 标签是 ThinkPHP 中最常用的循环标签name 对应控制器 assign 的变量名id 是循环体内的变量名。模板函数调用用竖线语法。日期时间戳在模板里直接格式化减少了控制器里的额外处理。注意 U 函数生成路由地址配合后端的路由配置能输出伪静态链接。3.2 后台登录与鉴权Session 校验是怎么拦截的后台和前台最大的区别在于访问控制。登录成功后写入 Session后台所有控制器继承一个需要校验登录状态的基类控制器。这个设计在 ThinkPHP 里非常常规也最值得抄。?php namespace Admin\Controller; use Think\Controller; class BaseController extends Controller { public function _initialize() { // 检查用户是否已登录 if (!session(user_id)) { // 未登录则跳转到登录页 $this-redirect(Login/index); } } }_initialize 方法是 ThinkPHP 控制器的初始化方法它会先于控制器里的任何动作执行。放在 BaseController 里之后后台其他控制器只要继承这个类就自动拥有了登录拦截能力?php namespace Admin\Controller; class ArticleController extends BaseController { public function add() { // 继承BaseController自动执行登录检查 $this-display(); } }这个继承思路是后台模块的核心骨架。以后想加权限细分比如不同管理员只能管理不同栏目可以直接在 BaseController 里扩展校验逻辑控制器的动作方法不用大改这一点对二次开发很友好。3.3 文章发布的后端处理接收表单并写入数据库后台发布文章的表单提交到 Admin 模块的 ArticleController 的 insert 动作处理逻辑包含了字段过滤和时间戳处理。这里有一个很重要的安全习惯POST 过来的数据不要直接整包入库。public function insert() { $data I(post.); $data[create_time] time(); $data[update_time] time(); $model M(news); $result $model-add($data); if ($result) { $this-success(发布成功, U(Article/index)); } else { $this-error(发布失败); } }I(post.) 是 ThinkPHP 的输入过滤函数会自动处理 SQL 注入和 XSS 的常见攻击向量但项目里缩略图和正文本身是 HTML 内容content 字段会经过 UEditor 的过滤处理。add 方法返回的是新插入记录的自增 ID判断这个值是否大于 0 就能确认写入是否成功。这里有个常见翻车点如果表单里没有 create_time 字段直接 I(post.) 取到的数组是缺这个字段的所以必须在控制器里手动补上时间戳否则数据库会报字段不能为空。UEditor 的内容字段 content 也是通过同一个 POST 数组提交的因为 HTML 源码里已经做了转义入库前不需要再 strip_tags。3.4 UEditor 图片上传文件接收与 JSON 返回UEditor 的后端接口是这个项目里最容易出问题的部分核心代码如下public function upload() { $upload new \Think\Upload(); $upload-maxSize 3145728; // 限制文件大小3M $upload-exts array(jpg, gif, png, jpeg); $upload-rootPath ./Uploads/; $upload-savePath editor/; $info $upload-uploadOne($_FILES[upfile]); if (!$info) { $result array(state ERROR, message $upload-getError()); } else { $result array( state SUCCESS, url /Uploads/ . $info[savepath] . $info[savename], title $info[savename], ); } echo json_encode($result); }UEditor 规定的返回格式必须包含 state 和 url 两个字段前端拿到 SUCCESS 之后才会把 url 回填到编辑区。uploadOne 方法接收的是表单文件域的名字这个参数值必须和 UEditor 前端配置的 fileFieldName 一致默认是 upfile。rootPath 是上传根目录savePath 会在根目录下再建子目录保存路径拼接起来就是最终的访问 URL。如果上传返回了 SUCCESS 但图片访问 404优先检查 rootPath 目录是否有写入权限以及 Public 目录下是否已经配置了 Uploads 的访问别名。4. 跑起来的完整过程环境准备、配置修改、数据初始化想把这个项目从压缩包变成一个能访问的站点一共就三步准备运行环境、改数据库配置、导入 SQL 初始化数据。这里用 PHPStudy 这类集成环境说明加载 ThinkPHP 源码包之后打开网站应该直接看到首页最怕的是看到一屏报错先不要慌按下面的顺序排查。4.1 修改数据库连接配置ThinkPHP 3.2 的数据库配置在 Application/Common/Conf/config.php 里你需要改的是主机、库名、账号和密码四项?php return array( DB_TYPE mysql, DB_HOST 127.0.0.1, DB_NAME news_system, DB_USER root, DB_PWD your_password, DB_PORT 3306, DB_PREFIX , );DB_PREFIX 默认留空如果建表时统一加上了前缀比如 tp_news这里需要改成 tp_。改完配置后框架里所有 M(news) 查询都会自动拼接前缀不需要模型层手动处理。如果数据库密码包含特殊字符比如 或 #建议用引号包好避免解析错误。4.2 导入数据并配置伪静态新建一个空数据库导入项目自带的 SQL 文件。导入完成后检查一下 news 表里有没有测试数据——正常情况下应该有两条左右的示例文章方便确认前台列表能显示出内容。前台的路由配置在 Application/Home/Conf/config.php 里URL_ROUTER_ON true, URL_ROUTE_RULES array( article/:id Article/detail, ),开启路由后访问 /article/1 会映射到 ArticleController 的 detail 动作参数 id 会被自动绑定到方法参数里。这里有一个环境相关的细节如果你用的是 Nginx需要在 server 配置里加一行 try_files 指令否则所有带路径的请求都会报 404Apache 环境则需要在项目根目录放置 .htaccess 文件。这是跑通过程中最先踩的坑。4.3 后台入口与默认账户后台入口默认是 /index.php/Admin/Login/index或伪静态路径 Admin/Login/index。这个项目的默认管理账号密码通常写在后端的登录代码里或初始化文档中一般是 admin / admin123 这类便于测试的组合。登录成功后后台首页能看到文章列表、添加文章的入口。后台新增文章页面的最后一步检验整个项目是否真正跑通的关键路径是在 UEditor 里插入一张图片并确认能显示、然后点击发布再回到前台首页刷新列表看新发布的文章是否排到第一条。这一步成功说明数据库写入、上传目录、模板渲染三个环节全部是通的。5. 避坑记录跑这个项目一定会遇到的 4 个问题这个项目虽然定位易学易用但它毕竟是 ThinkPHP 3.2 时代的产物放到今天的 PHP 版本和 Web 服务器环境下有几个问题几乎是必现的。我把最常见的几个坑连原因带解法整理出来照着查能省不少时间。5.1 访问后台地址出现 404 页面现象是输入后台 URL 后页面显示 404但前台访问正常。原因是后台模块的入口没有被正确解析或者服务器伪静态规则没有覆盖到 Admin 模块。解决方式是检查入口文件 index.php 是否存在于项目根目录并确认 Nginx 下伪静态规则里的 rewrite 是否正确匹配了路径。用 PHPStudy 的话直接换成 Apache 模式通常能立刻解决。5.2 UEditor 图片上传返回失败上传图片时前端提示上传失败浏览器控制台看到 HTTP 500 错误。最常见的原因是 ThinkPHP 的 Upload 类在保存文件时没有目录写入权限。解决方式是给 Uploads 目录设置可写权限Linux 下执行 chmod -R 777 Uploads并检查 PHP 的 upload_max_filesize 配置是否小于代码里设置的 3M 上限。5.3 使用 PHP 7 运行时报出 deprecated 错误ThinkPHP 3.2 在设计时主要支持 PHP 5.x跑在 PHP 7.2 以上环境中页面顶部或多或少的会飘出一行行 deprecation 提示。原因是框架内部使用了不少 PHP 7 中已废弃的语法特性。解决方式有两种第一种是修改 php.ini 中的 error_reporting 配置屏蔽 E_DEPRECATED 级别错误第二种是把入口文件 index.php 里加上一行错误级别控制代码。实际部署时用第一种开发调试时应该先把代码错误修掉。5.4 文章内容在详情页不换行后台录入文章时编辑器里分段明确前台展示时所有段落挤在一起。原因是 UEditor 生成的 HTML 标签是基于 p 标签的但模板输出时被样式表覆盖了段间距。解决方式是在详情页模板中为内容区域添加一个容器 class并附上标签默认样式。不是框架问题是 CSS 重置造成的常见现象。6. 二次开发实战给文章表增加点击量统计与热门排序当你把项目跑得滚瓜烂熟之后最想做的第一件事肯定是加点自己的东西。这里以一个常见的需求为例——给文章增加点击量统计并在前台按点击量排序展示热门文章。改动很小但能帮你彻底理解 ThinkPHP 的模型操作与模板数据流之间的关系。先在 news 表里加一个 hits 字段默认值为 0ALTER TABLE news ADD hits int(11) NOT NULL DEFAULT 0;然后在前台的 ArticleController 的 detail 方法里取出文章详情的同时执行一次自增操作把点击量加上去public function detail($id) { $articleModel M(news); // 点击文章时浏览量加1 $articleModel-where(id . $id)-setInc(hits); // 查询文章详情 $article $articleModel-where(id . $id)-find(); $this-assign(article, $article); $this-display(); }setInc 是 ThinkPHP 自带的字段自增方法它内部会生成一条 UPDATE 语句因为先执行了自增再查询所以详情页拿到的 numbers 已经是当前最新的点击量。要加上热门文章列表就在前台 Index 控制器里再写一个按 hits 排序的列表$hotList $articleModel-order(hits DESC)-limit(5)-select(); $this-assign(hotList, $hotList);模板里把 hotList 循环输出到侧边栏。这就是一次完整的增量开发——数据库加字段、控制器加逻辑、模板加循环三个文件动完功能就通了。做这个改动时我习惯每改一步就在浏览器里刷新验证一次先看 hits 字段有没有值再点一次看数字加不加一最后看排序变不变。从那以后我给项目加任何字段都强制走一遍这种最小验证流程不再攒到最后一起测。希望你拿到这个项目以后也能保持这个习惯进步就藏在这些一个字段一个字段养成的直觉里。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询