ThinkPHP 8 多应用模式完整教程
从零开始构建 ThinkPHP 8 多应用架构的完整指南
📖 目录
- [1. 什么是多应用模式](#1. 什么是多应用模式)
- [2. 多应用模式安装与配置](#2. 多应用模式安装与配置)
- [3. 目录结构规划](#3. 目录结构规划)
- [4. 路由配置](#4. 路由配置)
- [5. 服务层架构](#5. 服务层架构)
- [6. 模型层共享](#6. 模型层共享)
- [7. 缓存配置](#7. 缓存配置)
- [8. 中间件配置](#8. 中间件配置)
- [9. 异常处理](#9. 异常处理)
- [10. 数据库配置](#10. 数据库配置)
- [11. 视图配置](#11. 视图配置)
- [12. 公共类库](#12. 公共类库)
- [13. 事件系统](#13. 事件系统)
- [14. 验证器共享](#14. 验证器共享)
- [15. 跨应用调用](#15. 跨应用调用)
- [16. 最佳实践](#16. 最佳实践)
- [17. 常见问题](#17. 常见问题)
1. 什么是多应用模式
1.1 概念说明
多应用模式是指在一个 ThinkPHP 项目中,同时运行多个独立的应用模块,每个应用有自己的控制器、视图、中间件等,但共享模型、服务、配置等资源。
1.2 适用场景
✅ 适合使用多应用模式:
- 前台 + 后台 + API 三端分离
- 多个子系统(如:商城、论坛、博客)
- 不同业务模块需要独立部署
- 需要不同的访问入口和权限控制
❌ 不适合使用多应用模式:
- 简单的单一应用
- 只有一个管理后台
- 业务逻辑简单,不需要模块化
1.3 多应用 vs 单应用
| 特性 | 单应用模式 | 多应用模式 |
|---|---|---|
| 目录结构 | app/controller/ |
app/admin/controller/ app/api/controller/ |
| 路由 | /user/login |
/admin/user/login /api/user/login |
| 适用场景 | 小型项目 | 中大型项目 |
| 复杂度 | 简单 | 中等 |
| 维护性 | 一般 | 好 |
2. 多应用模式安装与配置
2.1 安装多应用扩展
ThinkPHP 8.0 默认是单应用模式,需要安装多应用扩展:
bash
# 1. 创建 ThinkPHP 8 项目
composer create-project topthink/think tp8_multi_app
# 2. 进入项目目录
cd tp8_multi_app
# 3. 安装多应用扩展
composer require topthink/think-multi-app
2.2 验证安装
安装成功后,会自动生成多应用支持文件:
bash
# 查看是否安装成功
composer show topthink/think-multi-app
2.3 启用多应用模式
多应用扩展安装后会自动启用,无需额外配置。
3. 目录结构规划
3.1 标准目录结构
project/
├── app/ # 应用目录
│ ├── common/ # 公共模块 ⭐
│ │ ├── model/ # 共享模型
│ │ │ ├── User.php
│ │ │ ├── Article.php
│ │ │ └── Category.php
│ │ ├── service/ # 共享服务
│ │ │ ├── UserService.php
│ │ │ ├── SmsService.php
│ │ │ └── UploadService.php
│ │ ├── library/ # 共享类库
│ │ │ ├── Auth.php
│ │ │ └── Tree.php
│ │ ├── validate/ # 共享验证器
│ │ │ └── UserValidate.php
│ │ └── exception/ # 共享异常类
│ │ └── ApiException.php
│ │
│ ├── admin/ # 后台应用 ⭐
│ │ ├── controller/ # 控制器
│ │ │ ├── Index.php
│ │ │ ├── User.php
│ │ │ └── Article.php
│ │ ├── service/ # 后台专属服务
│ │ │ ├── AdminService.php
│ │ │ └── MenuService.php
│ │ ├── middleware/ # 后台中间件
│ │ │ └── AdminAuth.php
│ │ ├── view/ # 后台视图
│ │ │ ├── index/
│ │ │ └── user/
│ │ ├── config/ # 后台配置(可选)
│ │ │ └── app.php
│ │ ├── route/ # 后台路由
│ │ │ └── app.php
│ │ ├── event.php # 后台事件定义
│ │ ├── middleware.php # 后台中间件定义
│ │ └── provider.php # 后台服务提供者
│ │
│ ├── api/ # API 应用 ⭐
│ │ ├── controller/
│ │ │ ├── Index.php
│ │ │ ├── User.php
│ │ │ └── Article.php
│ │ ├── service/ # API 专属服务
│ │ │ └── TokenService.php
│ │ ├── middleware/ # API 中间件
│ │ │ ├── JwtAuth.php
│ │ │ └── Cors.php
│ │ ├── route/ # API 路由
│ │ │ └── app.php
│ │ ├── event.php
│ │ ├── middleware.php
│ │ └── provider.php
│ │
│ ├── index/ # 前台应用 ⭐
│ │ ├── controller/
│ │ │ ├── Index.php
│ │ │ └── Article.php
│ │ ├── service/ # 前台专属服务
│ │ │ ├── CartService.php
│ │ │ └── OrderService.php
│ │ ├── middleware/
│ │ │ └── UserAuth.php
│ │ ├── view/ # 前台视图
│ │ ├── route/
│ │ │ └── app.php
│ │ ├── event.php
│ │ ├── middleware.php
│ │ └── provider.php
│ │
│ ├── event.php # 全局事件定义
│ ├── middleware.php # 全局中间件定义
│ ├── provider.php # 全局服务提供者
│ ├── ExceptionHandle.php # 全局异常处理
│ └── Request.php # 全局请求类
│
├── config/ # 全局配置目录
│ ├── app.php # 应用配置
│ ├── cache.php # 缓存配置
│ ├── database.php # 数据库配置
│ ├── route.php # 路由配置
│ └── view.php # 视图配置
│
├── public/ # 公共资源目录
│ ├── index.php # 入口文件
│ ├── static/ # 静态资源
│ │ ├── admin/ # 后台静态资源
│ │ ├── api/ # API 文档等
│ │ └── index/ # 前台静态资源
│ └── storage/ # 上传文件
│
├── runtime/ # 运行时目录
│ ├── admin/ # 后台运行时
│ ├── api/ # API 运行时
│ └── index/ # 前台运行时
│
├── route/ # 全局路由目录
│ └── app.php
│
├── .env # 环境变量
├── composer.json
└── think # 命令行工具
3.2 创建应用目录
方式 1:手动创建
bash
# 创建后台应用
mkdir -p app/admin/controller
mkdir -p app/admin/service
mkdir -p app/admin/middleware
mkdir -p app/admin/view
# 创建 API 应用
mkdir -p app/api/controller
mkdir -p app/api/service
mkdir -p app/api/middleware
# 创建前台应用
mkdir -p app/index/controller
mkdir -p app/index/service
mkdir -p app/index/middleware
mkdir -p app/index/view
# 创建公共模块
mkdir -p app/common/model
mkdir -p app/common/service
mkdir -p app/common/library
mkdir -p app/common/validate
方式 2:使用命令行(推荐)
bash
# ThinkPHP 8 提供了命令行工具
php think build
3.3 目录说明
| 目录 | 说明 | 共享性 |
|---|---|---|
app/common/ |
公共模块,所有应用共享 | ✅ 共享 |
app/admin/ |
后台应用,独立的业务逻辑 | ❌ 独立 |
app/api/ |
API 应用,提供接口服务 | ❌ 独立 |
app/index/ |
前台应用,面向用户 | ❌ 独立 |
config/ |
全局配置,所有应用共享 | ✅ 共享 |
route/ |
全局路由配置 | ✅ 共享 |
4. 路由配置
4.1 多应用路由规则
多应用模式下,URL 访问规则为:
http://domain.com/应用名/控制器/操作/参数
示例:
http://example.com/admin/user/index # 后台用户列表
http://example.com/api/user/login # API 用户登录
http://example.com/index/article/detail # 前台文章详情
4.2 全局路由配置
文件:config/route.php
php
<?php
return [
// 是否强制使用路由
'url_route_must' => false,
// 路由使用完整匹配
'route_complete_match' => false,
// 是否开启路由解析缓存
'route_check_cache' => false,
// 路由缓存连接名称
'route_cache_option' => [],
// 默认应用
'default_app' => 'index',
// 应用映射(可选)
'app_map' => [
// 'admin' => 'backend', // 将 admin 映射为 backend
],
// 域名绑定(可选)
'domain_bind' => [
// 'admin.example.com' => 'admin',
// 'api.example.com' => 'api',
],
// 禁止访问的应用
'deny_app_list' => ['common'],
];
4.3 应用独立路由
后台路由:app/admin/route/app.php
php
<?php
use think\facade\Route;
// 后台首页
Route::get('/', 'index/index');
// 后台登录
Route::get('login', 'login/index');
Route::post('login', 'login/check');
Route::get('logout', 'login/logout');
// 用户管理(资源路由)
Route::resource('user', 'user');
// 文章管理
Route::group('article', function () {
Route::get('/', 'article/index');
Route::get('create', 'article/create');
Route::post('save', 'article/save');
Route::get('edit/<id>', 'article/edit');
Route::put('update/<id>', 'article/update');
Route::delete('delete/<id>', 'article/delete');
});
// 需要登录的路由组
Route::group(function () {
Route::get('dashboard', 'index/dashboard');
Route::get('profile', 'user/profile');
})->middleware(\app\admin\middleware\AdminAuth::class);
API 路由:app/api/route/app.php
php
<?php
use think\facade\Route;
// API 版本 1
Route::group('v1', function () {
// 用户相关
Route::post('login', 'user/login');
Route::post('register', 'user/register');
// 需要认证的接口
Route::group(function () {
Route::get('user/info', 'user/info');
Route::put('user/update', 'user/update');
Route::get('article/list', 'article/list');
})->middleware(\app\api\middleware\JwtAuth::class);
});
// API 版本 2
Route::group('v2', function () {
Route::post('login', 'v2.user/login');
Route::get('article/list', 'v2.article/list');
});
// 跨域中间件(所有 API 路由)
Route::middleware(\app\api\middleware\Cors::class);
前台路由:app/index/route/app.php
php
<?php
use think\facade\Route;
// 首页
Route::get('/', 'index/index');
// 文章
Route::get('article/<id>', 'article/detail');
Route::get('article/category/<cate_id>', 'article/category');
// 用户中心(需要登录)
Route::group('user', function () {
Route::get('profile', 'user/profile');
Route::get('orders', 'order/list');
Route::get('order/<id>', 'order/detail');
})->middleware(\app\index\middleware\UserAuth::class);
4.4 域名绑定
如果不同应用使用不同域名,可以配置域名绑定:
文件:config/route.php
php
<?php
return [
// 域名绑定
'domain_bind' => [
'admin.example.com' => 'admin', // 后台域名
'api.example.com' => 'api', // API 域名
'www.example.com' => 'index', // 前台域名
],
];
访问示例:
http://admin.example.com/user/index # 等同于 /admin/user/index
http://api.example.com/user/login # 等同于 /api/user/login
http://www.example.com/article/detail # 等同于 /index/article/detail
4.5 路由别名
文件:config/route.php
php
<?php
return [
// 应用映射
'app_map' => [
'backend' => 'admin', // 访问 /backend 实际访问 admin 应用
'mobile' => 'index', // 访问 /mobile 实际访问 index 应用
],
];
5. 服务层架构
5.1 服务层概念
服务层(Service Layer)是业务逻辑的核心,位于控制器和模型之间,负责处理复杂的业务逻辑。
架构层次:
Controller (控制器)
↓ 调用
Service (服务层)
↓ 调用
Model (模型层)
↓ 操作
Database (数据库)
5.2 共享服务(app/common/service/)
用户服务:app/common/service/UserService.php
php
<?php
declare(strict_types=1);
namespace app\common\service;
use app\common\model\User;
use think\facade\Cache;
/**
* 用户服务(所有应用共享)
*/
class UserService
{
/**
* 获取用户信息(带缓存)
*/
public function getUserInfo(int $userId): array
{
$cacheKey = 'user_info_' . $userId;
return Cache::remember($cacheKey, function() use ($userId) {
$user = User::find($userId);
return $user ? $user->toArray() : [];
}, 3600);
}
/**
* 更新用户信息
*/
public function updateUser(int $userId, array $data): bool
{
$user = User::find($userId);
if (!$user) {
throw new \Exception('用户不存在');
}
$result = $user->save($data);
// 清除缓存
Cache::delete('user_info_' . $userId);
return $result !== false;
}
/**
* 检查用户名是否存在
*/
public function checkUsername(string $username): bool
{
return User::where('username', $username)->count() > 0;
}
}
短信服务:app/common/service/SmsService.php
php
<?php
declare(strict_types=1);
namespace app\common\service;
use think\facade\Cache;
/**
* 短信服务(所有应用共享)
*/
class SmsService
{
/**
* 发送验证码
*/
public function sendCode(string $mobile, string $scene = 'login'): bool
{
// 生成验证码
$code = $this->generateCode();
// 发送短信(这里使用阿里云短信服务示例)
$result = $this->sendSms($mobile, $code);
if ($result) {
// 缓存验证码(5分钟有效)
$cacheKey = 'sms_code_' . $scene . '_' . $mobile;
Cache::set($cacheKey, $code, 300);
return true;
}
return false;
}
/**
* 验证验证码
*/
public function verifyCode(string $mobile, string $code, string $scene = 'login'): bool
{
$cacheKey = 'sms_code_' . $scene . '_' . $mobile;
$cachedCode = Cache::get($cacheKey);
if ($cachedCode && $cachedCode === $code) {
// 验证成功后删除缓存
Cache::delete($cacheKey);
return true;
}
return false;
}
/**
* 生成验证码
*/
private function generateCode(int $length = 6): string
{
return str_pad((string)mt_rand(0, pow(10, $length) - 1), $length, '0', STR_PAD_LEFT);
}
/**
* 发送短信(示例)
*/
private function sendSms(string $mobile, string $code): bool
{
// 这里接入实际的短信服务商 API
// 例如:阿里云、腾讯云等
return true;
}
}
5.3 应用专属服务
后台管理员服务:app/admin/service/AdminService.php
php
<?php
declare(strict_types=1);
namespace app\admin\service;
use app\common\model\Admin;
use think\facade\Session;
class AdminService
{
public function login(string $username, string $password): array
{
$admin = Admin::where('username', $username)->find();
if (!$admin || !password_verify($password, $admin->password)) {
throw new \Exception('用户名或密码错误');
}
Session::set('admin_id', $admin->id);
Session::set('admin_name', $admin->username);
return $admin->toArray();
}
}
API Token 服务:app/api/service/TokenService.php
php
<?php
declare(strict_types=1);
namespace app\api\service;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class TokenService
{
private string $key = 'your-secret-key';
public function createToken(array $data): string
{
$payload = [
'iat' => time(),
'exp' => time() + 7200,
'data' => $data
];
return JWT::encode($payload, $this->key, 'HS256');
}
public function verifyToken(string $token): array
{
try {
$decoded = JWT::decode($token, new Key($this->key, 'HS256'));
return (array) $decoded->data;
} catch (\Exception $e) {
throw new \Exception('Token 无效', 401);
}
}
}
6. 模型层共享
6.1 模型放置位置
所有模型统一放在 app/common/model/ 目录,供所有应用共享。
6.2 用户模型示例
文件:app/common/model/User.php
php
<?php
declare(strict_types=1);
namespace app\common\model;
use think\Model;
use think\model\concern\SoftDelete;
class User extends Model
{
use SoftDelete;
protected $name = 'user';
protected string $deleteTime = 'delete_time';
protected $autoWriteTimestamp = true;
protected $type = [
'id' => 'integer',
'status' => 'integer',
];
protected $hidden = ['password', 'delete_time'];
public function setPasswordAttr($value): string
{
return password_hash($value, PASSWORD_DEFAULT);
}
}
6.3 文章模型示例
文件:app/common/model/Article.php
php
<?php
declare(strict_types=1);
namespace app\common\model;
use think\Model;
class Article extends Model
{
protected $name = 'article';
protected $autoWriteTimestamp = true;
// 关联分类
public function category()
{
return $this->belongsTo(Category::class, 'cate_id');
}
// 关联作者
public function author()
{
return $this->belongsTo(User::class, 'user_id');
}
}
7. 缓存配置
7.1 统一缓存配置
文件:config/cache.php
php
<?php
return [
'default' => 'redis',
'stores' => [
'file' => [
'type' => 'file',
'path' => runtime_path() . 'cache/',
'expire' => 0,
],
'redis' => [
'type' => 'redis',
'host' => env('redis.host', '127.0.0.1'),
'port' => env('redis.port', 6379),
'password' => env('redis.password', ''),
'select' => 0,
'prefix' => 'myapp:',
],
],
];
7.2 缓存服务封装
文件:app/common/service/CacheService.php
php
<?php
declare(strict_types=1);
namespace app\common\service;
use think\facade\Cache;
class CacheService
{
// 设置应用缓存(自动添加应用前缀)
public static function setAppCache(string $key, $value, int $expire = 0): bool
{
$appName = app('http')->getName();
$cacheKey = $appName . ':' . $key;
return Cache::set($cacheKey, $value, $expire);
}
// 获取应用缓存
public static function getAppCache(string $key)
{
$appName = app('http')->getName();
$cacheKey = $appName . ':' . $key;
return Cache::get($cacheKey);
}
// 设置共享缓存
public static function setCommonCache(string $key, $value, int $expire = 0): bool
{
return Cache::set('common:' . $key, $value, $expire);
}
// 获取共享缓存
public static function getCommonCache(string $key)
{
return Cache::get('common:' . $key);
}
}
8. 中间件配置
8.1 全局中间件
文件:app/middleware.php
php
<?php
// 全局中间件定义(所有应用都会执行)
return [
// 跨域请求支持
\think\middleware\AllowCrossDomain::class,
// Session 初始化
\think\middleware\SessionInit::class,
];
8.2 应用中间件
后台中间件:app/admin/middleware.php
php
<?php
return [
// 后台认证中间件
\app\admin\middleware\AdminAuth::class,
];
API 中间件:app/api/middleware.php
php
<?php
return [
// JWT 认证中间件
\app\api\middleware\JwtAuth::class,
// 跨域中间件
\app\api\middleware\Cors::class,
];
8.3 后台认证中间件
文件:app/admin/middleware/AdminAuth.php
php
<?php
declare(strict_types=1);
namespace app\admin\middleware;
use think\facade\Session;
class AdminAuth
{
public function handle($request, \Closure $next)
{
// 白名单(不需要登录的页面)
$whitelist = ['login/index', 'login/check'];
$controller = $request->controller();
$action = $request->action();
$path = $controller . '/' . $action;
// 检查是否在白名单中
if (in_array($path, $whitelist)) {
return $next($request);
}
// 检查是否登录
if (!Session::has('admin_id')) {
return redirect('/admin/login');
}
return $next($request);
}
}
8.4 JWT 认证中间件
文件:app/api/middleware/JwtAuth.php
php
<?php
declare(strict_types=1);
namespace app\api\middleware;
use app\api\service\TokenService;
class JwtAuth
{
public function handle($request, \Closure $next)
{
$token = $request->header('Authorization');
if (empty($token)) {
return json(['code' => 401, 'msg' => '未登录']);
}
// 移除 "Bearer " 前缀
$token = str_replace('Bearer ', '', $token);
$tokenService = new TokenService();
try {
$userData = $tokenService->verifyToken($token);
$request->userData = $userData;
} catch (\Exception $e) {
return json(['code' => 401, 'msg' => $e->getMessage()]);
}
return $next($request);
}
}
8.5 跨域中间件
文件:app/api/middleware/Cors.php
php
<?php
declare(strict_types=1);
namespace app\api\middleware;
class Cors
{
public function handle($request, \Closure $next)
{
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Authorization, Content-Type');
// OPTIONS 请求直接返回
if ($request->method() == 'OPTIONS') {
return response('', 200);
}
return $next($request);
}
}
9. 异常处理
9.1 全局异常处理
文件:app/ExceptionHandle.php
php
<?php
declare(strict_types=1);
namespace app;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
class ExceptionHandle extends Handle
{
public function render($request, Throwable $e): Response
{
// 验证异常
if ($e instanceof ValidateException) {
return json(['code' => 1, 'msg' => $e->getError()]);
}
// 数据不存在异常
if ($e instanceof DataNotFoundException || $e instanceof ModelNotFoundException) {
return json(['code' => 404, 'msg' => '数据不存在']);
}
// HTTP 异常
if ($e instanceof HttpException) {
return json(['code' => $e->getStatusCode(), 'msg' => $e->getMessage()]);
}
// 其他异常
return json([
'code' => 500,
'msg' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
]);
}
}
9.2 自定义异常类
文件:app/common/exception/ApiException.php
php
<?php
declare(strict_types=1);
namespace app\common\exception;
use Exception;
class ApiException extends Exception
{
public function __construct(string $message = '', int $code = 400)
{
parent::__construct($message, $code);
}
}
9.3 使用自定义异常
php
<?php
namespace app\api\controller;
use app\common\exception\ApiException;
class User
{
public function info()
{
$user = User::find(1);
if (!$user) {
throw new ApiException('用户不存在', 404);
}
return json(['code' => 0, 'data' => $user]);
}
}
10. 数据库配置
10.1 统一数据库配置
文件:config/database.php
php
<?php
return [
'default' => env('database.driver', 'mysql'),
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => env('database.hostname', '127.0.0.1'),
'database' => env('database.database', 'test'),
'username' => env('database.username', 'root'),
'password' => env('database.password', ''),
'hostport' => env('database.hostport', '3306'),
'charset' => 'utf8mb4',
'prefix' => env('database.prefix', ''),
],
],
];
10.2 环境变量配置
文件:.env
env
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = myapp
USERNAME = root
PASSWORD = 123456
HOSTPORT = 3306
PREFIX = tp_
10.3 多数据库连接
如果不同应用需要连接不同数据库:
php
<?php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'main_db',
'username' => 'root',
'password' => '',
],
'mysql_admin' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'admin_db',
'username' => 'root',
'password' => '',
],
],
];
使用示例:
php
<?php
// 使用默认连接
$user = User::find(1);
// 使用指定连接
$admin = Admin::connect('mysql_admin')->find(1);
11. 视图配置
11.1 统一视图配置
文件:config/view.php
php
<?php
return [
'type' => 'Think',
'view_path' => '',
'view_suffix' => 'html',
'view_depr' => DIRECTORY_SEPARATOR,
'tpl_begin' => '{',
'tpl_end' => '}',
'taglib_begin' => '{',
'taglib_end' => '}',
];
11.2 应用视图目录
app/
├── admin/
│ └── view/
│ ├── index/
│ │ └── index.html
│ └── user/
│ ├── index.html
│ └── edit.html
│
└── index/
└── view/
├── index/
│ └── index.html
└── article/
└── detail.html
11.3 控制器中使用视图
后台控制器:app/admin/controller/Index.php
php
<?php
namespace app\admin\controller;
use app\BaseController;
class Index extends BaseController
{
public function index()
{
// 渲染视图:app/admin/view/index/index.html
return view('index', [
'title' => '后台首页',
'user' => session('admin_name'),
]);
}
}
11.4 视图模板示例
文件:app/admin/view/index/index.html
html
<!DOCTYPE html>
<html>
<head>
<title>{$title}</title>
</head>
<body>
<h1>欢迎,{$user}</h1>
<p>这是后台首页</p>
</body>
</html>
12. 公共类库
12.1 公共类库目录
app/common/library/
├── Auth.php # 权限认证类
├── Tree.php # 树形结构类
├── Upload.php # 上传类
└── Wechat.php # 微信类
12.2 权限认证类示例
文件:app/common/library/Auth.php
php
<?php
declare(strict_types=1);
namespace app\common\library;
use think\facade\Session;
class Auth
{
/**
* 检查权限
*/
public static function check(string $rule): bool
{
$adminId = Session::get('admin_id');
if (!$adminId) {
return false;
}
// 超级管理员
if ($adminId == 1) {
return true;
}
// 检查权限逻辑
// ...
return true;
}
}
12.3 树形结构类示例
文件:app/common/library/Tree.php
php
<?php
declare(strict_types=1);
namespace app\common\library;
class Tree
{
/**
* 生成树形结构
*/
public static function toTree(array $list, int $pid = 0, string $pidField = 'pid'): array
{
$tree = [];
foreach ($list as $item) {
if ($item[$pidField] == $pid) {
$children = self::toTree($list, $item['id'], $pidField);
if ($children) {
$item['children'] = $children;
}
$tree[] = $item;
}
}
return $tree;
}
}
13. 事件系统
13.1 全局事件定义
文件:app/event.php
php
<?php
return [
'bind' => [],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogWrite' => [],
],
'subscribe' => [],
];
13.2 应用事件定义
后台事件:app/admin/event.php
php
<?php
return [
'bind' => [],
'listen' => [
'AdminLogin' => [
\app\admin\listener\AdminLoginListener::class,
],
],
'subscribe' => [],
];
13.3 事件类
文件:app/admin/event/AdminLogin.php
php
<?php
declare(strict_types=1);
namespace app\admin\event;
class AdminLogin
{
public $admin;
public function __construct($admin)
{
$this->admin = $admin;
}
}
13.4 监听器类
文件:app/admin/listener/AdminLoginListener.php
php
<?php
declare(strict_types=1);
namespace app\admin\listener;
use app\admin\event\AdminLogin;
class AdminLoginListener
{
public function handle(AdminLogin $event)
{
// 记录登录日志
$admin = $event->admin;
// 写入日志
// ...
}
}
13.5 触发事件
php
<?php
namespace app\admin\controller;
use app\admin\event\AdminLogin;
class Login
{
public function check()
{
// 登录逻辑
$admin = ['id' => 1, 'username' => 'admin'];
// 触发事件
event(new AdminLogin($admin));
return json(['code' => 0, 'msg' => '登录成功']);
}
}
14. 验证器共享
14.1 验证器目录
app/common/validate/
├── UserValidate.php
├── ArticleValidate.php
└── LoginValidate.php
14.2 用户验证器
文件:app/common/validate/UserValidate.php
php
<?php
declare(strict_types=1);
namespace app\common\validate;
use think\Validate;
class UserValidate extends Validate
{
protected $rule = [
'username' => 'require|length:3,20|alphaNum',
'password' => 'require|length:6,20',
'email' => 'require|email',
'mobile' => 'require|mobile',
];
protected $message = [
'username.require' => '用户名不能为空',
'username.length' => '用户名长度为3-20个字符',
'username.alphaNum' => '用户名只能是字母和数字',
'password.require' => '密码不能为空',
'password.length' => '密码长度为6-20个字符',
'email.require' => '邮箱不能为空',
'email.email' => '邮箱格式不正确',
'mobile.require' => '手机号不能为空',
'mobile.mobile' => '手机号格式不正确',
];
protected $scene = [
'login' => ['username', 'password'],
'register' => ['username', 'password', 'email', 'mobile'],
'edit' => ['email', 'mobile'],
];
}
14.3 在控制器中使用验证器
后台控制器:app/admin/controller/User.php
php
<?php
namespace app\admin\controller;
use app\common\validate\UserValidate;
use think\Request;
class User
{
public function save(Request $request)
{
$data = $request->post();
// 验证数据
$validate = new UserValidate();
if (!$validate->scene('register')->check($data)) {
return json(['code' => 1, 'msg' => $validate->getError()]);
}
// 保存数据
// ...
return json(['code' => 0, 'msg' => '保存成功']);
}
}
API 控制器:app/api/controller/User.php
php
<?php
namespace app\api\controller;
use app\common\validate\UserValidate;
use think\Request;
class User
{
public function register(Request $request)
{
$data = $request->post();
// 使用相同的验证器
$validate = new UserValidate();
if (!$validate->scene('register')->check($data)) {
return json(['code' => 1, 'msg' => $validate->getError()]);
}
// 注册逻辑
// ...
return json(['code' => 0, 'msg' => '注册成功']);
}
}
15. 跨应用调用
15.1 跨应用调用服务
场景:API 应用需要调用后台应用的服务
php
<?php
namespace app\api\controller;
use app\admin\service\AdminService; // 调用后台服务
class Admin
{
public function login()
{
// 可以直接调用其他应用的服务
$adminService = new AdminService();
$result = $adminService->login('admin', '123456');
return json(['code' => 0, 'data' => $result]);
}
}
15.2 跨应用调用模型
所有应用共享模型,无需跨应用调用
php
<?php
// 后台应用
namespace app\admin\controller;
use app\common\model\User;
class User
{
public function index()
{
$list = User::select();
return json(['code' => 0, 'data' => $list]);
}
}
php
<?php
// API 应用
namespace app\api\controller;
use app\common\model\User; // 使用相同的模型
class User
{
public function list()
{
$list = User::select();
return json(['code' => 0, 'data' => $list]);
}
}
15.3 注意事项
⚠️ 跨应用调用要谨慎:
- 避免循环依赖
- 如果多个应用都需要,考虑将服务移到
common - 注意命名空间的正确引用
16. 最佳实践
16.1 目录组织原则
✅ 推荐做法:
1. 共享资源放在 common
- 模型(Model)
- 通用服务(Service)
- 公共类库(Library)
- 验证器(Validate)
2. 应用专属资源放在各应用目录
- 控制器(Controller)
- 应用专属服务(Service)
- 中间件(Middleware)
- 视图(View)
3. 配置文件统一放在 config
- 数据库配置
- 缓存配置
- 路由配置
16.2 命名规范
控制器命名:
php
// ✅ 正确
app/admin/controller/User.php
app/api/controller/User.php
// ❌ 错误
app/admin/controller/user.php
app/api/controller/UserController.php
服务命名:
php
// ✅ 正确
app/common/service/UserService.php
app/admin/service/AdminService.php
// ❌ 错误
app/common/service/User.php
app/admin/service/Admin.php
16.3 代码复用
✅ 推荐:使用服务层复用业务逻辑
php
<?php
// 共享服务
namespace app\common\service;
class UserService
{
public function getUserInfo(int $userId): array
{
// 业务逻辑
return [];
}
}
// 后台控制器
namespace app\admin\controller;
class User
{
public function info()
{
$service = new \app\common\service\UserService();
return json($service->getUserInfo(1));
}
}
// API 控制器
namespace app\api\controller;
class User
{
public function info()
{
$service = new \app\common\service\UserService();
return json($service->getUserInfo(1));
}
}
16.4 依赖注入
✅ 推荐:使用依赖注入
php
<?php
namespace app\admin\controller;
use app\common\service\UserService;
class User
{
protected UserService $userService;
// 构造函数注入
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function info()
{
return json($this->userService->getUserInfo(1));
}
}
16.5 缓存策略
✅ 推荐:使用 Redis + 缓存前缀
php
<?php
// 共享缓存(所有应用可访问)
Cache::set('common:user_info_1', $data);
// 应用专属缓存(带应用前缀)
$appName = app('http')->getName();
Cache::set($appName . ':user_list', $data);
16.6 路由规划
✅ 推荐:使用资源路由
php
<?php
// app/admin/route/app.php
use think\facade\Route;
// 资源路由
Route::resource('user', 'user');
// 等同于:
// GET /user -> user/index
// GET /user/create -> user/create
// POST /user -> user/save
// GET /user/:id -> user/read
// GET /user/:id/edit -> user/edit
// PUT /user/:id -> user/update
// DELETE /user/:id -> user/delete
17. 常见问题
17.1 如何访问多应用?
问题: 安装多应用扩展后,如何访问不同的应用?
解决方案:
# 访问后台应用
http://example.com/admin/user/index
# 访问 API 应用
http://example.com/api/user/login
# 访问前台应用
http://example.com/index/article/detail
17.2 如何设置默认应用?
问题: 访问根目录时,默认进入哪个应用?
解决方案:
文件:config/route.php
php
<?php
return [
// 设置默认应用为 index
'default_app' => 'index',
];
访问 http://example.com/ 会自动进入 index 应用。
17.3 如何禁止访问某个应用?
问题: 不想让用户直接访问 common 应用
解决方案:
文件:config/route.php
php
<?php
return [
// 禁止访问的应用列表
'deny_app_list' => ['common'],
];
17.4 多应用如何共享 Session?
问题: 后台登录后,API 应用无法获取 Session
解决方案:
Session 默认是共享的,确保以下配置:
文件:config/session.php
php
<?php
return [
'type' => 'file',
'prefix' => 'think', // 统一前缀
'expire' => 3600,
];
17.5 如何实现应用间数据隔离?
问题: 不同应用需要使用不同的数据表前缀
解决方案:
方式 1:使用不同的数据库连接
php
<?php
// config/database.php
return [
'connections' => [
'mysql' => [
'prefix' => 'admin_',
],
'mysql_api' => [
'prefix' => 'api_',
],
],
];
// 使用
Admin::connect('mysql')->select(); // 使用 admin_ 前缀
User::connect('mysql_api')->select(); // 使用 api_ 前缀
方式 2:在模型中指定前缀
php
<?php
namespace app\common\model;
class Admin extends Model
{
protected $connection = 'mysql';
protected $table = 'admin_user'; // 完整表名
}
17.6 如何实现应用独立配置?
问题: 不同应用需要不同的配置
解决方案:
创建应用配置文件:app/admin/config/app.php
php
<?php
return [
'app_name' => '后台管理系统',
'page_size' => 20,
];
使用应用配置:
php
<?php
// 获取应用配置
$appName = config('app.app_name');
// 获取全局配置
$debug = config('app.debug');
17.7 如何实现域名绑定?
问题: 不同应用使用不同域名访问
解决方案:
文件:config/route.php
php
<?php
return [
'domain_bind' => [
'admin.example.com' => 'admin',
'api.example.com' => 'api',
'www.example.com' => 'index',
],
];
Nginx 配置:
nginx
server {
listen 80;
server_name admin.example.com api.example.com www.example.com;
root /path/to/project/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
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;
}
}
17.8 如何调试多应用?
问题: 如何查看当前是哪个应用?
解决方案:
php
<?php
// 获取当前应用名称
$appName = app('http')->getName();
echo $appName; // 输出:admin、api、index
// 获取当前控制器
$controller = request()->controller();
// 获取当前操作
$action = request()->action();
17.9 多应用如何部署?
问题: 生产环境如何部署多应用?
解决方案:
bash
# 1. 关闭调试模式
# .env
APP_DEBUG = false
# 2. 优化自动加载
composer install --optimize-autoloader --no-dev
# 3. 缓存配置
php think optimize:config
# 4. 缓存路由
php think optimize:route
# 5. 设置目录权限
chmod -R 755 /path/to/project
chmod -R 777 runtime
chmod -R 777 public/storage
17.10 如何创建新应用?
问题: 如何快速创建一个新应用?
解决方案:
bash
# 1. 创建应用目录
mkdir -p app/shop/controller
mkdir -p app/shop/service
mkdir -p app/shop/middleware
mkdir -p app/shop/view
# 2. 创建控制器
# app/shop/controller/Index.php
<?php
namespace app\shop\controller;
class Index
{
public function index()
{
return 'Shop Application';
}
}
# 3. 访问新应用
http://example.com/shop/index/index
18. 完整实战案例
18.1 项目需求
构建一个包含以下功能的多应用系统:
- 后台应用(admin):管理员登录、用户管理、文章管理
- API 应用(api):提供 RESTful API 接口
- 前台应用(index):用户浏览文章
18.2 项目结构
project/
├── app/
│ ├── common/
│ │ ├── model/
│ │ │ ├── User.php
│ │ │ └── Article.php
│ │ └── service/
│ │ └── UserService.php
│ ├── admin/
│ │ ├── controller/
│ │ │ ├── Login.php
│ │ │ ├── User.php
│ │ │ └── Article.php
│ │ ├── middleware/
│ │ │ └── AdminAuth.php
│ │ └── route/
│ │ └── app.php
│ ├── api/
│ │ ├── controller/
│ │ │ ├── User.php
│ │ │ └── Article.php
│ │ ├── middleware/
│ │ │ └── JwtAuth.php
│ │ └── route/
│ │ └── app.php
│ └── index/
│ ├── controller/
│ │ ├── Index.php
│ │ └── Article.php
│ └── route/
│ └── app.php
└── config/
├── app.php
├── database.php
└── cache.php
18.3 后台登录实现
控制器:app/admin/controller/Login.php
php
<?php
declare(strict_types=1);
namespace app\admin\controller;
use app\admin\service\AdminService;
use think\Request;
class Login
{
protected AdminService $adminService;
public function __construct(AdminService $adminService)
{
$this->adminService = $adminService;
}
// 登录页面
public function index()
{
return view('login/index');
}
// 登录验证
public function check(Request $request)
{
$username = $request->post('username');
$password = $request->post('password');
try {
$admin = $this->adminService->login($username, $password);
return json(['code' => 0, 'msg' => '登录成功', 'data' => $admin]);
} catch (\Exception $e) {
return json(['code' => 1, 'msg' => $e->getMessage()]);
}
}
// 退出登录
public function logout()
{
$this->adminService->logout();
return redirect('/admin/login');
}
}
18.4 API 接口实现
控制器:app/api/controller/Article.php
php
<?php
declare(strict_types=1);
namespace app\api\controller;
use app\common\model\Article;
use think\Request;
class Article
{
// 文章列表
public function list(Request $request)
{
$page = $request->param('page', 1, 'intval');
$limit = $request->param('limit', 10, 'intval');
$list = Article::where('status', 1)
->with('category')
->order('id', 'desc')
->page($page, $limit)
->select();
$total = Article::where('status', 1)->count();
return json([
'code' => 0,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
'limit' => $limit,
]
]);
}
// 文章详情
public function detail(Request $request)
{
$id = $request->param('id', 0, 'intval');
$article = Article::with('category')->find($id);
if (!$article) {
return json(['code' => 404, 'msg' => '文章不存在']);
}
return json(['code' => 0, 'msg' => 'success', 'data' => $article]);
}
}
18.5 前台展示实现
控制器:app/index/controller/Article.php
php
<?php
declare(strict_types=1);
namespace app\index\controller;
use app\common\model\Article;
use think\Request;
class Article
{
// 文章详情页
public function detail(Request $request)
{
$id = $request->param('id', 0, 'intval');
$article = Article::with('category')->find($id);
if (!$article) {
abort(404, '文章不存在');
}
// 增加浏览量
$article->inc('views')->update();
return view('article/detail', [
'article' => $article,
]);
}
}
19. 性能优化建议
19.1 缓存优化
php
<?php
// 1. 使用 Redis 缓存
// config/cache.php
return [
'default' => 'redis',
];
// 2. 缓存查询结果
$list = Article::cache('article_list', 3600)->select();
// 3. 使用标签缓存
Cache::tag('article')->set('list', $data);
Cache::tag('article')->clear(); // 清除所有文章相关缓存
19.2 数据库优化
php
<?php
// 1. 只查询需要的字段
$list = Article::field('id,title,create_time')->select();
// 2. 使用预加载避免 N+1 问题
$list = Article::with('category')->select();
// 3. 使用索引
// 确保 where 条件的字段有索引
19.3 路由优化
bash
# 缓存路由
php think optimize:route
# 缓存配置
php think optimize:config
20. 安全建议
20.1 SQL 注入防护
php
<?php
// ✅ 正确:使用参数绑定
$list = Db::name('user')->where('id', $id)->select();
// ❌ 错误:直接拼接 SQL
$sql = "SELECT * FROM user WHERE id = " . $id;
20.2 XSS 防护
php
<?php
// 模板中自动转义
{$content} // 自动转义
// 不转义(危险)
{$content|raw}
20.3 CSRF 防护
php
<?php
// 开启 CSRF 验证
// config/middleware.php
return [
\think\middleware\FormTokenCheck::class,
];
21. 总结
21.1 多应用模式核心要点
| 要点 | 说明 |
|---|---|
| 目录结构 | 清晰的应用划分,common 存放共享资源 |
| 路由配置 | 每个应用独立路由,支持域名绑定 |
| 服务层 | 共享服务放 common,专属服务放各应用 |
| 模型层 | 统一放在 common/model,所有应用共享 |
| 缓存配置 | 使用 Redis 统一缓存,支持应用前缀 |
| 中间件 | 全局中间件 + 应用中间件 |
| 事件系统 | 全局事件 + 应用事件 |
21.2 开发流程
1. 安装多应用扩展
↓
2. 规划目录结构
↓
3. 创建应用目录
↓
4. 配置路由
↓
5. 开发控制器
↓
6. 开发服务层
↓
7. 开发模型层
↓
8. 配置中间件
↓
9. 测试部署
21.3 学习路径
初级:
- 理解多应用概念
- 掌握目录结构
- 学会基本路由配置
- 实现简单的 CRUD
中级:
- 掌握服务层设计
- 理解依赖注入
- 学会中间件使用
- 掌握缓存策略
高级:
- 事件系统应用
- 性能优化
- 安全防护
- 分布式部署
22. 参考资源
22.1 官方文档
- ThinkPHP 8 官方文档: https://doc.thinkphp.cn/v8_0/
- 多应用扩展: https://github.com/top-think/think-multi-app
- GitHub: https://github.com/top-think/think
22.2 相关教程
22.3 社区资源
- ThinkPHP 社区: https://www.kancloud.cn/thinkphp
- 问答社区: https://www.thinkphp.cn/ask
23. 快速参考
23.1 常用命令
bash
# 安装多应用扩展
composer require topthink/think-multi-app
# 创建控制器
php think make:controller admin@User
# 创建模型
php think make:model common@User
# 创建中间件
php think make:middleware admin@AdminAuth
# 缓存路由
php think optimize:route
# 缓存配置
php think optimize:config
# 清除缓存
php think clear
23.2 常用配置
php
<?php
// 设置默认应用
'default_app' => 'index',
// 禁止访问的应用
'deny_app_list' => ['common'],
// 域名绑定
'domain_bind' => [
'admin.example.com' => 'admin',
],
// 应用映射
'app_map' => [
'backend' => 'admin',
],
23.3 目录速查
app/common/ # 共享资源
├── model/ # 模型
├── service/ # 服务
├── library/ # 类库
├── validate/ # 验证器
└── exception/ # 异常类
app/admin/ # 后台应用
├── controller/ # 控制器
├── service/ # 服务
├── middleware/ # 中间件
├── view/ # 视图
└── route/ # 路由
app/api/ # API 应用
├── controller/
├── service/
├── middleware/
└── route/
app/index/ # 前台应用
├── controller/
├── service/
├── middleware/
├── view/
└── route/
24. 附录
24.1 完整的 composer.json
json
{
"name": "topthink/think",
"type": "project",
"require": {
"php": ">=8.0",
"topthink/framework": "^8.0",
"topthink/think-orm": "^3.0",
"topthink/think-multi-app": "^1.0",
"topthink/think-view": "^1.0",
"firebase/php-jwt": "^6.0"
},
"autoload": {
"psr-4": {
"app\\": "app/"
}
}
}
24.2 完整的 .env 示例
env
APP_DEBUG = true
APP_TRACE = false
[APP]
DEFAULT_TIMEZONE = Asia/Shanghai
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = myapp
USERNAME = root
PASSWORD = 123456
HOSTPORT = 3306
CHARSET = utf8mb4
PREFIX = tp_
[REDIS]
HOST = 127.0.0.1
PORT = 6379
PASSWORD =
SELECT = 0
[CACHE]
DRIVER = redis
PREFIX = myapp:
🎉 恭喜!您已完成 ThinkPHP 8 多应用模式完整教程的学习!
如有问题,请参考: