ThinkPHP 6.0 实战教程:从零开始构建一个博客系统
第一部分:准备工作与项目初始化
什么是 ThinkPHP?
ThinkPHP 是一个开源的、快速、简单的面向对象的轻量级 PHP 开发框架,它遵循 Apache2 开源协议发布,是为了简化企业级应用开发和 Web 应用开发而诞生的,ThinkPHP 使用简单的 MVC 架构,让开发更加清晰、高效。

环境要求
在开始之前,请确保你的开发环境满足以下要求:
- PHP 版本: PHP 7.2.5 或更高版本 (推荐 PHP 8.0+)。
- Composer: PHP 的依赖管理工具,用于安装 ThinkPHP 框架和扩展。
- Web 服务器: Apache 2.4+ 或 Nginx 1.8+。
- 数据库: MySQL 5.7+ 或 MariaDB 10.2+。
- PHP 扩展:
pdo,pdo_mysql,mbstring,fileinfo,openssl等。
安装 ThinkPHP
ThinkPHP 官方推荐使用 Composer 来创建项目,打开你的终端(命令行工具),执行以下命令:
# 创建一个名为 myblog 的项目,并选择最新稳定版 composer create-project topthink/think myblog
这个命令会下载 ThinkPHP 框架及其所有依赖,并创建一个名为 myblog 的项目目录。
项目结构解析
安装完成后,进入 myblog 目录,你会看到以下主要文件夹:

app/: 应用目录,我们所有的业务逻辑代码都将写在这里。controller/: 控制器目录,处理用户请求。model/: 模型目录,与数据库交互。view/: 视图目录,负责页面展示。
config/: 配置目录,存放各种配置文件(数据库、路由、缓存等)。public/: Web 访问目录,这是你的 Web 服务器(如 Apache/Nginx)指向的根目录,所有外部请求都会先进入这个目录。route/: 路由目录,定义 URL 和控制器方法的映射关系。vendor/: Composer 依赖目录,存放 ThinkPHP 框架和第三方库。
第二部分:配置与数据库设计
配置数据库连接
我们需要修改配置文件来连接到我们的数据库。
- 打开
config/database.php文件。 - 修改
connections数组中的mysql配置项,填入你的数据库信息。
// config/database.php
return [
// 默认使用的数据库连接配置
'default' => env('database.driver', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
'auto_timestamp' => true,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => env('database.type', 'mysql'),
// 服务器地址
'hostname' => env('database.hostname', '127.0.0.1'),
// 数据库名
'database' => env('database.database', 'myblog'),
// 用户名
'username' => env('database.username', 'root'),
// 密码
'password' => env('database.password', 'your_password'),
// 端口
'hostport' => env('database.hostport', '3306'),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => env('database.charset', 'utf8mb4'),
// 数据库表前缀
'prefix' => env('database.prefix', ''),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('app_debug'),
// 开启字段缓存
'fields_cache' => false,
],
],
];
提示: ThinkPHP 支持使用 .env 文件来管理环境变量,更加安全,你可以在项目根目录创建 .env 文件,将上述配置信息写入其中。
创建数据库和表
在你的 MySQL 中,创建一个名为 myblog 的数据库,我们设计博客系统需要的两张表:article(文章表)和 category(分类表)。
-- 创建分类表 CREATE TABLE `category` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分类ID', `name` varchar(50) NOT NULL COMMENT '分类名称', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类'; -- 创建文章表 CREATE TABLE `article` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '文章ID', varchar(255) NOT NULL COMMENT '文章标题', `content` text NOT NULL COMMENT '文章内容', `category_id` int(11) NOT NULL COMMENT '分类ID', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_category_id` (`category_id`), CONSTRAINT `fk_article_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章';
第三部分:构建 MVC
创建控制器
控制器是用户请求的入口,我们将创建一个 Index 控制器来处理首页和文章列表,一个 Article 控制器来处理文章详情。

