我想知道是否有人可以提供帮助,因为我已经撞墙并仍在学习Laravel ORM。我可以解释为什么,当我跑:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id');
}
我得到这个结果集:(剪断......)
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": [
{
"user_id": 1,
"location_tag_id": 1,
"id": 1,
"created_at": "2015-05-13 13:06:28",
"updated_at": "2015-05-13 13:06:28",
"name": "Test Tag 0",
"location_id": 1,
"deleted_at": null
},
{
"user_id": 1,
"location_tag_id": 2,
"id": 2,
"created_at": "2015-05-13 11:40:21",
"updated_at": "2015-05-13 12:56:13",
"name": "Test Tag 123",
"location_id": 1,
"deleted_at": null
}
]
}
哪个是王牌!但是,当我开始从location_tags join中选择我想要的列时,使用:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id')
->select('lt.id', 'lt.name');
}
我最终得到了:
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": []
}
有人可以解释发生了什么吗?并且可能指出我正确的方向来限制选择?谢谢!
更新 我也试过了:
$query = \App\User::with(['permissions', 'locationTags' => function($query){
$query->select('lt.id', 'lt.name');
}]);
返回相同的结果:(
答案 0 :(得分:6)
想出来。这里的关键是您必须包含Laravel可用于映射结果集的至少一个键的select()
值。就我而言,它是user_id
,就像这样:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id')
->select('user_id', 'lt.name', 'location_tag_id');
}
然后返回更好的结果集:
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": [
{
"user_id": 1,
"name": "Test Tag 0",
"location_tag_id": 1
},
{
"user_id": 1,
"name": "Test Tag 123",
"location_tag_id": 2
}
]
}
希望将来可以帮助某人,因为它让我猜不到几个小时。
答案 1 :(得分:2)
对不起,你在这里走了一段路。关系定义应该只定义关系。它是一个强大的功能,支持ORM的许多其他方面。你在这里做的或多或少是构建自定义查询,严重限制了关系的有效性。
直到最近,正确的方式看起来像这样。
// class User
public function userHasLocationTags() {
$this->hasMany('App\UserHasLocationTags', 'user_id');
}
// class UserHasLocationTags
public function locationTags() {
$this->hasMany('App\LocationTags', 'location_tag_id');
}
你会急切地加载所有结果。
$user = User::where('id', 1)->with('userHasLocationTags.locationTags')->first();
上面的代码产生了3个查询。一个获取User,一个获取所有UserHasLocationTags,一个获取所有LocationTags。这可能看起来很浪费,但请考虑以下几点。
$users = User::take(100)->with('userHasLocationTags.locationTags')->get();
同样,这只是3个查询,但现在您已经为100个用户加载了所有位置标记。
但我可以看到你是一个注重效率的人,加载所有中间关系和整个嵌套层次结构可能并不适合你。好消息! Laravel 5为这种情况添加了另一种关系类型。 hasManyThrough(向下滚动以找到它)。
有很多通过
“有很多通过”关系为通过中间关系访问远距离关系提供了方便的捷径。例如,Country模型可能有很多Post通过User模型。
所以在你的情况下它可能看起来像这样......
// class User
public function locationTags()
{
return $this->hasManyThrough('App\LocationTags', 'App\UserHasLocationTags');
}
$users = User::take(100)->with('locationTags')->get();
现在我们归结为两个问题。如果您认为需要进一步优化此选项并仅选择特定列,则可以修改预先加载。
$users = User::take(100)->with(['locationTags' => function ($query)
{
$query->select('user_id', 'name', 'location_tag_id');
})->get();
如果你经常这样做,你应该把它包裹在scope中。