如何通过中间Country表实现Plan模型的belongsToThrough关联获取Currency
嘿,我来帮你搞定这个关联问题!你想要实现的是通过Country表间接关联Plan和Currency,这属于跨中间表的关联场景,Laravel原生的belongsTo直接用肯定不行——因为它是做直接关联的,没法跳过中间的Country表,这就是你之前代码返回null的原因。
先明确下咱们的数据库结构(避免理解偏差):
plans表:id(整数)、country_id(字符串,对应countries.code)countries表:code(字符串)、currency_id(字符串,对应currencies.code)currencies表:code(字符串)
下面给你两种可行的解决方案:
方案一:用Laravel原生hasOneThrough实现间接关联
这是最正统的方式,因为咱们是从Plan出发,经过Country拿到对应的Currency,刚好符合hasOneThrough的使用场景。在Plan模型里这么定义关联:
class Plan extends \Illuminate\Database\Eloquent\Model { public function currency() { return $this->hasOneThrough( Currency::class, // 最终要关联的目标模型 Country::class, // 中间过渡的模型 'code', // 中间表countries关联主表plans的字段(plans.country_id对应countries.code) 'code', // 目标表currencies关联中间表countries的字段(countries.currency_id对应currencies.code) 'country_id', // 主表plans的关联字段 'currency_id' // 中间表countries的关联字段 ); } }
我给你拆解下参数逻辑,避免搞混:
- 前两个参数是模型顺序:先写最终要拿的模型,再写中间过渡的模型
- 后面四个参数是字段对应关系:从中间表到主表,再到目标表到中间表,最后是主表和中间表的关联字段
定义好之后,直接用$plan->currency就能拿到对应的Currency模型啦,预加载也可以正常用:Plan::with('currency')->get()
方案二:分步关联(适合不想记复杂参数的情况)
如果觉得hasOneThrough的参数太绕,也可以先给Plan定义Country的关联,再通过Country拿到Currency,封装成一个方法:
class Plan extends \Illuminate\Database\Eloquent\Model { // 先定义Plan到Country的直接关联 public function country() { return $this->belongsTo(Country::class, 'country_id', 'code'); } // 封装拿到Currency的方法 public function currency() { return $this->country->currency; } }
不过这种方式要注意预加载的写法,需要先加载Country再加载Currency:
$plans = Plan::with('country.currency')->get(); foreach ($plans as $plan) { // 这里就能正常访问$plan->currency了 echo $plan->currency->code; }
为啥你之前的代码返回null?
你之前直接用belongsTo(Currency::class, 'currency_id', 'code', 'country'),但plans表本身根本没有currency_id字段啊!这个字段在countries表里,belongsTo是直接关联当前模型的表和目标模型的表,没法跳过中间表,自然找不到数据返回null啦。
现在试试上面的方案,应该就能正常拿到Currency了!
内容的提问来源于stack exchange,提问作者mikelovelyuk

