作为一名经历过多个电商项目的老兵,我最近完成了一个基于ThinkPHP和Laravel双框架的图书商城项目。这个案例特别有意思,因为它不仅实现了常规的电商功能,更重要的是通过对比两个主流PHP框架在实际项目中的表现,为技术选型提供了真实参考。
为什么选择图书商城作为载体?首先,图书销售具有标准化程度高、SKU管理相对简单的特点,适合作为框架对比的"试验场"。其次,线上图书市场近年来持续增长,2022年中国图书零售市场规模已达926亿元,这种业务场景具有现实商业价值。
项目的核心需求可以归纳为:
选择ThinkPHP 6.0和Laravel 8.0并非偶然。这两个版本都是各自框架的LTS(长期支持)版本,在稳定性和社区支持方面都有保障。我在技术评审会上与团队争论了很久,最终决定采用双框架实现而非混用,主要基于以下考虑:
系统采用经典的B/S架构,但在细节上做了不少优化:
code复制[客户端层]
├─ 移动端适配:采用响应式布局而非独立m站
└─ PC端优化:针对大屏增加"书架视图"模式
[表现层]
├─ ThinkPHP实现:基于传统的MVC模式
└─ Laravel实现:采用服务层隔离的DDD变体
[服务层]
├─ 共用服务:支付、短信、邮件
└─ 特有服务:TP的快速CRUD生成 vs Laravel的队列服务
[数据层]
├─ 主库:MySQL 8.0(共用)
└─ 缓存:Redis 6.2(差异化配置)
特别要说明的是数据库设计。虽然两个框架共用同一个MySQL实例,但在表结构设计上:
sql复制# ThinkPHP偏好的设计
CREATE TABLE `tp_books` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
# Laravel推荐的设计
CREATE TABLE `laravel_books` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`price` decimal(8,2) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
可以看到,Laravel默认采用更严格的字段约束和时间戳管理,这反映了两个框架不同的设计哲学。
用户模块是电商系统的基础,两个框架的实现差异非常典型:
ThinkPHP实现方案:
php复制// 控制器方法
public function login()
{
$user = UserModel::where('username', input('username'))
->where('password', md5(input('password')))
->find();
if($user){
session('user', $user);
return json(['code'=>200]);
}
return json(['code'=>400]);
}
Laravel实现方案:
php复制// 使用内置的AuthenticatesUsers trait
class LoginController extends Controller {
use AuthenticatesUsers;
protected function attemptLogin(Request $request)
{
return Auth::attempt(
$request->only('email', 'password'),
$request->filled('remember')
);
}
}
关键差异点:
图书搜索是核心功能,我们实现了三种搜索方式:
基础搜索(框架内置)
where('title','like',"%$keyword%")where('title','LIKE',"%{$keyword}%")高级搜索(自定义)
php复制// 两框架共用的搜索逻辑
$query = Book::when($request->has('author'), function($q) use ($request){
return $q->where('author', $request->author);
})->when($request->has('min_price'), function($q) use ($request){
return $q->where('price', '>=', $request->min_price);
});
php复制// 使用通用客户端
$client = ClientBuilder::create()
->setHosts(config('elasticsearch.hosts'))
->build();
$params = [
'index' => 'books',
'body' => [
'query' => [
'multi_match' => [
'query' => $keyword,
'fields' => ['title^3', 'author^2', 'description']
]
]
]
];
$results = $client->search($params);
实测发现,在10万级图书数据下:
支付模块需要特别注意安全性和稳定性,我们的实现方案:
mermaid复制graph TD
A[创建订单] --> B[选择支付方式]
B --> C{支付宝/微信}
C -->|支付宝| D[跳转支付页面]
C -->|微信| E[生成支付二维码]
D --> F[支付结果回调]
E --> F
F --> G[订单状态更新]
ThinkPHP支付控制器:
php复制public function alipay($orderId)
{
$order = OrderModel::find($orderId);
$config = config('payment.alipay');
$alipay = new AlipayTradeService($config);
$request = new AlipayTradePagePayRequest();
$request->setReturnUrl(url('return'));
$request->setNotifyUrl(url('notify'));
$request->setBizContent(json_encode([
'out_trade_no' => $order->sn,
'total_amount' => $order->amount,
'subject' => "图书购买:{$order->books->pluck('title')->join(',')}"
]));
return $alipay->pageExecute($request);
}
Laravel支付服务:
php复制class PaymentService {
public function handleAlipay(Order $order)
{
$alipay = app('alipay');
return $alipay->page([
'out_trade_no' => $order->number,
'total_amount' => $order->total,
'subject' => '图书购买:'.$order->items->count().'本'
]);
}
}
值得注意的安全实践:
ThinkPHP缓存方案:
php复制// 使用内置缓存
$books = cache('hot_books');
if(!$books){
$books = BookModel::order('sales DESC')->limit(10)->select();
cache('hot_books', $books, 3600);
}
Laravel缓存方案:
php复制// 使用记忆化缓存
$books = Cache::remember('hot_books', 3600, function(){
return Book::orderByDesc('sales')->limit(10)->get();
});
性能测试数据(ab -n 1000 -c 100):
| 场景 | TP QPS | Laravel QPS |
|---|---|---|
| 无缓存 | 128 | 98 |
| 文件缓存 | 210 | 185 |
| Redis缓存 | 420 | 390 |
| 预生成静态JSON | 680 | 650 |
索引优化:
sql复制# 图书表关键索引
ALTER TABLE books ADD INDEX idx_category_status (category_id, status);
ALTER TABLE books ADD FULLTEXT INDEX ft_title_author (title, author);
查询优化:
php复制// 避免的写法
$books = Book::all()->filter(function($book){
return $book->price > 50;
});
// 推荐的写法
$books = Book::where('price', '>', 50)->get();
连接池配置:
env复制# ThinkPHP数据库配置
DB_POOL_SIZE = 50
DB_IDLE_TIMEOUT = 300
# Laravel数据库配置
DB_CONNECTION=mysql
DB_POOL_SIZE=100
XSS防护:
htmlspecialchars过滤输出{!! !!}明确不转义CSRF防护:
html复制<!-- ThinkPHP表单 -->
<form>
<input type="hidden" name="__token__" value="{$Request.token}">
</form>
<!-- Laravel表单 -->
<form>
@csrf
</form>
SQL注入防护:
php复制// 不安全的写法
DB::select("SELECT * FROM users WHERE id = $id");
// 安全的写法
DB::select("SELECT * FROM users WHERE id = ?", [$id]);
订单金额校验:
php复制public function payNotify(Request $request)
{
DB::transaction(function() use ($request){
$order = Order::lockForUpdate()->find($request->order_id);
if(abs($order->amount - $request->amount) > 0.01){
throw new Exception('金额不一致');
}
// 处理支付成功逻辑
});
}
库存扣减方案:
sql复制UPDATE books SET stock = stock - 1
WHERE id = 123 AND stock >= 1;
风控规则示例:
最小化生产环境配置:
ThinkPHP优化配置:
php复制// config/app.php
return [
'app_debug' => false,
'app_trace' => false,
'sql_explain' => false,
'database' => [
'break_reconnect' => true,
'fields_cache' => true
]
];
Laravel优化命令:
bash复制# 优化配置加载
php artisan config:cache
# 路由缓存
php artisan route:cache
# 类映射优化
php artisan optimize
基础监控:
业务监控:
日志分析:
php复制// 结构化日志示例
Log::channel('payment')->info('Payment notified', [
'order_id' => $order->id,
'amount' => $order->amount,
'payment_id' => $request->trade_no
]);
经过完整项目实践,我总结出两个框架的核心差异:
| 维度 | ThinkPHP优势 | Laravel优势 |
|---|---|---|
| 开发速度 | 脚手架生成快,中文文档完善 | Artisan命令丰富,生态工具多 |
| 性能表现 | 简单请求响应更快(约快15-20%) | 复杂业务更稳定 |
| 学习曲线 | 对新手更友好 | 概念更现代但学习成本略高 |
| 扩展能力 | 适合标准CRUD业务 | 适合需要长期演进的中大型项目 |
| 社区支持 | 国内社区活跃 | 全球生态更丰富 |
选型建议:
最初尝试让两个框架共享Session失败,解决方案:
php复制// 统一Session驱动配置(使用Redis)
// ThinkPHP
'SESSION_TYPE' => 'Redis',
'SESSION_PREFIX' => 'tp_session:',
// Laravel
SESSION_DRIVER=redis
SESSION_CONNECTION=session
REDIS_PREFIX=laravel_session:
双框架生成订单号时出现重复,最终方案:
php复制// ThinkPHP
$orderSn = date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
// Laravel
$orderSn = Str::upper(Str::random(2)).date('YmdHis').mt_rand(1000,9999);
支付宝回调验证在不同框架下的处理差异:
php复制// ThinkPHP验证
$alipay = new AlipayTradeService($config);
$result = $alipay->check($request->post());
// Laravel验证
$alipay = app('alipay');
$verified = $alipay->verify();
基于当前实现,后续可以重点考虑:
服务化拆分:
混合存储方案:
mermaid复制graph LR
A[热点数据] --> Redis
B[商品信息] --> MySQL
C[搜索索引] --> Elasticsearch
D[图片资源] --> OSS
智能化扩展:
这个项目给我的最大启示是:没有最好的框架,只有最适合的框架。在实际开发中,我们团队发现ThinkPHP在快速原型开发阶段确实更高效,但当业务复杂度上升到一定水平后,Laravel的结构化优势就显现出来了。建议新手可以从ThinkPHP入手,但要有意识地学习Laravel的设计思想。