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

Laravel 9中如何获取特定Vendor的下单Customer?

问题描述

我正在开发一个支持多商家(Vendor)售卖商品的应用,需要筛选出在特定商家处下单的客户(Customer)。现有三个模型:Vendor、Customer、Order,关联关系如下:

  • Vendor hasMany Orders
  • Order belongsTo Vendor
  • Order belongsTo Customer
  • Customer hasMany Orders

模型代码如下:

Vendor 模型

class Vendor extends Model
{
    /**
     * @return HasMany
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }
}

Order 模型

class Order extends Model
{
    /**
     * @return BelongsTo
     */
    public function customer() : BelongsTo
    {
        return $this->belongsTo(Customer::class);
    }

    /**
     * @return BelongsTo
     */
    public function vendor() : BelongsTo
    {
        return $this->belongsTo(Vendor::class);
    }
}

Customer 模型

class Customer extends Model
{
    /**
     * @return HasMany
     */
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }
}

我尝试通过 hasManyThrough 关联获取商家的所有下单客户,但无法正常工作:

class Vendor extends Model
{
    /**
     * @return HasManyThrough
     */
    public function customers(): HasManyThrough
    {
        return $this->hasManyThrough(Customer::class, Order::class);
    }
}

补充:相关迁移文件如下

Vendor 迁移

Schema::create('vendors', function (Blueprint $table) {
    $table->id();
    $table->string('name')->nullable();
    $table->string('logo')->nullable();
    $table->string('business_type', 32);
    $table->string('address', 128)->nullable();
    $table->string('postcode', 16);
    $table->bigInteger('town_id')->unsigned()->index();
    $table->string('website', 128)->nullable();
    $table->string('telephone', 64)->nullable();
    $table->tinyInteger('approved')->default(0);
    $table->timestamps();
});

Customer 迁移

Schema::create('customers', function (Blueprint $table) {
    $table->id();
    $table->string('email')->unique();
    $table->string('first_name');
    $table->string('last_name');
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->boolean('is_active');
    $table->string('country');
    $table->timestamps();
});

Order 迁移

Schema::create('orders', function (Blueprint $table) {
    $table->id();

    $table->string('status')->index();
    $table->bigInteger('customer_id')->unsigned()->nullable()->index();
    $table->bigInteger('vendor_id')->unsigned()->index()->nullable();
    $table->string('reference')->nullable()->unique()->index();
    $table->decimal('sub_total')->default(0);
    $table->decimal('discount_total')->default(0);
    $table->decimal('shipping_total')->default(0);
    $table->decimal('tax_total')->default(0);
    $table->decimal('grand_total')->default(0);
    $table->timestamps();
});

解决方案

方案1:正确配置 hasManyThrough 关联

hasManyThrough 无法正常工作是因为默认的外键匹配不符合表结构,手动指定关联参数即可:

class Vendor extends Model
{
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * @return HasManyThrough
     */
    public function customers(): HasManyThrough
    {
        return $this->hasManyThrough(
            Customer::class,
            Order::class,
            'vendor_id',       // 中间表orders关联vendors的外键
            'id',              // customers表的主键
            'id',              // vendors表的主键
            'customer_id'      // 中间表orders关联customers的外键
        )->distinct(); // 去重,避免同一客户多次下单被重复返回
    }
}

使用方式:

// 获取指定商家的所有下单客户
$customers = Vendor::find($vendorId)->customers;

方案2:用多对多关联(belongsToMany)实现

商家与客户通过订单形成多对多关系,直接定义 belongsToMany 关联更贴合业务逻辑:

class Vendor extends Model
{
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * @return BelongsToMany
     */
    public function customers(): BelongsToMany
    {
        return $this->belongsToMany(
            Customer::class,
            'orders',          // 中间表名称
            'vendor_id',       // 中间表关联vendors的外键
            'customer_id',     // 中间表关联customers的外键
            'id',              // vendors表主键
            'id'               // customers表主键
        )->distinct();        // 去重,排除同一客户的重复记录
    }
}

使用方式:

$customers = Vendor::find($vendorId)->customers;

方案3:通过关联查询获取(集合层面处理)

如果不想修改模型关联,可通过订单关联间接提取客户,这种方式在集合层面处理,性能略逊于前两种:

// 获取指定商家的订单并预加载客户,提取后去重
$customers = Vendor::find($vendorId)
    ->orders()
    ->with('customer')
    ->get()
    ->pluck('customer')
    ->filter() // 过滤customer_id为null的无效记录
    ->unique('id');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 16:42:51