默认用 session 存购物车,因 laravel 5.5 的 cart 类天然适配且支持未登录用户;登录后通过幂等逻辑将 session 数据迁至数据库 cart_items 表,该表以 (user_id, sku_id) 为联合唯一索引,所有操作需校验 sku 有效性、实时查价、原子增减数量,并同步清理 session 与数据库。

购物车数据该存在 session 还是数据库?
默认用 session,不是因为“简单”,而是 Laravel 5.5 的 Cart 类(需自行实现)天然适配 session 驱动,且用户未登录时必须靠它。强行存数据库会导致未登录用户无法加购、或要额外维护 guest_id 映射关系,得不偿失。
登录后可无缝迁移到数据库:监听 Auth::login 事件,把当前 session 中的购物车条目批量写入用户专属的 cart_items 表,并清空 session。迁移逻辑必须幂等——重复登录不能产生重复记录。
- session key 建议固定为
cart,避免和其它模块冲突 - 不要用
Session::put('cart', $data)直接塞整个数组,改用session()->put('cart.items', [...])分层存,方便后续 patch 更新 - 若开启 Redis session,注意
cart数据结构别嵌套过深,否则序列化/反序列化易出错
如何设计 cart_items 表支持多规格商品?
别用 product_id 直接关联商品主表。真实场景中同款商品可能有颜色、尺码等规格,库存和价格都独立。必须抽象出 sku_id(对应 skus 表),所有加购、减购、结算都基于它。
cart_items 至少包含:user_id(可为空)、sku_id、quantity、created_at、updated_at。关键约束:联合唯一索引 (user_id, sku_id),防止同一用户对同一 SKU 重复插入。
- 加购时先
where(['user_id' => $uid, 'sku_id' => $sid])->first(),存在则increment('quantity'),不存在才create() - 前端传来的
sku_id必须经过校验——查skus表确认状态为active且stock > 0,否则直接返回错误 - 不要在购物车里存价格,每次渲染/结算前实时查
skus.price,避免价格变更后购物车数据滞后
updateQuantity() 方法为什么总覆盖而不是累加?
常见错误是把前端传来的 quantity 当作“新值”直接赋给数据库字段,结果用户点“+1”却变成“设为 1”。正确做法是:读出现有数量,再做加减运算,最后写回。
Laravel 的 increment() 和 decrement() 是原子操作,比先查后改更安全,尤其在并发加购场景下能避免超卖。
- 前端点击“+”应发请求到
POST /cart/items/{id}/increment,后端调$item->increment('quantity', $step),其中$step默认为 1 - 用户手动输入数量并提交时,走
PUT /cart/items/{id},此时才用update(['quantity' => $request->quantity]),但必须校验$request->quantity >= 1且不超过 SKU 库存 - 所有更新操作后,立即重新计算该条目的小计(
quantity * sku.price)并缓存到 session 或响应体中,避免前端二次计算出错
清空购物车时 delete() 为什么没删干净?
用 CartItem::where('user_id', auth()->id())->delete() 看似没问题,但如果用户刚登录、购物车数据还在 session 里没同步到数据库,就会漏删;反之,如果用户登出又登录,session 里还残留旧数据,下次加购会叠加。
清空动作必须同时清理两处:数据库 + session。顺序很重要——先删数据库,再清 session,否则删完数据库后 session 还有数据,下次访问又会刷回数据库。
- 封装一个
CartService::flush()方法,内部执行CartItem::where(...)->delete()+session()->forget('cart') - 别依赖中间件自动清空:比如用户换账号登录时,不能靠
Auth::logout()后的 session 清理,因为购物车属于业务数据,应显式调用 flush - 测试时故意在未登录状态下加 2 个商品,再登录、清空、登出、重新登录,验证是否真的空了——这是最容易漏掉的边界











