发布日期: 2023年12月15日
ThinkPHP 6.0简介与新特性
ThinkPHP是一款国内领先的PHP开源框架,6.0版本在之前版本的基础上进行了全面重构,引入了更多现代化特性:
- 完全支持PHP 7.1+特性
- 采用更加严谨的PSR规范
- 引入中间件机制替代原有的行为扩展
- 依赖注入和支持更加完善
- 改进了数据库查询和模型功能
- 提供了更强大的命令行工具
本教程将通过开发一个完整的企业级内容管理系统(CMS),深入讲解ThinkPHP 6.0的核心功能和最佳实践。
环境搭建与项目初始化
环境要求
PHP >= 7.1.0
PDO PHP Extension
MBstring PHP Extension
JSON PHP Extension
Composer
使用Composer创建项目
# 创建ThinkPHP项目
composer create-project topthink/think tp-cms
# 进入项目目录
cd tp-cms
# 启动内置服务器
php think run
基础配置修改
修改config目录下的配置文件:
// config/database.php 数据库配置
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'tp_cms',
'username' => 'root',
'password' => 'password',
'charset' => 'utf8mb4',
'prefix' => 'cms_',
],
],
];
// config/app.php 应用配置
return [
'app_debug' => true,
'default_lang' => 'zh-cn',
'default_timezone' => 'Asia/Shanghai',
'url_html_suffix' => 'html',
];
系统架构设计
目录结构规划
tp-cms/
├── app/ # 应用目录
│ ├── controller/ # 控制器层
│ │ ├── admin/ # 后台控制器
│ │ └── home/ # 前台控制器
│ ├── model/ # 模型层
│ │ ├── admin/ # 后台模型
│ │ └── home/ # 前台模型
│ ├── view/ # 视图层
│ │ ├── admin/ # 后台模板
│ │ └── home/ # 前台模板
│ └── middleware/ # 中间件
├── config/ # 配置目录
├── public/ # web入口目录
├── route/ # 路由定义
├── database/ # 数据库文件
└── extend/ # 扩展类库
多应用模式配置
ThinkPHP 6.0支持多应用模式,我们需要创建后台管理应用:
# 安装多应用扩展
composer require topthink/think-multi-app
# 创建admin应用目录结构
php think build admin
路由规划
// route/app.php
use thinkfacadeRoute;
// 前台路由
Route::get('/', 'home/Index/index');
Route::get('article/:id', 'home/Article/detail');
Route::get('category/:id', 'home/Category/index');
// 后台路由组
Route::group('admin', function() {
Route::get('/', 'admin/Index/index');
Route::get('login', 'admin/Login/index');
Route::post('login', 'admin/Login/check');
})->prefix('admin/');
数据库设计与模型实现
数据库表设计
// 用户表
CREATE TABLE `cms_user` (
`id` int(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
`email` varchar(100) NOT NULL DEFAULT '' COMMENT '邮箱',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态',
`create_time` int(11) NOT NULL DEFAULT '0',
`update_time` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
// 文章表
CREATE TABLE `cms_article` (
`id` int(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`category_id` int(11) NOT NULL DEFAULT '0' COMMENT '分类ID',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '标题',
`content` text COMMENT '内容',
`author` varchar(50) NOT NULL DEFAULT '' COMMENT '作者',
`keywords` varchar(255) NOT NULL DEFAULT '' COMMENT '关键词',
`description` varchar(500) NOT NULL DEFAULT '' COMMENT '描述',
`thumb` varchar(255) NOT NULL DEFAULT '' COMMENT '缩略图',
`views` int(11) NOT NULL DEFAULT '0' COMMENT '浏览量',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态',
`create_time` int(11) NOT NULL DEFAULT '0',
`update_time` int(11) NOT NULL DEFAULT '0',
`publish_time` int(11) NOT NULL DEFAULT '0' COMMENT '发布时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表';
// 分类表
CREATE TABLE `cms_category` (
`id` int(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称',
`pid` int(11) NOT NULL DEFAULT '0' COMMENT '父级ID',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态',
`create_time` int(11) NOT NULL DEFAULT '0',
`update_time` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类表';
模型实现
// app/model/User.php
namespace appmodel;
use thinkModel;
use thinkmodelconcernSoftDelete;
class User extends Model
{
use SoftDelete;
protected $name = 'user';
protected $autoWriteTimestamp = true;
protected $deleteTime = 'delete_time';
// 密码自动加密
public function setPasswordAttr($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
// 状态获取器
public function getStatusTextAttr($value, $data)
{
$status = [0 => '禁用', 1 => '正常'];
return $status[$data['status']];
}
}
// app/model/Article.php
namespace appmodel;
use thinkModel;
class Article extends Model
{
protected $name = 'article';
protected $autoWriteTimestamp = true;
// 分类关联
public function category()
{
return $this->belongsTo(Category::class);
}
// 发布状态获取器
public function getStatusTextAttr($value, $data)
{
$status = [0 => '草稿', 1 => '已发布'];
return $status[$data['status']];
}
}
使用迁移和填充
// 创建迁移文件
php think migrate:create CreateCmsTables
// database/migrations/20231215000000_create_cms_tables.php
use thinkmigrationMigrator;
class CreateCmsTables extends Migrator
{
public function change()
{
$table = $this->table('user', ['comment' => '用户表', 'engine' => 'InnoDB']);
$table->addColumn('username', 'string', ['limit' => 50, 'default' => '', 'comment' => '用户名'])
->addColumn('password', 'string', ['limit' => 255, 'default' => '', 'comment' => '密码'])
->addTimestamps()
->create();
}
}
后台管理系统实现
后台登录实现
// app/admin/controller/Login.php
namespace appadmincontroller;
use thinkfacadeView;
use appmodelUser;
class Login
{
public function index()
{
return View::fetch();
}
public function check()
{
$username = input('post.username');
$password = input('post.password');
$captcha = input('post.captcha');
// 验证码验证
if (!captcha_check($captcha)) {
return json(['code' => 0, 'msg' => '验证码错误']);
}
// 用户验证
$user = User::where('username', $username)->find();
if (!$user || !password_verify($password, $user->password)) {
return json(['code' => 0, 'msg' => '用户名或密码错误']);
}
// 登录成功
session('admin_user', $user->toArray());
return json(['code' => 1, 'msg' => '登录成功', 'url' => url('admin/index')]);
}
}
后台中间件实现
// app/admin/middleware/Auth.php
namespace appadminmiddleware;
use thinkfacadeSession;
class Auth
{
public function handle($request, Closure $next)
{
// 排除登录页面
if (!preg_match('/admin/login/', $request->url())) {
if (!Session::has('admin_user')) {
return redirect('/admin/login');
}
}
return $next($request);
}
}
// 注册中间件 app/admin/middleware.php
return [
appadminmiddlewareAuth::class
];
文章管理CRUD实现
// app/admin/controller/Article.php
namespace appadmincontroller;
use thinkfacadeView;
use appmodelArticle;
use appmodelCategory;
class Article extends Base
{
public function index()
{
$page = input('page', 1);
$limit = input('limit', 15);
$keyword = input('keyword', '');
$map = [];
if ($keyword) {
$map[] = ['title', 'like', "%{$keyword}%"];
}
$list = Article::with('category')
->where($map)
->order('id', 'desc')
->paginate(['page' => $page, 'list_rows' => $limit]);
return View::fetch('index', [
'list' => $list,
'page' => $list->render(),
'keyword' => $keyword
]);
}
public function add()
{
if (request()->isPost()) {
$data = input('post.');
$data['publish_time'] = strtotime($data['publish_time']);
$result = Article::create($data);
if ($result) {
return json(['code' => 1, 'msg' => '添加成功', 'url' => url('admin/article/index')]);
} else {
return json(['code' => 0, 'msg' => '添加失败']);
}
}
$categories = Category::where('status', 1)->select();
return View::fetch('add', ['categories' => $categories]);
}
public function edit($id)
{
$article = Article::find($id);
if (!$article) {
$this->error('文章不存在');
}
if (request()->isPost()) {
$data = input('post.');
$data['publish_time'] = strtotime($data['publish_time']);
if ($article->save($data)) {
return json(['code' => 1, 'msg' => '更新成功', 'url' => url('admin/article/index')]);
} else {
return json(['code' => 0, 'msg' => '更新失败']);
}
}
$categories = Category::where('status', 1)->select();
return View::fetch('edit', [
'article' => $article,
'categories' => $categories
]);
}
public function delete($id)
{
$article = Article::find($id);
if (!$article) {
return json(['code' => 0, 'msg' => '文章不存在']);
}
if ($article->delete()) {
return json(['code' => 1, 'msg' => '删除成功']);
} else {
return json(['code' => 0, 'msg' => '删除失败']);
}
}
}
前端展示功能实现
首页控制器实现
// app/home/controller/Index.php
namespace apphomecontroller;
use thinkfacadeView;
use appmodelArticle;
use appmodelCategory;
class Index
{
public function index()
{
// 获取推荐文章
$featuredArticles = Article::where('status', 1)
->order('views', 'desc')
->limit(5)
->select();
// 获取最新文章
$latestArticles = Article::with('category')
->where('status', 1)
->order('publish_time', 'desc')
->paginate(10);
return View::fetch('index', [
'featuredArticles' => $featuredArticles,
'latestArticles' => $latestArticles,
'page' => $latestArticles->render()
]);
}
}
文章详情页实现
// app/home/controller/Article.php
namespace apphomecontroller;
use thinkfacadeView;
use appmodelArticle;
class Article
{
public function detail($id)
{
$article = Article::with('category')->find($id);
if (!$article || $article->status != 1) {
abort(404, '文章不存在');
}
// 增加浏览量
$article->views = ['inc', 1];
$article->save();
// 获取相关文章
$relatedArticles = Article::where('category_id', $article->category_id)
->where('id', '', $id)
->where('status', 1)
->order('publish_time', 'desc')
->limit(5)
->select();
return View::fetch('detail', [
'article' => $article,
'relatedArticles' => $relatedArticles
]);
}
}
分类页面实现
// app/home/controller/Category.php
namespace apphomecontroller;
use thinkfacadeView;
use appmodelArticle;
use appmodelCategory;
class Category
{
public function index($id)
{
$category = Category::find($id);
if (!$category) {
abort(404, '分类不存在');
}
// 获取分类下的文章
$articles = Article::where('category_id', $id)
->where('status', 1)
->order('publish_time', 'desc')
->paginate(15);
return View::fetch('index', [
'category' => $category,
'articles' => $articles,
'page' => $articles->render()
]);
}
}
安全防护与性能优化
安全措施实现
// 表单令牌防护
// 在模板中添加
<input type="hidden" name="__token__" value="{:token()}">
// 在控制器中验证
$check = $request->checkToken('__token__');
if (false === $check) {
throw new ValidateException('令牌验证错误');
}
// XSS过滤
public function add()
{
$data = input('post.');
// 过滤HTML标签
$data['content'] = clean($data['content']);
// 或者使用HTMLPurifier等专业库
}
// SQL注入防护
// 使用查询构造器自动参数绑定
$list = Article::where('title', 'like', "%{$keyword}%")->select();
// 或者使用预处理
$list = Db::query('SELECT * FROM cms_article WHERE title LIKE ?', ["%{$keyword}%"]);
性能优化策略
// 使用缓存
public function index()
{
$cacheKey = 'home_index_data';
$data = cache($cacheKey);
if (!$data) {
$data = [
'featuredArticles' => Article::where('status', 1)
->order('views', 'desc')
->limit(5)
->select(),
'latestArticles' => Article::with('category')
->where('status', 1)
->order('publish_time', 'desc')
->paginate(10)
];
// 缓存10分钟
cache($cacheKey, $data, 600);
}
return View::fetch('index', $data);
}
// 数据库索引优化
// 为常用查询字段添加索引
ALTER TABLE `cms_article` ADD INDEX `idx_category_status` (`category_id`, `status`);
ALTER TABLE `cms_article` ADD INDEX `idx_publish_time` (`publish_time`);
// 使用延迟关联优化分页
public function index()
{
$page = input('page', 1);
$limit = input('limit', 15);
// 先获取主键ID
$ids = Article::where('status', 1)
->order('publish_time', 'desc')
->page($page, $limit)
->column('id');
// 再获取完整数据
$list = Article::with('category')
->whereIn('id', $ids)
->order('publish_time', 'desc')
->select();
}
项目部署与总结
生产环境部署
# 关闭调试模式
// config/app.php
'app_debug' => false,
'app_trace' => false,
# 优化路由缓存
php think optimize:route
# 生成配置缓存
php think optimize:config
# 类库映射优化
php think optimize:autoload
# 使用Composer优化自动加载
composer dump-autoload --optimize
# 设置目录权限
chmod -R 755 public/uploads
chmod -R 755 runtime
Nginx配置示例
server {
listen 80;
server_name example.com;
root /path/to/tp-cms/public;
index index.php index.html;
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /.ht {
deny all;
}
}
项目总结
通过本教程,我们完成了一个基于ThinkPHP 6.0的企业级内容管理系统,涵盖了:
- ThinkPHP 6.0框架的核心特性使用
- 多应用模式的配置与开发
- 数据库设计与模型关系处理
- 完整的后台管理系统实现
- 响应式前端展示页面
- 安全防护与性能优化策略
- 生产环境部署配置
这个CMS系统可以作为企业网站、博客系统、新闻发布系统的基础框架,根据实际需求进行功能扩展和定制开发。
进一步学习建议
- 学习使用ThinkPHP的门面模式和依赖注入
- 掌握更多中间件的应用场景
- 深入了解事件系统和钩子行为
- 学习API接口开发与身份认证
- 掌握队列处理和定时任务
- 学习Docker容器化部署

