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

Laravel多对多关系下GROUP BY与OR条件的查询实现

Laravel 多对多关系查询:筛选指定供应商为唯一供应商或首选供应商的商品

需求说明

需要查询满足以下任一条件的商品(Articoli):

  • 指定供应商(fornitore_id)是该商品的唯一供应商
  • 指定供应商是该商品的首选供应商(关联表中preferito字段值为"si")

表结构

商品表(articoli)商品供应商关联表(articolo_fornitore)供应商表(fornitori)
ID(主键)ID(主键)ID(主键)
descr(商品描述)articolo_id(商品ID)rag_soc(公司名称)
pos(存放位置)fornitore_id(供应商ID)sito(官网地址)
prezzo(售价)cod_art_for(供应商商品编码)iva(增值税号)
note(备注)preferito(是否首选:"si"/"no")note(供应商备注)

模型定义代码

商品模型(Articoli)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Articoli extends Model
{
    protected $table = 'articoli';

    protected $fillable = [
        'cod_articolo',
        'descrizione',
        'quantita_minima',
        'note',
        'posizione',
        'prezzo_vendita',
    ];

    // 关联商品供应商关联表(一对多)
    public function articolo_fornitore()
    {
        return $this->hasMany(ArticoloFornitore::class);
    }
}

商品供应商关联模型(ArticoloFornitore)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class ArticoloFornitore extends Model
{
    protected $table = 'articolo_fornitore';

    protected $fillable = [
        'articolo_id',
        'fornitore_id',
        'cod_articolo_fornitore',
        'quantita_giacente',
        'preferito',
    ];

    // 关联商品表
    public function articolo()
    {
        return $this->belongsTo(Articoli::class, 'articolo_id');
    }

    // 关联供应商表
    public function fornitore()
    {
        return $this->belongsTo(Fornitore::class, 'fornitore_id');
    }
}

供应商模型(Fornitore)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Fornitore extends Model
{
    protected $table = 'fornitori';

    protected $fillable = [
        'id',
        'rag_sociale',
        'pec',
        'sito',
        'p_iva',
        'cod_fiscale',
        'note',
        'mod_pagamento_id',
    ];

    // 关联商品供应商关联表(一对多)
    public function articolo_fornitore()
    {
        return $this->hasMany(ArticoloFornitore::class);
    }
}

已验证的原生SQL查询

SELECT 
    articoli.id, articoli.cod_articolo 
FROM 
    articoli 
INNER JOIN 
    articolo_fornitore on articoli.id = articolo_fornitore.articolo_id 
INNER JOIN 
    fornitori ON articolo_fornitore.fornitore_id = fornitori.id 
WHERE 
    -- 条件1:指定供应商是该商品的首选供应商
    (articolo_fornitore.fornitore_id = $idfornitore AND 
     articolo_fornitore.preferito = "si") 
    -- 条件2:指定供应商是该商品的唯一供应商
    OR (articolo_fornitore.fornitore_id = $idfornitore 
        AND articoli.id IN (SELECT articoli.id 
                            FROM articoli 
                            INNER JOIN articolo_fornitore ON articoli.id = articolo_fornitore.articolo_id 
                            GROUP BY articoli.id 
                            HAVING COUNT(DISTINCT articolo_fornitore.fornitore_id) = 1)) 

Laravel 适配实现

方式1:使用查询构造器(对应原生SQL)

$idFornitore = 1; // 替换为实际的供应商ID

$articoli = DB::table('articoli')
    ->select('articoli.id', 'articoli.cod_articolo')
    ->join('articolo_fornitore', 'articoli.id', '=', 'articolo_fornitore.articolo_id')
    ->join('fornitori', 'articolo_fornitore.fornitore_id', '=', 'fornitori.id')
    ->where(function ($query) use ($idFornitore) {
        // 条件1:指定供应商是首选
        $query->where('articolo_fornitore.fornitore_id', $idFornitore)
              ->where('articolo_fornitore.preferito', 'si');
    })
    ->orWhere(function ($query) use ($idFornitore) {
        // 条件2:指定供应商是唯一供应商
        $query->where('articolo_fornitore.fornitore_id', $idFornitore)
              ->whereIn('articoli.id', function ($subquery) {
                  $subquery->select('articoli.id')
                           ->from('articoli')
                           ->join('articolo_fornitore', 'articoli.id', '=', 'articolo_fornitore.articolo_id')
                           ->groupBy('articoli.id')
                           ->havingRaw('COUNT(DISTINCT articolo_fornitore.fornitore_id) = 1');
              });
    })
    ->distinct() // 去重,避免同一商品因关联记录被多次查询
    ->get();

方式2:使用Eloquent模型关联查询

$idFornitore = 1;

$articoli = Articoli::whereHas('articolo_fornitore', function ($query) use ($idFornitore) {
    $query->where('fornitore_id', $idFornitore)
          ->where('preferito', 'si');
})
->orWhere(function ($query) use ($idFornitore) {
    // 先筛选出关联了指定供应商的商品
    $query->whereHas('articolo_fornitore', function ($q) use ($idFornitore) {
        $q->where('fornitore_id', $idFornitore);
    })
    // 再筛选出只有一个供应商的商品
    ->whereHas('articolo_fornitore', function ($q) {
        $q->groupBy('articolo_id')
          ->havingRaw('COUNT(DISTINCT fornitore_id) = 1');
    });
})
->select('id', 'cod_articolo')
->distinct()
->get();

补充:优化多对多关联定义

当前模型使用hasMany/belongsTo实现关联,更标准的多对多关系推荐用belongsToMany,可简化后续查询:
在Articoli模型中添加:

public function fornitori()
{
    return $this->belongsToMany(Fornitore::class, 'articolo_fornitore', 'articolo_id', 'fornitore_id')
                ->withPivot('preferito', 'cod_articolo_fornitore');
}

在Fornitore模型中添加:

public function articoli()
{
    return $this->belongsToMany(Articoli::class, 'articolo_fornitore', 'fornitore_id', 'articolo_id')
                ->withPivot('preferito', 'cod_articolo_fornitore');
}

优化后简化查询:

$idFornitore = 1;

$articoli = Articoli::whereHas('fornitori', function ($query) use ($idFornitore) {
    $query->where('fornitori.id', $idFornitore)
          ->where('articolo_fornitore.preferito', 'si');
})
->orWhere(function ($query) use ($idFornitore) {
    $query->whereHas('fornitori', function ($q) use ($idFornitore) {
        $q->where('id', $idFornitore);
    })
    ->whereHas('fornitori', function ($q) {
        $q->groupBy('articoli.id')
          ->havingRaw('COUNT(DISTINCT fornitori.id) = 1');
    });
})
->select('id', 'cod_articolo')
->distinct()
->get();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 19:04:59