在 app/controller/ 目录下创建 Index.php 和 Article.php。
app/controller/Index.php (首页控制器)
<?php
declare (strict_types = 1);
namespace app\controller;
use app\BaseController;
use app\model\Article as ArticleModel;
use think\facade\View;
class Index extends BaseController
{
// 首页,显示文章列表
public function index()
{
// 获取最新的10篇文章,并关联分类信息
$articles = ArticleModel::with(['category'])
->order('create_time', 'desc')
->limit(10)
->select();
// 将数据传递给视图
View::assign('articles', $articles);
return View::fetch();
}
}
app/controller/Article.php (文章控制器)
<?php
declare (strict_types = 1);
namespace app\controller;
use app\BaseController;
use app\model\Article as ArticleModel;
use think\facade\View;
use think\exception\ValidateException;
class Article extends BaseController
{
// 显示文章详情
public function detail($id)
{
// 根据ID查找文章,如果找不到则抛出404异常
$article = ArticleModel::with(['category'])->find($id);
if (!$article) {
abort(404, '文章不存在');
}
View::assign('article', $article);
return View::fetch();
}
}
创建模型
模型负责与数据库交互,我们需要为 article 和 category 表创建对应的模型。
在 app/model/ 目录下创建 Article.php 和 Category.php。
app/model/Article.php (文章模型)
<?php
declare (strict_types = 1);
namespace app\model;
use think\Model;
class Article extends Model
{
// 设置数据表名
protected $name = 'article';
// 设置字段信息
protected $schema = [
'id' => 'int',
'title' => 'string',
'content' => 'string',
'category_id' => 'int',
'create_time' => 'datetime',
'update_time' => 'datetime',
];
// 自动时间戳
protected $autoWriteTimestamp = true;
// 定义与 Category 模型的关联关系 (一个文章属于一个分类)
public function category()
{
return $this->belongsTo(Category::class);
}
}
app/model/Category.php (分类模型)
<?php
declare (strict_types = 1);
namespace app\model;
use think\Model;
class Category extends Model
{
protected $name = 'category';
protected $schema = [
'id' => 'int',
'name' => 'string',
'create_time' => 'datetime',
'update_time' => 'datetime',
];
protected $autoWriteTimestamp = true;
// 定义与 Article 模型的关联关系 (一个分类有多个文章)
public function articles()
{
return $this->hasMany(Article::class);
}
}
创建视图
视图负责展示数据,我们需要创建对应的 HTML 模板文件。
在 view/ 目录下创建 index/index.html 和 article/detail.html。
view/index/index.html (首页模板)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">我的博客 - 首页</title>
<style>
body { font-family: sans-serif; line-height: 1.6; margin: 0; padding: 20px; }
.container { max-width: 800px; margin: auto; }
h1 { color: #333; }
.article-list { list-style: none; padding: 0; }
.article-item { border-bottom: 1px solid #eee; padding: 15px 0; }
.article-item:last-child { border-bottom: none; }
.article-title { font-size: 1.5em; margin-bottom: 5px; }
.article-title a { text-decoration: none; color: #007bff; }
.article-meta { color: #666; font-size: 0.9em; }
</style>
</head>
<body>
<div class="container">
<h1>文章列表</h1>
<ul class="article-list">
{volist name="articles" id="article"}
<li class="article-item">
<h2 class="article-title">
<a href="{:url('article/detail', ['id' => $article.id])}">{$article.title}</a>
</h2>
<p class="article-meta">
分类: {$article.category.name} | 发布时间: {$article.create_time}
</p>
<p>
{mb_substr(strip_tags($article.content), 0, 100, 'UTF-8')}...
</p>
</li>
{/volist}
</ul>
</div>
</body>
</html>
view/article/detail.html (文章详情模板)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">{$article.title} - 我的博客</title>
<style>
body { font-family: sans-serif; line-height: 1.6; margin: 0; padding: 20px; }
.container { max-width: 800px; margin: auto; }
h1 { color: #333; }
.article-meta { color: #666; font-size: 0.9em; margin-bottom: 20px; }
.article-content { white-space: pre-wrap; }
</style>
</head>
<body>
<div class="container">
<h1>{$article.title}</h1>
<p class="article-meta">
分类: {$article.category.name} | 发布时间: {$article.create_time}
</p>
<div class="article-content">
{$article.content}
</div>
<a href="{:url('index/index')}">返回首页</a>
</div>
</body>
</html>
第四部分:配置路由与访问
配置路由
为了让 URL 更美观,我们需要配置路由,打开 route/app.php 文件,添加以下路由规则:
// route/app.php
// 首页路由,指向 Index 控制器的 index 方法
Route::get('/', 'Index/index');
// 文章详情路由,使用变量规则 {id}
// 访问格式如 /article/1
Route::get('article/:id', 'Article/detail')
->pattern(['id' => '\d+']); // 限制 id 必须为数字
配置 Web 服务器
Apache 配置
确保你的 public 目录是 Web 服务器的根目录,在 Apache 的配置文件中(通常是 httpd.conf 或虚拟主机配置文件),设置:
<VirtualHost *:80>
DocumentRoot "/path/to/your/myblog/public"
ServerName myblog.local
<Directory "/path/to/your/myblog/public">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Nginx 配置
同样,将 public 目录作为根目录,在 Nginx 配置中添加:
server {
listen 80;
server_name myblog.local;
root /path/to/your/myblog/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # 或者使用 unix sock
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
配置完成后,重启你的 Web 服务器。
访问网站
在浏览器中访问你配置的域名(http://myblog.local)。
- 首页: 你应该能看到文章列表。
- 文章详情: 点击文章标题,你应该能跳转到文章详情页面。
第五部分:进阶功能(后台管理)
一个完整的博客系统需要一个后台来管理文章和分类。
创建后台控制器
在 app/controller/ 目录下创建一个 admin 目录,并在其中创建控制器。
app/controller/admin/Article.php (后台文章管理)
<?php
declare (strict_types = 1);
namespace app\controller\admin;
use app\BaseController;
use app\model\Article as ArticleModel;
use think\facade\View;
use think\facade\Db;
class Article extends BaseController
{
// 文章列表
public function index()
{
$list = ArticleModel::with(['category'])->paginate(10);
View::assign('list', $list);
return View::fetch();
}
// 添加文章页面
public function add()
{
// 获取所有分类
View::assign('categories', \app\model\Category::select());
return View::fetch();
}
// 保存新文章
public function save()
{
$data = $this->request->post();
try {
ArticleModel::create($data);
return json(['code' => 1, 'msg' => '添加成功']);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => '添加失败:' . $e->getMessage()]);
}
}
// 编辑文章页面
public function edit($id)
{
$article = ArticleModel::find($id);
if (!$article) {
abort(404);
}
View::assign('article', $article);
View::assign('categories', \app\model\Category::select());
return View::fetch();
}
// 更新文章
public function update($id)
{
$data = $this->request->put();
try {
ArticleModel::update($data, ['id' => $id]);
return json(['code' => 1, 'msg' => '更新成功']);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => '更新失败:' . $e->getMessage()]);
}
}
// 删除文章
public function delete($id)
{
try {
ArticleModel::destroy($id);
return json(['code' => 1, 'msg' => '删除成功']);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => '删除失败:' . $e->getMessage()]);
}
}
}
app/controller/admin/Category.php (后台分类管理)
<?php
declare (strict_types = 1);
namespace app\controller\admin;
use app\BaseController;
use app\model\Category as CategoryModel;
use think\facade\View;
class Category extends BaseController
{
public function index()
{
$list = CategoryModel::select();
View::assign('list', $list);
return View::fetch();
}
public function add()
{
if ($this->request->isPost()) {
$data = $this->request->post();
try {
CategoryModel::create($data);
return json(['code' => 1, 'msg' => '添加成功']);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => '添加失败:' . $e->getMessage()]);
}
}
return View::fetch();
}
public function edit($id)
{
$category = CategoryModel::find($id);
if (!$category) {
abort(404);
}
View::assign('category', $category);
return View::fetch();
}
public function update($id)
{
$data = $this->request->put();
try {
CategoryModel::update($data, ['id' => $id]);
return json(['code' => 1, 'msg' => '更新成功']);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => '更新失败:' . $e->getMessage()]);
}
}
public function delete($id)
{
try {
CategoryModel::destroy($id);
return json(['code' => 1, 'msg' => '删除成功']);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => '删除失败:' . $e->getMessage()]);
}
}
}
创建后台视图
在 view/ 目录下创建 admin 目录,并添加对应的模板文件,这里只展示 article/index.html 作为示例。
view/admin/article/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">后台 - 文章管理</title>
<style>
body { font-family: sans-serif; margin: 20px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
a { text-decoration: none; color: #007bff; margin-right: 10px; }
</style>
</head>
<body>
<h1>文章管理</h1>
<a href="{:url('admin/article/add')}">添加文章</a>
<table>
<thead>
<tr>
<th>ID</th>
<th>标题</th>
<th>分类</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{volist name="list" id="article"}
<tr>
<td>{$article.id}</td>
<td>{$article.title}</td>
<td>{$article.category.name}</td>
<td>{$article.create_time}</td>
<td>
<a href="{:url('admin/article/edit', ['id' => $article.id])}">编辑</a>
<a href="javascript:void(0)" onclick="del({$article.id})">删除</a>
</td>
</tr>
{/volist}
</tbody>
</table>
{$list|raw}
<script>
function del(id) {
if (confirm('确定要删除吗?')) {
fetch('{:url("admin/article/delete")}?id=' + id, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
alert('删除成功');
window.location.reload();
} else {
alert(data.msg);
}
});
}
}
</script>
</body>
</html>
配置后台路由
在 route/app.php 中添加后台路由,通常会给后台路由加上一个前缀,如 /admin。
// route/app.php
// ... 前台路由 ...
// 后台路由组,添加 /admin 前缀
Route::group('admin', function () {
Route::get('article', 'admin.Article/index');
Route::get('article/add', 'admin.Article/add');
Route::post('article/save', 'admin.Article/save');
Route::get('article/edit/:id', 'admin.Article/edit');
Route::put('article/update/:id', 'admin.Article/update');
Route::delete('article/delete', 'admin.Article/delete'); // 或者用 GET
Route::get('category', 'admin.Category/index');
// ... 其他分类路由 ...
})->middleware(\app\http\middleware\AuthMiddleware::class); // 假设我们有一个认证中间件
第六部分:总结与进阶
至此,你已经使用 ThinkPHP 6.0 搭建了一个功能完整的前台展示和后台管理的博客系统。
回顾我们学到的东西:
- 项目初始化: 使用 Composer 快速创建项目。
- 配置: 配置数据库连接等核心设置。
- MVC 架构:
- M (Model): 创建模型,定义数据表结构和关联关系。
- V (View): 编写模板,使用 ThinkPHP 的模板引擎渲染数据。
- C (Controller): 创建控制器,接收请求,调用模型,返回视图。
- 路由: 配置清晰的 URL 路由,实现请求的优雅分发。
- 数据库操作: 使用模型进行增、删、改、查,并学习关联查询。
- 后台开发: 扩展了后台管理功能,涉及更复杂的表单处理和 AJAX 交互。
后续可以学习和深入的方向:
- 中间件: 实现用户登录认证、权限控制等,为后台路由添加一个登录检查中间件。
- 验证器: 创建专门的验证器类,对表单数据进行更严谨的验证。
- 数据库迁移: 使用数据库迁移工具来管理数据库结构的变更。
- 缓存: 使用 ThinkPHP 的缓存功能来提升网站性能。
- 命令行工具: 学习使用
think命令行工具来自动化生成代码、执行任务等。 - 部署: 学习如何将你的 ThinkPHP 项目部署到生产服务器(如使用 Docker)。
ThinkPHP 文档是学习的最佳资源,当你遇到问题时,一定要查阅 ThinkPHP 6.0 完全开发手册。
希望这份实战教程对你有帮助!祝你编码愉快!
