Laravel 5.4如何通过hasManyThrough获取用户订阅课程的作业?
嘿,刚好碰到过类似的问题,Laravel 5.4 的 hasManyThrough 确实存在这个局限——它没办法指定中间表(UserCourse)用来关联目标表(Homework)的字段,默认会用中间表的 id 去关联 Homework 的 course_id,这就导致了 SQL 错误。下面给你两个实用的解决方案:
方案一:手动构建查询(简单直接)
如果不需要 Eloquent 关联的高级特性(比如预加载、关联附加等),直接在 User 模型里写一个方法,用查询构造器手动关联表即可:
public function homeworks() { return \App\Homework::join('user_course', 'user_course.course_id', '=', 'homework.course_id') ->where('user_course.user_id', $this->id) ->select('homework.*'); }
使用的时候直接调用:
$user = User::find(1); $userHomeworks = $user->homeworks()->get();
这个方法简单粗暴,马上就能解决问题,唯一的缺点是它返回的是查询构造器实例,不是标准的 Eloquent 关联对象,所以没法用 with('homeworks') 这种预加载方式。
方案二:自定义关联类(支持 Eloquent 特性)
如果需要保留 Eloquent 关联的所有特性(比如预加载、关联计数等),可以自定义一个扩展版的 HasManyThrough 关联类,来弥补 5.4 的参数缺失:
步骤1:创建自定义关联类
在 app/Relations 目录下新建 CustomHasManyThrough.php(如果没有 Relations 目录就自己创建):
namespace App\Relations; use Illuminate\Database\Eloquent\Relations\HasManyThrough; class CustomHasManyThrough extends HasManyThrough { protected $throughForeignKey; public function __construct($query, $farParent, $throughParent, $firstKey, $secondKey, $throughForeignKey) { parent::__construct($query, $farParent, $throughParent, $firstKey, $secondKey); $this->throughForeignKey = $throughForeignKey; } // 重写方法,指定中间表用来关联目标表的字段 protected function getQualifiedForeignKeyName() { return $this->throughParent->getTable() . '.' . $this->throughForeignKey; } }
步骤2:在 User 模型中使用自定义关联
修改 User 模型,引入自定义关联类并定义 homeworks 方法:
use App\Relations\CustomHasManyThrough; use Illuminate\Database\Eloquent\Model; class User extends Model { // ... 其他模型代码 public function homeworks() { // 初始化中间表和目标表的实例 $throughModel = $this->newRelatedInstance(\App\UserCourse::class); $targetQuery = $this->newRelatedInstance(\App\Homework::class)->newQuery(); return new CustomHasManyThrough( $targetQuery, $this, $throughModel, 'user_id', // User表关联UserCourse的字段 'course_id', // Homework表中关联的字段 'course_id' // UserCourse表关联Homework的字段(这就是5.4缺失的参数) ); } }
现在你就可以像使用标准 Eloquent 关联一样操作了:
// 直接获取作业 $user = User::find(1); $homeworks = $user->homeworks; // 预加载作业 $users = User::with('homeworks')->get();
补充说明
Laravel 5.5 确实通过 PR #19114 给 hasManyThrough 添加了 $throughKey 参数,用来指定中间表关联目标表的字段,所以在 5.5+ 里你可以直接这么写:
$this->hasManyThrough('App\Homework', 'App\UserCourse', 'user_id', 'course_id', null, 'course_id');
但 5.4 不支持这个参数,所以只能用上面的两种方法来绕开这个限制。
内容的提问来源于stack exchange,提问作者Denis Rozimovschii

