从零开始构建 ThinkPHP 8 多应用架构的完整指南
ThinkPHP 8 多应用模式完整教程
从零开始构建 ThinkPHP 8 多应用架构的完整指南
📖 目录
- 1. 什么是多应用模式
- 2. 多应用模式安装与配置
- 3. 目录结构规划
- 4. 路由配置
- 5. 服务层架构
- 6. 模型层共享
- 7. 缓存配置
- 8. 中间件配置
- 9. 异常处理
- 10. 数据库配置
- 11. 视图配置
- 12. 公共类库
- 13. 事件系统
- 14. 验证器共享
- 15. 跨应用调用
- 16. 最佳实践
- 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 默认是单应用模式,需要安装多应用扩展:
# 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 验证安装
安装成功后,会自动生成多应用支持文件:
# 查看是否安装成功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:手动创建
# 创建后台应用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:使用命令行(推荐)
# 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
<?phpreturn[// 是否强制使用路由'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
<?phpusethink\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
<?phpusethink\facade\Route;// API 版本 1Route::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 版本 2Route::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
<?phpusethink\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
<?phpreturn[// 域名绑定'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
<?phpreturn[// 应用映射'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
<?phpdeclare(strict_types=1);namespaceapp\common\service;useapp\common\model\User;usethink\facade\Cache;/** * 用户服务(所有应用共享) */classUserService{/** * 获取用户信息(带缓存) */publicfunctiongetUserInfo(int$userId):array{$cacheKey='user_info_'.$userId;returnCache::remember($cacheKey,function()use($userId){$user=User::find($userId);return$user?$user->toArray():[];},3600);}/** * 更新用户信息 */publicfunctionupdateUser(int$userId,array$data):bool{$user=User::find($userId);if(!$user){thrownew\Exception('用户不存在');}$result=$user->save($data);// 清除缓存Cache::delete('user_info_'.$userId);return$result!==false;}/** * 检查用户名是否存在 */publicfunctioncheckUsername(string$username):bool{returnUser::where('username',$username)->count()>0;}}短信服务:app/common/service/SmsService.php
<?phpdeclare(strict_types=1);namespaceapp\common\service;usethink\facade\Cache;/** * 短信服务(所有应用共享) */classSmsService{/** * 发送验证码 */publicfunctionsendCode(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);returntrue;}returnfalse;}/** * 验证验证码 */publicfunctionverifyCode(string$mobile,string$code,string$scene='login'):bool{$cacheKey='sms_code_'.$scene.'_'.$mobile;$cachedCode=Cache::get($cacheKey);if($cachedCode&&$cachedCode===$code){// 验证成功后删除缓存Cache::delete($cacheKey);returntrue;}returnfalse;}/** * 生成验证码 */privatefunctiongenerateCode(int$length=6):string{returnstr_pad((string)mt_rand(0,pow(10,$length)-1),$length,'0',STR_PAD_LEFT);}/** * 发送短信(示例) */privatefunctionsendSms(string$mobile,string$code):bool{// 这里接入实际的短信服务商 API// 例如:阿里云、腾讯云等returntrue;}}5.3 应用专属服务
后台管理员服务:app/admin/service/AdminService.php
<?phpdeclare(strict_types=1);namespaceapp\admin\service;useapp\common\model\Admin;usethink\facade\Session;classAdminService{publicfunctionlogin(string$username,string$password):array{$admin=Admin::where('username',$username)->find();if(!$admin||!password_verify($password,$admin->password)){thrownew\Exception('用户名或密码错误');}Session::set('admin_id',$admin->id);Session::set('admin_name',$admin->username);return$admin->toArray();}}API Token 服务:app/api/service/TokenService.php
<?phpdeclare(strict_types=1);namespaceapp\api\service;useFirebase\JWT\JWT;useFirebase\JWT\Key;classTokenService{privatestring$key='your-secret-key';publicfunctioncreateToken(array$data):string{$payload=['iat'=>time(),'exp'=>time()+7200,'data'=>$data];returnJWT::encode($payload,$this->key,'HS256');}publicfunctionverifyToken(string$token):array{try{$decoded=JWT::decode($token,newKey($this->key,'HS256'));return(array)$decoded->data;}catch(\Exception$e){thrownew\Exception('Token 无效',401);}}}6. 模型层共享
6.1 模型放置位置
所有模型统一放在 app/common/model/ 目录,供所有应用共享。
6.2 用户模型示例
文件:app/common/model/User.php
<?phpdeclare(strict_types=1);namespaceapp\common\model;usethink\Model;usethink\model\concern\SoftDelete;classUserextendsModel{useSoftDelete;protected$name='user';protectedstring$deleteTime='delete_time';protected$autoWriteTimestamp=true;protected$type=['id'=>'integer','status'=>'integer',];protected$hidden=['password','delete_time'];publicfunctionsetPasswordAttr($value):string{returnpassword_hash($value,PASSWORD_DEFAULT);}}6.3 文章模型示例
文件:app/common/model/Article.php
<?phpdeclare(strict_types=1);namespaceapp\common\model;usethink\Model;classArticleextendsModel{protected$name='article';protected$autoWriteTimestamp=true;// 关联分类publicfunctioncategory(){return$this->belongsTo(Category::class,'cate_id');}// 关联作者publicfunctionauthor(){return$this->belongsTo(User::class,'user_id');}}7. 缓存配置
7.1 统一缓存配置
文件:config/cache.php
<?phpreturn['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
<?phpdeclare(strict_types=1);namespaceapp\common\service;usethink\facade\Cache;classCacheService{// 设置应用缓存(自动添加应用前缀)publicstaticfunctionsetAppCache(string$key,$value,int$expire=0):bool{$appName=app('http')->getName();$cacheKey=$appName.':'.$key;returnCache::set($cacheKey,$value,$expire);}// 获取应用缓存publicstaticfunctiongetAppCache(string$key){$appName=app('http')->getName();$cacheKey=$appName.':'.$key;returnCache::get($cacheKey);}// 设置共享缓存publicstaticfunctionsetCommonCache(string$key,$value,int$expire=0):bool{returnCache::set('common:'.$key,$value,$expire);}// 获取共享缓存publicstaticfunctiongetCommonCache(string$key){returnCache::get('common:'.$key);}}8. 中间件配置
8.1 全局中间件
文件:app/middleware.php
<?php// 全局中间件定义(所有应用都会执行)return[// 跨域请求支持\think\middleware\AllowCrossDomain::class,// Session 初始化\think\middleware\SessionInit::class,];8.2 应用中间件
后台中间件:app/admin/middleware.php
<?phpreturn[// 后台认证中间件\app\admin\middleware\AdminAuth::class,];API 中间件:app/api/middleware.php
<?phpreturn[// JWT 认证中间件\app\api\middleware\JwtAuth::class,// 跨域中间件\app\api\middleware\Cors::class,];8.3 后台认证中间件
文件:app/admin/middleware/AdminAuth.php
<?phpdeclare(strict_types=1);namespaceapp\admin\middleware;usethink\facade\Session;classAdminAuth{publicfunctionhandle($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')){returnredirect('/admin/login');}return$next($request);}}8.4 JWT 认证中间件
文件:app/api/middleware/JwtAuth.php
<?phpdeclare(strict_types=1);namespaceapp\api\middleware;useapp\api\service\TokenService;classJwtAuth{publicfunctionhandle($request,\Closure$next){$token=$request->header('Authorization');if(empty($token)){returnjson(['code'=>401,'msg'=>'未登录']);}// 移除 "Bearer " 前缀$token=str_replace('Bearer ','',$token);$tokenService=newTokenService();try{$userData=$tokenService->verifyToken($token);$request->userData=$userData;}catch(\Exception$e){returnjson(['code'=>401,'msg'=>$e->getMessage()]);}return$next($request);}}8.5 跨域中间件
文件:app/api/middleware/Cors.php
<?phpdeclare(strict_types=1);namespaceapp\api\middleware;classCors{publicfunctionhandle($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'){returnresponse('',200);}return$next($request);}}9. 异常处理
9.1 全局异常处理
文件:app/ExceptionHandle.php
<?phpdeclare(strict_types=1);namespaceapp;usethink\db\exception\DataNotFoundException;usethink\db\exception\ModelNotFoundException;usethink\exception\Handle;usethink\exception\HttpException;usethink\exception\ValidateException;usethink\Response;useThrowable;classExceptionHandleextendsHandle{publicfunctionrender($request,Throwable$e):Response{// 验证异常if($einstanceofValidateException){returnjson(['code'=>1,'msg'=>$e->getError()]);}// 数据不存在异常if($einstanceofDataNotFoundException||$einstanceofModelNotFoundException){returnjson(['code'=>404,'msg'=>'数据不存在']);}// HTTP 异常if($einstanceofHttpException){returnjson(['code'=>$e->getStatusCode(),'msg'=>$e->getMessage()]);}// 其他异常returnjson(['code'=>500,'msg'=>$e->getMessage(),'file'=>$e->getFile(),'line'=>$e->getLine(),]);}}9.2 自定义异常类
文件:app/common/exception/ApiException.php
<?phpdeclare(strict_types=1);namespaceapp\common\exception;useException;classApiExceptionextendsException{publicfunction__construct(string$message='',int$code=400){parent::__construct($message,$code);}}9.3 使用自定义异常
<?phpnamespaceapp\api\controller;useapp\common\exception\ApiException;classUser{publicfunctioninfo(){$user=User::find(1);if(!$user){thrownewApiException('用户不存在',404);}returnjson(['code'=>0,'data'=>$user]);}}10. 数据库配置
10.1 统一数据库配置
文件:config/database.php
<?phpreturn['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
[DATABASE] TYPE = mysql HOSTNAME = 127.0.0.1 DATABASE = myapp USERNAME = root PASSWORD = 123456 HOSTPORT = 3306 PREFIX = tp_ 10.3 多数据库连接
如果不同应用需要连接不同数据库:
<?phpreturn['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// 使用默认连接$user=User::find(1);// 使用指定连接$admin=Admin::connect('mysql_admin')->find(1);11. 视图配置
11.1 统一视图配置
文件:config/view.php
<?phpreturn['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
<?phpnamespaceapp\admin\controller;useapp\BaseController;classIndexextendsBaseController{publicfunctionindex(){// 渲染视图:app/admin/view/index/index.htmlreturnview('index',['title'=>'后台首页','user'=>session('admin_name'),]);}}11.4 视图模板示例
文件:app/admin/view/index/index.html
<!DOCTYPEhtml><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
<?phpdeclare(strict_types=1);namespaceapp\common\library;usethink\facade\Session;classAuth{/** * 检查权限 */publicstaticfunctioncheck(string$rule):bool{$adminId=Session::get('admin_id');if(!$adminId){returnfalse;}// 超级管理员if($adminId==1){returntrue;}// 检查权限逻辑// ...returntrue;}}12.3 树形结构类示例
文件:app/common/library/Tree.php
<?phpdeclare(strict_types=1);namespaceapp\common\library;classTree{/** * 生成树形结构 */publicstaticfunctiontoTree(array$list,int$pid=0,string$pidField='pid'):array{$tree=[];foreach($listas$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
<?phpreturn['bind'=>[],'listen'=>['AppInit'=>[],'HttpRun'=>[],'HttpEnd'=>[],'LogWrite'=>[],],'subscribe'=>[],];13.2 应用事件定义
后台事件:app/admin/event.php
<?phpreturn['bind'=>[],'listen'=>['AdminLogin'=>[\app\admin\listener\AdminLoginListener::class,],],'subscribe'=>[],];13.3 事件类
文件:app/admin/event/AdminLogin.php
<?phpdeclare(strict_types=1);namespaceapp\admin\event;classAdminLogin{public$admin;publicfunction__construct($admin){$this->admin=$admin;}}13.4 监听器类
文件:app/admin/listener/AdminLoginListener.php
<?phpdeclare(strict_types=1);namespaceapp\admin\listener;useapp\admin\event\AdminLogin;classAdminLoginListener{publicfunctionhandle(AdminLogin$event){// 记录登录日志$admin=$event->admin;// 写入日志// ...}}13.5 触发事件
<?phpnamespaceapp\admin\controller;useapp\admin\event\AdminLogin;classLogin{publicfunctioncheck(){// 登录逻辑$admin=['id'=>1,'username'=>'admin'];// 触发事件event(newAdminLogin($admin));returnjson(['code'=>0,'msg'=>'登录成功']);}}14. 验证器共享
14.1 验证器目录
app/common/validate/ ├── UserValidate.php ├── ArticleValidate.php └── LoginValidate.php 14.2 用户验证器
文件:app/common/validate/UserValidate.php
<?phpdeclare(strict_types=1);namespaceapp\common\validate;usethink\Validate;classUserValidateextendsValidate{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
<?phpnamespaceapp\admin\controller;useapp\common\validate\UserValidate;usethink\Request;classUser{publicfunctionsave(Request$request){$data=$request->post();// 验证数据$validate=newUserValidate();if(!$validate->scene('register')->check($data)){returnjson(['code'=>1,'msg'=>$validate->getError()]);}// 保存数据// ...returnjson(['code'=>0,'msg'=>'保存成功']);}}API 控制器:app/api/controller/User.php
<?phpnamespaceapp\api\controller;useapp\common\validate\UserValidate;usethink\Request;classUser{publicfunctionregister(Request$request){$data=$request->post();// 使用相同的验证器$validate=newUserValidate();if(!$validate->scene('register')->check($data)){returnjson(['code'=>1,'msg'=>$validate->getError()]);}// 注册逻辑// ...returnjson(['code'=>0,'msg'=>'注册成功']);}}15. 跨应用调用
15.1 跨应用调用服务
场景:API 应用需要调用后台应用的服务
<?phpnamespaceapp\api\controller;useapp\admin\service\AdminService;// 调用后台服务classAdmin{publicfunctionlogin(){// 可以直接调用其他应用的服务$adminService=newAdminService();$result=$adminService->login('admin','123456');returnjson(['code'=>0,'data'=>$result]);}}15.2 跨应用调用模型
所有应用共享模型,无需跨应用调用
<?php// 后台应用namespaceapp\admin\controller;useapp\common\model\User;classUser{publicfunctionindex(){$list=User::select();returnjson(['code'=>0,'data'=>$list]);}}<?php// API 应用namespaceapp\api\controller;useapp\common\model\User;// 使用相同的模型classUser{publicfunctionlist(){$list=User::select();returnjson(['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 命名规范
控制器命名:
// ✅ 正确 app/admin/controller/User.php app/api/controller/User.php // ❌ 错误 app/admin/controller/user.php app/api/controller/UserController.php 服务命名:
// ✅ 正确 app/common/service/UserService.php app/admin/service/AdminService.php // ❌ 错误 app/common/service/User.php app/admin/service/Admin.php 16.3 代码复用
✅ 推荐:使用服务层复用业务逻辑
<?php// 共享服务namespaceapp\common\service;classUserService{publicfunctiongetUserInfo(int$userId):array{// 业务逻辑return[];}}// 后台控制器namespaceapp\admin\controller;classUser{publicfunctioninfo(){$service=new\app\common\service\UserService();returnjson($service->getUserInfo(1));}}// API 控制器namespaceapp\api\controller;classUser{publicfunctioninfo(){$service=new\app\common\service\UserService();returnjson($service->getUserInfo(1));}}16.4 依赖注入
✅ 推荐:使用依赖注入
<?phpnamespaceapp\admin\controller;useapp\common\service\UserService;classUser{protectedUserService$userService;// 构造函数注入publicfunction__construct(UserService$userService){$this->userService=$userService;}publicfunctioninfo(){returnjson($this->userService->getUserInfo(1));}}16.5 缓存策略
✅ 推荐:使用 Redis + 缓存前缀
<?php// 共享缓存(所有应用可访问)Cache::set('common:user_info_1',$data);// 应用专属缓存(带应用前缀)$appName=app('http')->getName();Cache::set($appName.':user_list',$data);16.6 路由规划
✅ 推荐:使用资源路由
<?php// app/admin/route/app.phpusethink\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/delete17. 常见问题
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
<?phpreturn[// 设置默认应用为 index'default_app'=>'index',];访问 http://example.com/ 会自动进入 index 应用。
17.3 如何禁止访问某个应用?
问题: 不想让用户直接访问 common 应用
解决方案:
文件:config/route.php
<?phpreturn[// 禁止访问的应用列表'deny_app_list'=>['common'],];17.4 多应用如何共享 Session?
问题: 后台登录后,API 应用无法获取 Session
解决方案:
Session 默认是共享的,确保以下配置:
文件:config/session.php
<?phpreturn['type'=>'file','prefix'=>'think',// 统一前缀'expire'=>3600,];17.5 如何实现应用间数据隔离?
问题: 不同应用需要使用不同的数据表前缀
解决方案:
方式 1:使用不同的数据库连接
<?php// config/database.phpreturn['connections'=>['mysql'=>['prefix'=>'admin_',],'mysql_api'=>['prefix'=>'api_',],],];// 使用Admin::connect('mysql')->select();// 使用 admin_ 前缀User::connect('mysql_api')->select();// 使用 api_ 前缀方式 2:在模型中指定前缀
<?phpnamespaceapp\common\model;classAdminextendsModel{protected$connection='mysql';protected$table='admin_user';// 完整表名}17.6 如何实现应用独立配置?
问题: 不同应用需要不同的配置
解决方案:
创建应用配置文件:app/admin/config/app.php
<?phpreturn['app_name'=>'后台管理系统','page_size'=>20,];使用应用配置:
<?php// 获取应用配置$appName=config('app.app_name');// 获取全局配置$debug=config('app.debug');17.7 如何实现域名绑定?
问题: 不同应用使用不同域名访问
解决方案:
文件:config/route.php
<?phpreturn['domain_bind'=>['admin.example.com'=>'admin','api.example.com'=>'api','www.example.com'=>'index',],];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// 获取当前应用名称$appName=app('http')->getName();echo$appName;// 输出:admin、api、index// 获取当前控制器$controller=request()->controller();// 获取当前操作$action=request()->action();17.9 多应用如何部署?
问题: 生产环境如何部署多应用?
解决方案:
# 1. 关闭调试模式# .env APP_DEBUG =false# 2. 优化自动加载composerinstall --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 如何创建新应用?
问题: 如何快速创建一个新应用?
解决方案:
# 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 functionindex(){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
<?phpdeclare(strict_types=1);namespaceapp\admin\controller;useapp\admin\service\AdminService;usethink\Request;classLogin{protectedAdminService$adminService;publicfunction__construct(AdminService$adminService){$this->adminService=$adminService;}// 登录页面publicfunctionindex(){returnview('login/index');}// 登录验证publicfunctioncheck(Request$request){$username=$request->post('username');$password=$request->post('password');try{$admin=$this->adminService->login($username,$password);returnjson(['code'=>0,'msg'=>'登录成功','data'=>$admin]);}catch(\Exception$e){returnjson(['code'=>1,'msg'=>$e->getMessage()]);}}// 退出登录publicfunctionlogout(){$this->adminService->logout();returnredirect('/admin/login');}}18.4 API 接口实现
控制器:app/api/controller/Article.php
<?phpdeclare(strict_types=1);namespaceapp\api\controller;useapp\common\model\Article;usethink\Request;classArticle{// 文章列表publicfunctionlist(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();returnjson(['code'=>0,'msg'=>'success','data'=>['list'=>$list,'total'=>$total,'page'=>$page,'limit'=>$limit,]]);}// 文章详情publicfunctiondetail(Request$request){$id=$request->param('id',0,'intval');$article=Article::with('category')->find($id);if(!$article){returnjson(['code'=>404,'msg'=>'文章不存在']);}returnjson(['code'=>0,'msg'=>'success','data'=>$article]);}}18.5 前台展示实现
控制器:app/index/controller/Article.php
<?phpdeclare(strict_types=1);namespaceapp\index\controller;useapp\common\model\Article;usethink\Request;classArticle{// 文章详情页publicfunctiondetail(Request$request){$id=$request->param('id',0,'intval');$article=Article::with('category')->find($id);if(!$article){abort(404,'文章不存在');}// 增加浏览量$article->inc('views')->update();returnview('article/detail',['article'=>$article,]);}}19. 性能优化建议
19.1 缓存优化
<?php// 1. 使用 Redis 缓存// config/cache.phpreturn['default'=>'redis',];// 2. 缓存查询结果$list=Article::cache('article_list',3600)->select();// 3. 使用标签缓存Cache::tag('article')->set('list',$data);Cache::tag('article')->clear();// 清除所有文章相关缓存19.2 数据库优化
<?php// 1. 只查询需要的字段$list=Article::field('id,title,create_time')->select();// 2. 使用预加载避免 N+1 问题$list=Article::with('category')->select();// 3. 使用索引// 确保 where 条件的字段有索引19.3 路由优化
# 缓存路由 php think optimize:route # 缓存配置 php think optimize:config 20. 安全建议
20.1 SQL 注入防护
<?php// ✅ 正确:使用参数绑定$list=Db::name('user')->where('id',$id)->select();// ❌ 错误:直接拼接 SQL$sql="SELECT * FROM user WHERE202_XSS__2286">20.2 XSS 防护<?php// 模板中自动转义{$content}// 自动转义// 不转义(危险){$content|raw}20.3 CSRF 防护<?php// 开启 CSRF 验证// config/middleware.phpreturn[\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-appGitHub: https://github.com/top-think/think22.2 相关教程TP8常见问题FAQ.mdthinkphp行为文档.md22.3 社区资源ThinkPHP 社区: https://www.kancloud.cn/thinkphp问答社区: https://www.thinkphp.cn/ask23. 快速参考23.1 常用命令# 安装多应用扩展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 clear23.2 常用配置<?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{"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 示例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 多应用模式完整教程的学习!如有问题,请参考:TP8常见问题FAQ.md官方文档