从零开始构建 ThinkPHP 8 多应用架构的完整指南

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 官方文档

22.2 相关教程

22.3 社区资源


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 多应用模式完整教程的学习!

如有问题,请参考:

相关推荐
码农水水2 小时前
中国电网Java面试被问:流批一体架构的实现和状态管理
java·c语言·开发语言·面试·职场和发展·架构·kafka
lkbhua莱克瓦242 小时前
稠密、稀疏与MoE:大模型时代的三重架构革命
人工智能·深度学习·机器学习·ai·架构
这儿有一堆花3 小时前
深入解析 VPC:云端网络架构的核心基石
网络·架构
鹏北海4 小时前
qiankun微前端通信与路由方案总结
前端·微服务·架构
郑州光合科技余经理4 小时前
私有化B2B订货系统实战:核心模块设计与代码实现
java·大数据·开发语言·后端·架构·前端框架·php
大猫和小黄4 小时前
若依从零到部署:前后端分离和微服务版
java·微服务·云原生·架构·前后端分离·若依
小小王app小程序开发4 小时前
家政服务小程序特殊玩法开发全解析:技术实现+架构支撑+合规落地
小程序·架构
u0104058364 小时前
Spring Boot与Spring Cloud的协同:构建健壮的微服务架构
spring boot·spring cloud·架构
鹏北海5 小时前
qiankun两种加载模式registerMicroApps和loadMicroApp对比分析
前端·架构·前端框架