You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel查询构建器实现一对多关联单条附件关联问题

问题描述

我有一个products表,通过model_has_attachments表与attachments表关联。需要通过查询构建器为每个产品记录关联其第一条附件,但当前查询仅返回少量带有model_has_attachments ID的记录,其余均为null。

原查询代码:

$products = DB::table('products')->
leftJoin(DB::raw('(select `model_id`, `attachment_id` from model_has_attachments where model_has_attachments.model_id = id) as model_has_attachments'), 'model_has_attachments.model_id', 'products.id')->
leftJoin('attachments', 'model_has_attachments.attachment_id', '=', 'attachments.id')->
            select('products.id', 'products.square', 'products.height', 'products.address', 'products.rooms', 'products.title', 'products.description', 'model_has_attachments.model_id as id_model', 'model_has_attachments.attachment_id')->
            where([
            ['products.deleted_at', '=', null],
        ]);

尝试在DB::raw中添加limit 1,仅返回products表的第一条记录,而非关联后的结果。另外尝试直接关联model_has_attachments表的方式,会获取所有附件记录,导致产品有多个附件时出现重复的产品记录,末尾加->limit(1)也无效:

leftJoin('model_has_attachments', function ($join) {
    $join->on('products.id', '=', 'model_has_attachments.model_id')->where('model_has_attachments.model_type', '=', Product::class);
})->
问题分析与解决方案

问题根源

  1. 子查询条件错误:子查询里的where model_has_attachments.model_id = id未指定表前缀,数据库会将id解析为model_has_attachments表的id字段,而非外层products表的id,导致仅能匹配到model_has_attachments.id = model_has_attachments.model_id的极少记录。
  2. 全局limit错误:直接在子查询加limit 1会让子查询仅返回单条记录,关联后只有该记录对应的产品能匹配,其他产品无法关联附件。
  3. 直接关联导致重复:未筛选单条附件就关联model_has_attachments表,每个附件都会生成一条产品记录,造成产品重复。

正确实现方式

方式一:查询构建器子查询筛选单条附件

先通过子查询筛选出每个产品对应的第一条附件,再关联主查询:

$products = DB::table('products')
    ->leftJoin(
        DB::raw('(
            SELECT model_id, attachment_id 
            FROM model_has_attachments 
            WHERE model_type = ? 
            ORDER BY id ASC -- 按附件ID排序取第一条,可替换为created_at等字段
            GROUP BY model_id
        ) as first_attachments'),
        'first_attachments.model_id', '=', 'products.id'
    )
    ->leftJoin('attachments', 'first_attachments.attachment_id', '=', 'attachments.id')
    ->select(
        'products.id', 'products.square', 'products.height', 
        'products.address', 'products.rooms', 'products.title', 
        'products.description', 'first_attachments.model_id as id_model', 
        'attachments.id as attachment_id', 'attachments.file_name' // 按需选择附件字段
    )
    ->whereNull('products.deleted_at')
    ->setBindings([Product::class])
    ->get();

方式二:Laravel模型关联查询(更简洁)

先在Product模型中定义多态关联:

// app/Models/Product.php
public function attachments()
{
    return $this->morphToMany(Attachment::class, 'model', 'model_has_attachments');
}

查询时通过关联闭包限制只取第一条附件:

$products = Product::with(['attachments' => function ($query) {
    $query->take(1);
}])
->whereNull('deleted_at')
->get();

内容的提问来源于stack exchange,提问作者RedEclipse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:01:33