Laravel多态多对多关联(含中间表)配置问题求助
Laravel 多态关联配置问题
现有表结构
- shops(id, name, ...)
- shopables(id, shop_id, shopable_type, shopable_id, price, count, ...)(另外,是否应该将其重命名为good_shop(good_type, good_id)?)
- parts(id, name, ...)
- items(id, name, ...)
- ASICs(id, name, ...)
注意:shopable_type字段存储的是part、item、ASIC,而非完整类名(如App\Part)。
需求
需要在模型中实现以下关联方法:
- Part、Item、ASIC模型:
shops()方法(关联对应的店铺) - Shop模型:
goods()方法(关联所有类型的商品:parts、items、ASICs)
现有代码问题
尝试配置关联但无法成功,以下是现有模型代码:
Shop 模型
<?php namespace App\Models; use App\Models\traits\HasImage; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; class Shop extends Model { use HasFactory; use HasImage; protected $fillable = ['name', 'description', 'type', 'slug', 'delivery_time', 'warranty']; // public function parts() // { // return $this // ->belongsToMany(Part::class) // ->using(GoodShopPivot::class) // ->withPivot('id', 'count'); // } // // public function items() // { // return $this // ->belongsToMany(Item::class) // ->using(GoodShopPivot::class) // ->withPivot('id', 'count', 'price', 'currency'); // } public function shopables() { Relation::morphMap([ 'part' => 'App\Part', 'item' => 'App\Item', 'asic' => 'App\ASIC', ]); return $this->morphTo('shopable'); } }
Part 模型(Item、ASIC模型结构类似)
<?php namespace App\Models; class Part extends Model { use HasFactory; use SoftDeletes; protected $dates = ['deleted_at']; /** * The attributes that should be hidden for arrays. */ protected $hidden = ['created_at', 'updated_at', 'GPU_real_hashrate', 'real_temperature', 'PSU_breaks_chance']; protected $guarded = ['id']; // public function shops() // { // return $this // ->belongsToMany(Shop::class) // ->withPivot(['count', 'price']) // ->using(GoodShopPivot::class); // } // public function shops() { // return $this->morphToMany('App\Shop', 'shopable')->withPivot('price', 'count', 'currency'); return $this ->morphTo(Shop::class, 'shopable') ; } }
解决方案
1. 表名建议
shopables表的命名符合Laravel多态关联的常规约定({关联名}ables),如果重命名为good_shop,需要手动指定表名增加配置成本,建议保留原表名。
2. 模型关联配置修正
Shop 模型:实现goods()方法
这里需要使用多对多多态关联(morphToMany),因为一个店铺对应多个不同类型的商品,同时一个商品也可属于多个店铺:
<?php namespace App\Models; use App\Models\traits\HasImage; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; class Shop extends Model { use HasFactory; use HasImage; protected $fillable = ['name', 'description', 'type', 'slug', 'delivery_time', 'warranty']; public function goods() { // 多对多多态关联,关联所有商品类型 return $this->morphToMany( \App\Models\Model::class, 'shopable', 'shopables', 'shop_id', 'shopable_id' )->withPivot('price', 'count', 'currency'); // 携带中间表字段 } }
Part 模型:实现shops()方法(Item、ASIC模型同理)
反向关联店铺,同样使用morphToMany:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Part extends Model { use HasFactory; use SoftDeletes; protected $dates = ['deleted_at']; protected $hidden = ['created_at', 'updated_at', 'GPU_real_hashrate', 'real_temperature', 'PSU_breaks_chance']; protected $guarded = ['id']; public function shops() { return $this->morphToMany( \App\Models\Shop::class, 'shopable', 'shopables', 'shopable_id', 'shop_id' )->withPivot('price', 'count', 'currency'); } }
3. 全局多态映射优化
将Relation::morphMap配置放在App\Providers\AppServiceProvider的boot()方法中,全局生效避免重复定义:
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Database\Eloquent\Relations\Relation; class AppServiceProvider extends ServiceProvider { public function register(): void { // } public function boot(): void { Relation::morphMap([ 'part' => \App\Models\Part::class, 'item' => \App\Models\Item::class, 'asic' => \App\Models\ASIC::class, ]); } }
4. 使用示例
// 获取某个店铺的所有商品 $shop = Shop::find(1); $goods = $shop->goods; // 返回包含Part、Item、ASIC的集合 // 获取某个零件所属的所有店铺 $part = Part::find(1); $shops = $part->shops; // 返回关联店铺集合,可通过$shop->pivot访问price、count等字段
内容的提问来源于stack exchange,提问作者Влад Рогов
相关产品推荐
相关产品推荐

