Laravel 9中如何获取特定Vendor的下单Customer?
问题描述
我正在开发一个支持多商家(Vendor)售卖商品的应用,需要筛选出在特定商家处下单的客户(Customer)。现有三个模型:Vendor、Customer、Order,关联关系如下:
- Vendor
hasManyOrders - Order
belongsToVendor - Order
belongsToCustomer - Customer
hasManyOrders
模型代码如下:
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
相关产品推荐
相关产品推荐

