Laravel 5.6中中间表含双外键的HasManyThrough关联实现问询
在Laravel 5.6中实现HasManyThrough关联(中间表持有双外键)
没问题,我来帮你搞定这个关联配置!先理清楚你的场景:你有三个模型——Carrier(承运商)、ShippingZone(配送区域)、ShippingRate(配送费率),其中ShippingRate是中间表,同时持有另外两个模型的外键。我们一步步来实现:
第一步:补全迁移代码
你的shipping_rates迁移缺少关联另外两个表的外键字段,先把它补全:
Schema::create('carriers', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); // 建议加上时间戳字段,符合Laravel默认规范 }); Schema::create('shipping_zones', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); Schema::create('shipping_rates', function (Blueprint $table) { $table->increments('id'); $table->string('name'); // 关联Carrier的外键 $table->unsignedInteger('carrier_id'); $table->foreign('carrier_id')->references('id')->on('carriers')->onDelete('cascade'); // 关联ShippingZone的外键 $table->unsignedInteger('shipping_zone_id'); $table->foreign('shipping_zone_id')->references('id')->on('shipping_zones')->onDelete('cascade'); // 可添加费率相关业务字段,比如价格 $table->decimal('price', 8, 2); $table->timestamps(); });
第二步:定义模型关联
接下来在三个模型里配置关联关系,重点是HasManyThrough的参数要对应正确:
1. Carrier模型(关联到ShippingZone)
通过ShippingRate中间表,获取该承运商对应的所有配送区域:
namespace App; use Illuminate\Database\Eloquent\Model; class Carrier extends Model { // 获取该承运商的所有配送费率记录 public function shippingRates() { return $this->hasMany(ShippingRate::class); } // 通过配送费率表,关联到配送区域 public function shippingZones() { return $this->hasManyThrough( ShippingZone::class, // 目标模型 ShippingRate::class, // 中间模型 'carrier_id', // 中间表中关联当前模型的外键 'id', // 目标模型的主键 'id', // 当前模型的主键 'shipping_zone_id' // 中间表中关联目标模型的外键 ); } }
2. ShippingZone模型(反向关联到Carrier)
如果需要从配送区域反向获取所有支持它的承运商,同样用HasManyThrough:
namespace App; use Illuminate\Database\Eloquent\Model; class ShippingZone extends Model { // 获取该区域的所有配送费率记录 public function shippingRates() { return $this->hasMany(ShippingRate::class); } // 通过配送费率表,关联到承运商 public function carriers() { return $this->hasManyThrough( Carrier::class, // 目标模型 ShippingRate::class, // 中间模型 'shipping_zone_id', // 中间表中关联当前模型的外键 'id', // 目标模型的主键 'id', // 当前模型的主键 'carrier_id' // 中间表中关联目标模型的外键 ); } }
3. ShippingRate模型(基础关联)
这个中间模型只需要定义两个belongsTo关联即可:
namespace App; use Illuminate\Database\Eloquent\Model; class ShippingRate extends Model { public function carrier() { return $this->belongsTo(Carrier::class); } public function shippingZone() { return $this->belongsTo(ShippingZone::class); } }
第三步:使用关联
配置好之后,你就可以轻松调用这些关联了:
// 获取ID为1的承运商的所有配送区域 $carrier = Carrier::find(1); $zones = $carrier->shippingZones; // 带条件查询:获取承运商1的所有名称包含"北美"的配送区域 $filteredZones = $carrier->shippingZones()->where('name', 'like', '%北美%')->get(); // 获取ID为2的配送区域的所有承运商 $zone = ShippingZone::find(2); $carriers = $zone->carriers;
注意事项
HasManyThrough的参数顺序很重要,一定要对应好各个外键和主键的字段名,否则关联会失败;- 如果需要同时获取中间表的字段(比如配送价格),可以在关联里添加
select语句:return $this->hasManyThrough( // ... 其他参数 )->select('shipping_zones.*', 'shipping_rates.price as rate_price'); - 建议给所有表加上
timestamps字段,符合Laravel的默认规范,方便后续维护。
内容的提问来源于stack exchange,提问作者Munna Khan
相关产品推荐
相关产品推荐

