Laravel:对比集合判断用户已购产品的最优方案——whereIn?
高效判断用户已拥有产品的最优方案
嘿,针对你这个需求,核心思路就是把判断逻辑提前到数据库查询阶段,绝对别把用户所有已购产品都加载到内存里——那可是性能杀手。下面给你两种靠谱的方案,优先选第一种:
1. 数据库层面直接关联标记(最推荐)
利用SQL的左连接,在查询分类下产品的同时,直接关联products_user表并过滤当前用户ID,让每条产品自带一个“是否已购买”的标记。这样PHP拿到数据就能直接用,完全不用额外处理。
举个Laravel Eloquent的例子(假设你用的是Laravel)
先确认模型关联是对的:
Category和Product是一对多(hasMany)Product和User通过products_user表做关联(belongsToMany)
然后这么写查询:
$currentUserId = auth()->id(); // 拿当前登录用户ID $categories = Category::with(['products' => function ($query) use ($currentUserId) { // 左连接用户-产品关联表,只筛选当前用户的记录 $query->leftJoin('products_user', function ($join) use ($currentUserId) { $join->on('products.id', '=', 'products_user.product_id') ->where('products_user.user_id', '=', $currentUserId); }) // 加个is_bought字段,有记录就是1(已买),没有就是0(未买) ->select('products.*', \DB::raw('CASE WHEN products_user.user_id IS NOT NULL THEN 1 ELSE 0 END AS is_bought')); })->get();
之后遍历产品的时候,直接看$product->is_bought的值:是1就显示“Bought”按钮,是0就显示“Buy”——完美,全程不用加载用户的已购产品集合,内存占用极低。
2. 批量拿已购产品ID(次优选择)
如果不想改主查询的关联逻辑,那退一步,只查用户已购产品的ID集合(注意是ID,不是整个产品模型),这个集合的内存占用比加载一堆模型小太多了:
$currentUserId = auth()->id(); // 只拉ID,内存占用可以忽略不计 $ownedProductIds = DB::table('products_user') ->where('user_id', $currentUserId) ->pluck('product_id') ->toArray(); // 正常查分类和产品 $categories = Category::with('products')->get(); // 遍历的时候判断ID在不在数组里 foreach ($categories as $category) { foreach ($category->products as $product) { $isBought = in_array($product->id, $ownedProductIds); // 渲染按钮的逻辑就靠这个变量啦 } }
这种方式比加载全部已购产品模型高效很多,但还是不如第一种——毕竟第一种是数据库直接帮我们做好判断,PHP只需要用结果就行。
绝对要避开的坑
千万别这么写,用户买的产品多了绝对卡爆:
// 大错特错!会把用户所有已购产品模型都加载到内存里,数量多的时候内存直接起飞 $ownedProducts = auth()->user()->products; foreach ($categories->products as $product) { if ($ownedProducts->contains($product)) { // ... } }
contains方法每次判断都要遍历一遍已购产品集合,产品多的话这个时间复杂度会爆炸,内存占用更是离谱,绝对要避免。
总结一下,第一种数据库左连接加标记字段的方案是最高效的,把压力甩给数据库(它本来就擅长做这类关联判断),PHP这边轻松拿结果用就行,完全不用担内存问题。
内容的提问来源于stack exchange,提问作者Hans M.
相关产品推荐
相关产品推荐

