Laravel 9 Eloquent查询添加子查询:判断用户是否购买商品
解决Laravel 9中Eloquent查询附加用户购买状态的问题
核心问题分析
你之前的尝试主要存在两个误区:一是将Eloquent代码直接写入SQL片段(如selectRaw),数据库无法识别PHP语法;二是子查询写法错误,导致无法关联主查询的商品ID,或者触发了提前执行的查询(如first())。以下是几种可行的解决方案:
方案一:Eloquent子查询(推荐,安全且保持ORM风格)
利用Eloquent的子查询能力,通过addSelect嵌入EXISTS判断,自动处理表关联和参数绑定,避免SQL注入:
$user = $request->user(); $products = Product::query() ->select('id', 'name') ->addSelect([ 'is_bought_by_auth_user' => ProductUser::query() ->where('user_id', $user->id) ->whereColumn('product_id', 'products.id') ->exists() ]) ->get();
whereColumn用于关联主查询的products.id和中间表的product_id,确保每条商品对应正确的判断exists()会生成SQL的EXISTS子查询,返回1(已购买)或0(未购买)的布尔值
方案二:左连接(LEFT JOIN)实现
通过左连接中间表,直接在查询中判断用户是否有购买记录:
$user = $request->user(); $products = Product::query() ->select('products.id', 'products.name') ->selectRaw('IF(product_users.user_id IS NOT NULL, 1, 0) AS is_bought_by_auth_user') ->leftJoin('product_users', function ($join) use ($user) { $join->on('product_users.product_id', '=', 'products.id') ->where('product_users.user_id', $user->id); }) ->groupBy('products.id', 'products.name') ->get();
- 左连接会保留所有商品记录,匹配到的购买记录会有
user_id值,否则为NULL groupBy用于避免同一商品因多次购买产生重复记录
方案三:修正DB::raw写法(不推荐,仅作参考)
如果坚持使用原生SQL片段,需修正表名错误并使用参数绑定避免注入:
$user = $request->user(); $products = Product::query() ->select('id', 'name') ->addSelect(\DB::raw("(EXISTS (SELECT * FROM product_users WHERE product_users.product_id = products.id AND product_users.user_id = ?)) AS is_bought_by_auth_user"), [$user->id]) ->get();
- 注意中间表默认名为
product_users(对应模型ProductUser),之前的product_user是错误的 - 使用
?作为占位符,通过第二个参数传入$user->id,避免SQL注入
之前尝试的错误原因
- 方法1:
selectRaw中直接写入ProductUser::where(...)是PHP代码,不是SQL语法,数据库无法解析,导致语法错误。 - 方法2:
first()会立即执行子查询,而非将其嵌入主查询,应该去掉first(),让Eloquent将其作为子查询表达式处理。 - 方法3:模型中可以使用Auth,但需引入门面
use Illuminate\Support\Facades\Auth;,不过这种写法会让模型依赖请求上下文,不符合单一职责原则,不推荐。
内容的提问来源于stack exchange,提问作者ancarofl6
相关产品推荐
相关产品推荐

