Laravel 10:订单创建代码优化问题咨询
订单创建函数优化方案
原代码的核心问题集中在多次重复数据库查询和逻辑可读性差上,以下是针对性的优化方案:
主要优化措施
1. 批量查询脚本,消灭N+1查询
原代码在循环里挨个调用Script::find($id),每传一个脚本ID就发起一次SQL查询,脚本数量多的时候性能会急剧下降。改成用whereIn批量查询,一次SQL就能获取所有有效脚本。
2. 一次性获取用户已购脚本,避免重复校验
之前每次循环都调用hasScript检查用户是否拥有该脚本,会产生大量重复查询。现在直接一次性拉取用户所有已拥有的脚本ID,通过集合对比就能快速找出重复购买的脚本。
3. 拆解复杂链式调用,提升代码可读性
原来的嵌套链式调用过于绕弯,还需要在闭包里引用外部变量,维护成本高。现在把逻辑拆成独立步骤,每一步的职责清晰明了。
4. 修复低级错误+事务兜底
- 原代码里
$data["cost"]是笔误,应为$order_data["cost"],已修正。 - 将订单创建、余额扣除、脚本关联等写操作包裹在数据库事务中,确保所有操作要么全部成功,要么全部回滚,避免出现数据不一致的情况。
优化后的完整代码
/** * @param array $request_data * @return Order * @throws AuthException * @throws OrderException * @throws PromocodeException */ public function create(array $request_data): Order { if (!auth()->check()) { throw AuthException::unauthenticated(); } /** @var User $user */ $user = auth()->user(); $promocodeService = new PromocodeService(); $userService = new UserService(); // 提取请求中的脚本ID并去重,避免重复处理 $scriptIds = collect($request_data["scripts"])->unique()->values()->all(); if (empty($scriptIds)) { throw OrderException::noScriptIsSelected(); } // 批量查询有效脚本,一次SQL搞定所有 $scripts = Script::whereIn('id', $scriptIds)->get(); if ($scripts->count() !== count($scriptIds)) { // 存在无效脚本ID,可根据业务需求抛异常或过滤 throw OrderException::invalidScript(); } // 一次性获取用户已拥有的所有脚本ID $userOwnedScriptIds = $user->scripts()->pluck('id')->toArray(); $duplicateScripts = $scripts->whereIn('id', $userOwnedScriptIds); if ($duplicateScripts->isNotEmpty()) { $duplicateNames = $duplicateScripts->pluck('name')->implode(', '); throw OrderException::alreadyHasScript($duplicateNames); } // 组装订单基础数据,直接用sum计算总金额 $orderData = [ "user_id" => $user->id, "scripts" => $scripts->pluck('id')->toArray(), "cost" => $scripts->sum('cost'), "promocode_id" => null ]; // 处理优惠码逻辑 if (isset($request_data["promocode"])) { $promoResult = $promocodeService->useByName($request_data["promocode"], $orderData["cost"]); $orderData["cost"] = $promoResult["new_cost"]; $orderData["promocode_id"] = $promoResult["promocode_id"]; } // 检查余额是否足够 if ($orderData["cost"] > $user->balance) { throw OrderException::notEnoughMoney(); } // 事务包裹所有写操作,保证数据一致性 DB::transaction(function () use ($user, $orderData) { /** @var Order $order */ $order = $user->orders()->create($orderData); $order->scripts()->sync($orderData["scripts"]); // 用Laravel内置的decrement方法,原子性更安全 $user->decrement('balance', $orderData["cost"]); $user->scripts()->attach($orderData["scripts"]); }); // 重新查询订单并加载关联脚本,确保返回完整数据 return $user->orders()->with('scripts')->latest()->first(); }
额外优化细节
- 用
unique()去重脚本ID,避免重复处理同一个脚本。 - 对比查询到的脚本数量和请求ID数量,快速识别无效脚本。
- 替换手动累加金额为
sum('cost'),代码更简洁高效。 - 用
decrement方法扣余额,比手动计算后更新更安全,避免并发问题。
内容的提问来源于stack exchange,提问作者Ademax
相关产品推荐
相关产品推荐

