多对多关联如何把abc三张表的请求数据用更优雅的语句合并在一起,提高获取数据的速度

1. 运行环境

1). 当前使用的 Laravel 版本?

laravel 10.9

2. 问题描述?

当前有三张表以及两张中间表:

  • appsApp相关信息
  • navigationsApp操作系统分类。例如:安卓、电脑、Ios
    app_navigation主要字段有app_idnavigation_id
  • classifications主要字段有navigation_idApp的应用场景分类。例如:游戏、影视、音乐
    app_classification主要字段有app_idclassification_id

现在需要根据navigations操作系统的分类,获取到所有属于当前navigations操作系统分类的apps信息,以及包含每个App的classifications应用场景的分类。
App Model代码:

public function navigations():BelongsToMany{
    return $this->belongsToMany(Navigation::class)->withPivot('version','size','newdate','url','download','content','log','operatingsystem');
}
public function classifications():BelongsToMany{
    return $this->belongsToMany(Classification::class);
}

Navigation Model代码:

public function apps():BelongsToMany{
    return $this->belongsToMany(App::class)->withPivot('version','size','newdate')->orderByPivot('newdate', 'desc');
}

Classification Model代码:

public function apps():BelongsToMany{
    return $this->belongsToMany(App::class);
}

在这个问题提出前,我尝试过两种方法,获取数据的速度特别慢。

  1. 先获取到用户访问的navigations分类,再遍历每一个App模型的classifications()方法,按照 10000条数据计算,这个方式我需要等待 20秒才能获取到数据。
  2. 分别获取navigationsclassifications共同的apps()模型方法。再通过循环进行数据拼接,这个方式我需要等待 6秒才能获取到数据。代码如下所示:
    $navigation=Navigation::where('id',$navigation->id)->first();
    $apps=$navigation->apps()->get(['apps.id','good','author','name','popular','weights'])-toArray();//获取当前操作系统所有的app
    $classifications=Classification::where('navigation_id',$navigation->id)->get();//获取当前操作系统所有的分类
    foreach($classifications as $classification){
     foreach($classification->apps()->get(['apps.id'])->toArray() as $app){
         $temp[]=$app;
     }
    }
    foreach($apps as $app){
     $classificationId=[];
     for($i=0;$i<count($temp);$i++){
         if($temp[$i]['pivot']['app_id']===$app['id']){
             $classificationId[]=$temp[$i]['pivot']['classification_id'];
             unset($temp[$i]);
             $temp = array_values($temp);
         }
     }
     $app['classifications']=$classificationId;
     $data['apps'][]=$app;
    }

请求到的数据:

如何才能通过更加优雅的方法让速度提升上去,谢谢大佬赐教

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

使用with()关联,只需要触发一次数据库连接。时间从本地的 6秒钟降到1.2秒。如果在服务器的话,用时更低。下面的代码注释是之前尝试过的方法2。

$navigation=Navigation::where('id',$navigation->id)->first();
$apps=$navigation->apps()->with(['classifications'=>function($query) use($navigation){
$query->where('navigation_id',$navigation->id)->get(['classifications.id']);
}])->get(['apps.id','good','author','name','popular','weights']);//获取当前操作系统所有的app

 /*foreach($navigation->classifications as $classification){
            foreach($classification->apps()->get(['apps.id']) as $app){
                $temp[]=$app;
            }
        }
        foreach($apps as $app){
            $classificationId=[];
            for($i=0;$i<count($temp);$i++){
                if($temp[$i]->pivot->app_id===$app->id){
                    $classificationId[]=$temp[$i]->pivot->classification_id;
                    unset($temp[$i]);
                    $temp = array_values($temp);
                }
            }
            $app['classifications']=$classificationId;
            $data['apps'][]=$app;
        }*/

file

10个月前 评论
讨论数量: 10
sanders

有几点不懂:

  1. 分类为何与操作系统有关?但模型关系上有看似没有关系。
  2. 应用和操作系统的关系中还带有版本信息,我猜是不是应该单独再建一张表来存储?因为这与当前的查询貌似没有关系凭空增加扫描行数。

讲一下我的理解:

  1. 从我们的经验来说,第一个原则是,查哪个模型的列表就用哪个模型的查询构建器。
  2. 多数情况下要使用 paginate 方法来约束查询行数。
  3. 要使用 with 方法解决 "N+1" 问题。

所以我觉得你的列表查询应该写成类似这个样子。

App::whereHas('navigations', fn(Builder $query) => $query->where('id',$navigation->id))
->with(['classifications:id,good,name'])
->select(['apps.id','good','author','name','popular','weights'])
->paginate();
10个月前 评论
csmarco (楼主) 10个月前
csmarco (楼主) 10个月前
sanders (作者) 10个月前
sanders (作者) 10个月前
csmarco (楼主) 10个月前
csmarco (楼主) 10个月前
云客网络工作室

为啥不用with嵌套呀

10个月前 评论
csmarco (楼主) 10个月前

使用with()关联,只需要触发一次数据库连接。时间从本地的 6秒钟降到1.2秒。如果在服务器的话,用时更低。下面的代码注释是之前尝试过的方法2。

$navigation=Navigation::where('id',$navigation->id)->first();
$apps=$navigation->apps()->with(['classifications'=>function($query) use($navigation){
$query->where('navigation_id',$navigation->id)->get(['classifications.id']);
}])->get(['apps.id','good','author','name','popular','weights']);//获取当前操作系统所有的app

 /*foreach($navigation->classifications as $classification){
            foreach($classification->apps()->get(['apps.id']) as $app){
                $temp[]=$app;
            }
        }
        foreach($apps as $app){
            $classificationId=[];
            for($i=0;$i<count($temp);$i++){
                if($temp[$i]->pivot->app_id===$app->id){
                    $classificationId[]=$temp[$i]->pivot->classification_id;
                    unset($temp[$i]);
                    $temp = array_values($temp);
                }
            }
            $app['classifications']=$classificationId;
            $data['apps'][]=$app;
        }*/

file

10个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!