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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:49:48