约束嵌套的第3级关系

时间:2017-11-17 10:24:31

标签: laravel laravel-5 eloquent laravel-eloquent laravel-5.5

我正在使用预先加载来构建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,如果可能的话,我更喜欢雄辩的方法。我一直在寻找一个类似问题的其他问题,答案和评论,但我没有运气,因此我发布了这个并寻求你的想法。谢谢

3 个答案:

答案 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;
}