ThinkPHP 6.0 深度实践:构建高性能API服务与多态关联实战指南

2026-01-11 0 274
免费资源下载

作者:技术架构师 | 发布日期:2023年10月

一、项目架构设计理念

在现代Web开发中,API服务已成为系统间通信的核心。本次我们将基于ThinkPHP 6.0构建一个电商平台的评论系统,重点展示多态关联在实际业务中的应用。

1.1 分层架构设计

app/
├── controller/    # 控制器层
├── service/      # 业务服务层(新增)
├── model/        # 数据模型层
├── repository/   # 数据仓库层(新增)
├── validate/     # 验证器层
└── middleware/   # 中间件层

二、多态关联模型实战

2.1 业务场景分析

电商系统中,评论功能需要支持多种类型的目标对象:商品、文章、视频等。传统的外键关联无法满足这种动态关联需求,这正是多态关联的应用场景。

2.2 数据表设计

-- 评论表
CREATE TABLE `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `content` text NOT NULL COMMENT '评论内容',
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `commentable_type` varchar(50) NOT NULL COMMENT '关联模型类型',
  `commentable_id` int(11) NOT NULL COMMENT '关联模型ID',
  `created_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 商品表
CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '商品名称',
  `price` decimal(10,2) NOT NULL COMMENT '价格',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 文章表
CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL COMMENT '文章标题',
  `content` text NOT NULL COMMENT '文章内容',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.3 模型定义

// app/model/Comment.php
namespace appmodel;

use thinkModel;

class Comment extends Model
{
    // 定义多态关联
    public function commentable()
    {
        return $this->morphTo();
    }
    
    // 关联用户
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

// app/model/Product.php
class Product extends Model
{
    // 定义反向多态关联
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

// app/model/Article.php
class Article extends Model
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

三、服务层设计与实现

3.1 创建评论服务

// app/service/CommentService.php
namespace appservice;

use appmodelComment;
use apprepositoryCommentRepository;
use appvalidateCommentValidate;
use thinkexceptionValidateException;

class CommentService
{
    protected $repository;
    
    public function __construct(CommentRepository $repository)
    {
        $this->repository = $repository;
    }
    
    /**
     * 添加评论
     * @param array $data 评论数据
     * @return Comment
     * @throws Exception
     */
    public function addComment(array $data): Comment
    {
        // 数据验证
        validate(CommentValidate::class)->check($data);
        
        // 业务逻辑处理
        $data['user_id'] = request()->userId; // 从JWT中获取
        
        // 使用仓库模式保存
        $comment = $this->repository->create($data);
        
        // 触发评论添加事件
        event('CommentAdded', $comment);
        
        return $comment;
    }
    
    /**
     * 获取目标对象的评论列表(支持分页)
     * @param string $type 模型类型
     * @param int $id 模型ID
     * @param int $page 页码
     * @param int $limit 每页数量
     * @return array
     */
    public function getComments(string $type, int $id, int $page = 1, int $limit = 15): array
    {
        $modelClass = $this->getModelClass($type);
        
        if (!class_exists($modelClass)) {
            throw new Exception('不支持的评论目标类型');
        }
        
        $model = $modelClass::find($id);
        
        if (!$model) {
            throw new Exception('目标对象不存在');
        }
        
        // 使用with预加载用户信息
        $comments = $model->comments()
            ->with(['user' => function($query) {
                $query->field('id,username,avatar');
            }])
            ->order('created_at', 'desc')
            ->paginate([
                'page' => $page,
                'list_rows' => $limit
            ]);
        
        return [
            'list' => $comments->items(),
            'total' => $comments->total(),
            'page' => $comments->currentPage(),
            'limit' => $comments->listRows()
        ];
    }
    
    /**
     * 获取模型类名
     * @param string $type
     * @return string
     */
    private function getModelClass(string $type): string
    {
        $map = [
            'product' => 'appmodelProduct',
            'article' => 'appmodelArticle',
            'video' => 'appmodelVideo',
        ];
        
        return $map[$type] ?? '';
    }
}

3.2 数据仓库层

// app/repository/CommentRepository.php
namespace apprepository;

use appmodelComment;
use thinkfacadeDb;

class CommentRepository
{
    /**
     * 创建评论(使用事务)
     * @param array $data
     * @return Comment
     * @throws Exception
     */
    public function create(array $data): Comment
    {
        Db::startTrans();
        try {
            $comment = Comment::create($data);
            
            // 更新目标对象的评论计数
            $this->updateCommentCount($data['commentable_type'], $data['commentable_id']);
            
            Db::commit();
            return $comment;
        } catch (Exception $e) {
            Db::rollback();
            throw $e;
        }
    }
    
    /**
     * 更新评论计数
     * @param string $type
     * @param int $id
     */
    private function updateCommentCount(string $type, int $id): void
    {
        $table = $this->getTableName($type);
        $count = Comment::where([
            'commentable_type' => $type,
            'commentable_id' => $id
        ])->count();
        
        Db::table($table)
            ->where('id', $id)
            ->update(['comment_count' => $count]);
    }
    
    private function getTableName(string $type): string
    {
        return $type . 's'; // 简单映射,实际应根据配置
    }
}

四、控制器与API设计

4.1 RESTful API控制器

// app/controller/api/v1/Comment.php
namespace appcontrollerapiv1;

use appserviceCommentService;
use thinkfacadeRequest;
use thinkResponse;

class Comment
{
    protected $service;
    
    public function __construct(CommentService $service)
    {
        $this->service = $service;
    }
    
