我正在使用预先加载来构建api,因此我可以简单地返回具有深层关系的用户模型,并自动将其转换为json。这是设置。
users
id
..
clients
id
..
user_clients
id
user_id
client_id
..
campaigns
id
..
client_campaigns
id
client_id
campaign_id
..
campaign_activities
id
campaign_id
..
client_campaign_activity_templates
id
campaign_activity_id
client_id *(templates are unique per client)*
..
我已经建立了模型的关系。
User
public function clients() {
return $this->belongsToMany('App\Client','user_clients');
}
Client
public function campaigns() {
return $this->belongsToMany('App\Campaign','client_campaigns');
}
Campaign
public function activities() {
return $this->hasMany('App\CampaignActivity');
}
CampaignActivity
public function templates() {
return $this->hasMany('App\ClientCampaignActivityTemplate')
}
我有一个简单的api端点来提供User对象的JSON,包括使用预先加载的深层关系。
public function getLoggedInUser(Request $request) {
return \App\User::with('clients.campaigns.activities.templates')->find($request->user()->id);
}
使用邮递员测试,我可以让用户包括其深层关系。
{
"user": {
"id": 1,
"name": "user1",
"clients": [
{
"id": 1,
"name": "client1",
"campaigns": [
{
"id": 1,
"name": "campaign1",
"activities": [
{
"id": 1,
"name": "activity1",
"templates": [
{
"id": 1,
"name": "template1 for client1",
"client_id": 1,
"body": "this is a template.",
}, {
"id": 2,
"name": "template1 for client2",
"client_id": 2,
"body": "This is a template for client2"
}
]
}, {
"id": 2,
"name": "activity2",
"templates": []
}, {
"id": 3,
"name": "activity3",
"templates": []
}
]
}
]
}
]
}
}
但是,在user->clients->campaigns->activities->templates
级别,它会列出该活动的所有模板。我知道基于上面模型的关系代码,它应该表现得像那样。
所以问题是如何过滤模板以过滤campaign_activity_id和client_id?
我一直在试验如何过滤模板,因此它只列出该活动的模板和该客户端的模板。我有一个有效的解决方案,但它是N + 1,如果可能的话,我更喜欢雄辩的方法。我一直在寻找一个类似问题的其他问题,答案和评论,但我没有运气,因此我发布了这个并寻求你的想法。谢谢
答案 0 :(得分:1)
我认为你需要的是eager loading constraints。
public function getLoggedInUser(Request $request) {
return \App\User::with('clients.campaigns.activities.templates',
function($query) use($request) {
$client_ids = Client::whereHas('users', function($q) use($request){
$q->where('id', $request->user()->id);
})->pluck('id');
$query->whereIn('templates.client_id', $client_ids);
})->find($request->user()->id);
}
未经测试,但只需要一个额外的查询。
我正在做的是:为您的预先加载定义约束,即仅显示客户ID列表(client_id
)中具有pluck
的模板,其中包含与用户。
答案 1 :(得分:0)
尝试使用闭包来过滤相关模型:
$users = App\User::with([
'clients' => function ($query) {
$query->where('id', $id);
},
'clients.campaigns' => function ($query) {
$query->where('id', $id);
}
])->get();
答案 2 :(得分:0)
这是我的工作解决方案,但如果你们有更好的方法,我仍然感兴趣。
在CampaignActivity
模型上,我添加了一个公共属性client_id
并将关系代码修改为
CampaignActivity
public $client_id = 0
public function templates() {
return $this->hasMany('App\ClientCampaignActivityTemplate')->where('client_id', $this->client_id);
}
并且在我的控制器上,仅将热切加载限制为活动(实际上,在这种情况下,使用预先加载[9]执行更多sqls而不仅仅是迭代[7],而且急切加载也没有任何意义因为我们正在迭代大声笑)
public function getLoggedInUser(Request $request) {
foreach ($user->clients as $client)
foreach( $client->campaigns as $campaign)
foreach ($campaign->activities as $activity) {
$activity->client_id = $client->id;
$activity->templates; //to load the values
}
return $user;
}