使用 Laravel 的路由模型绑定功能来处理关联模型。

37
我想知道使用laravel的路由模型绑定是否可以返回关系数据?
假设我有一个用户模型,并且有一个'friends'与其他用户相关联的关系,我想从路由或控制器返回用户信息和关系。
例如,对于路由domain.tld/user/123
Route::model('user', 'User');

Route::get('/user/{user}', function(User $user) {

    return Response::json($user);

});

这将返回用户信息,但我也想获取关系,有没有简单/适当的方式可以实现这一点?

我知道我可以这样做


Route::get('/user/{user}', function((User $user) {

    return Response::json(User::find($user['id'])->with('friends')->get());

});
Route::get('/user/{id}', function(($id) {

   return Response::json(User::find($id)->with('friends')->get());

});

不过我怀疑可能有更好的方法。

3个回答

66

不要像Matt Burrow建议的那样,在每个查询上急切加载关系,仅仅为了在一个上下文中可用。这是低效的。

相反,在您的控制器操作中,当您需要它们时,可以“按需”加载关系。因此,如果您使用路由模型绑定将User实例提供给控制器操作,但您还想要friends关系,则可以执行以下操作:

class UserController extends Controller
{
    public function show(User $user)
    {
        $user->load('friends');

        return view('user.show', compact('user'));
    }
}

编辑: 您还可以使用loadMissing有条件地加载关系。仅在模型上尚未加载关系时才会加载该关系:

class UserController extends Controller
{
    public function show(User $user)
    {
        // If friends relation has already been loaded, will be a no-op
        $user->loadMissing('friends');

        return view('user.show', compact('user'));
    }
}

谢谢您。您还可以像使用 with 一样使用 load,例如作为更复杂查询的一部分,如:return $module->load(['questions', 'questions.results' => function($q) use($user){ return $q->where('user_id', $user->id); }]); - Djave
8
load() 方法用于在查询完成后加载关联数据,而 with() 方法则是将关联数据作为查询的一部分包含进来。 - Martin Bean
根据问题,确保受保护的 $with 变量是值得回答的,但就应用性能而言,这才是我们应该遵循的。仅在需要时显式加载它。 - iamawesome

39

您可以在用户模型中填充$with属性。像这样:

protected $with = ['friends'];

这将自动为您载入关系数据。

请注意:这将对每个用户模型查询都执行此操作。

如果您不想始终加载好友,则可以将其绑定到路由中的参数上,如下所示:

Route::bind('user_id', function($id) {
    return User::with('friends')->findOrFail($id);
});

Route::get('/user/{user_id}', 'BlogController@viewPost');

1
感谢您,这行代码 protected $with = ['friends']; 救了我一命 (: - Tarik Manoar

7
马丁·宾的回答可能不是您想要解决这个问题的方式,因为它会向您的控制器引入一个n+1:
1)必须通过路由模型绑定加载User,然后...
2)现在加载friends的关系
但他是正确的,您可能不希望每次都加载关系。
这就是为什么马特·伯罗的解决方案可能更好(他绑定了不同的值:而不是{user},您可以使用类似{user_with_friends}的内容,并将其与{user}分开绑定...)
就我个人而言,如果您只需要为该路由加载friends,我会简单地传递$userId(不进行绑定),并以以下方式开始控制器方法: $user = User::with('friends')->findOrFail($userId); 您可以让Laravel自动处理ModelNotFoundException(就像它对路由模型绑定一样),或者将其包装在try/catch中。

1
Martin Bean的回答可能不是您想要解决此问题的方式,因为它会向您的控制器引入n+1。不,它并没有。 - Martin Bean
当我们谈论路由模型绑定时,N+1问题会如何被引入 - 即:只返回单个模型。当只有一个$user时,$user->load('friends')肯定只会加载相关的朋友一次,不会出现N+1问题。 - Joseph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接