    /**
     * 添加评论
     * @return Response
     */
    public function create(): Response
    {
        $data = Request::only([
            'content', 
            'commentable_type', 
            'commentable_id'
        ]);
        
        try {
            $comment = $this->service->addComment($data);
            
            return json([
                'code' => 200,
                'message' => '评论成功',
                'data' => $comment->hidden(['user_id', 'updated_at'])
            ]);
        } catch (Exception $e) {
            return json([
                'code' => 500,
                'message' => $e->getMessage()
            ], 500);
        }
    }
    
    /**
     * 获取评论列表
     * @param string $type
     * @param int $id
     * @return Response
     */
    public function index(string $type, int $id): Response
    {
        $page = Request::param('page', 1);
        $limit = Request::param('limit', 15);
        
        try {
            $result = $this->service->getComments($type, $id, $page, $limit);
            
            return json([
                'code' => 200,
                'message' => 'success',
                'data' => $result
            ]);
        } catch (Exception $e) {
            return json([
                'code' => 404,
                'message' => $e->getMessage()
            ], 404);
        }
    }
}

4.2 路由配置

// route/api.php
use thinkfacadeRoute;

// 评论API
Route::group('api/v1', function() {
    // 获取评论列表
    Route::get('comments/:type/:id', 'api/v1.Comment/index');
    
    // 添加评论
    Route::post('comments', 'api/v1.Comment/create');
    
    // 删除评论
    Route::delete('comments/:id', 'api/v1.Comment/delete');
})->allowCrossDomain();

五、高级特性与优化

5.1 使用观察者模式

// app/observer/CommentObserver.php
namespace appobserver;

use appmodelComment;
use thinkfacadeCache;

class CommentObserver
{
    /**
     * 新增评论后清除缓存
     * @param Comment $comment
     */
    public function afterInsert(Comment $comment): void
    {
        $cacheKey = sprintf(
            'comments_%s_%d', 
            $comment->commentable_type, 
            $comment->commentable_id
        );
        
        Cache::delete($cacheKey);
        
        // 异步处理通知
        queue('appjobNotifyJob', [
            'type' => 'comment',
            'data' => $comment->toArray()
        ]);
    }
    
    /**
     * 删除评论前检查权限
     * @param Comment $comment
     * @return bool
     */
    public function beforeDelete(Comment $comment): bool
    {
        $userId = request()->userId;
        
        // 只能删除自己的评论或管理员
        if ($comment->user_id != $userId && !$this->isAdmin($userId)) {
            return false;
        }
        
        return true;
    }
}

5.2 性能优化策略

// 使用Redis缓存评论列表
public function getCachedComments(string $type, int $id): array
{
    $cacheKey = sprintf('comments_%s_%d', $type, $id);
    
    return Cache::remember($cacheKey, 300, function() use ($type, $id) {
        return $this->getComments($type, $id);
    });
}

// 数据库查询优化
public function getCommentsWithOptimization(string $type, int $id): array
{
    return Comment::where([
            'commentable_type' => $type,
            'commentable_id' => $id
        ])
        ->with([
            'user' => function($query) {
                $query->cache(60)->field('id,username,avatar');
            }
        ])
        ->field('id,content,user_id,created_at')
        ->order('created_at', 'desc')
        ->select()
        ->toArray();
}

六、测试用例

6.1 单元测试示例

// tests/CommentServiceTest.php
namespace tests;

use appserviceCommentService;
use thinktestingTestCase;

class CommentServiceTest extends TestCase
{
    protected $service;
    
    protected function setUp(): void
    {
        parent::setUp();
        $this->service = app(CommentService::class);
    }
    
    /**
     * 测试添加评论
     */
    public function testAddComment(): void
    {
        $data = [
            'content' => '测试评论内容',
            'commentable_type' => 'product',
            'commentable_id' => 1,
        ];
        
        $comment = $this->service->addComment($data);
        
        $this->assertInstanceOf('appmodelComment', $comment);
        $this->assertEquals('测试评论内容', $comment->content);
        $this->assertEquals('product', $comment->commentable_type);
    }
    
    /**
     * 测试获取评论列表
     */
    public function testGetComments(): void
    {
        $result = $this->service->getComments('product', 1);
        
        $this->assertIsArray($result);
        $this->assertArrayHasKey('list', $result);
        $this->assertArrayHasKey('total', $result);
    }
}

七、部署与监控

7.1 Docker部署配置

# docker-compose.yml
version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    volumes:
      - ./app:/var/www/html/app
      - ./runtime:/var/www/html/runtime
    environment:
      - APP_DEBUG=false
      - DB_HOST=mysql
    depends_on:
      - mysql
      - redis
  
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: thinkphp_app
    volumes:
      - mysql_data:/var/lib/mysql
  
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

volumes:
  mysql_data:

7.2 性能监控配置

// config/trace.php
return [
    // 开启SQL监听
    'sql' => true,
    
    // 慢查询阈值(毫秒)
    'slow_sql_time' => 1000,
    
    // 请求追踪
    'request' => true,
    
    // 内存使用监控
    'memory' => true,
];

// 使用中间件记录请求日志
class RequestLogMiddleware
{
    public function handle($request, Closure $next)
    {
        $startTime = microtime(true);
        $startMemory = memory_get_usage();
        
        $response = $next($request);
        
        $endTime = microtime(true);
        $endMemory = memory_get_usage();
        
        // 记录到日志或监控系统
        Log::write(sprintf(
            '请求: %s | 耗时: %.2fms | 内存: %.2fMB',
            $request->url(),
            ($endTime - $startTime) * 1000,
            ($endMemory - $startMemory) / 1024 / 1024
        ));
        
        return $response;
    }
}
ThinkPHP 6.0 深度实践:构建高性能API服务与多态关联实战指南
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 thinkphp ThinkPHP 6.0 深度实践:构建高性能API服务与多态关联实战指南 https://www.taomawang.com/server/thinkphp/1517.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务