如何在Laravel视图中提高多计数查询的性能

时间:2019-04-01 15:51:55

标签: mysql laravel laravel-5.7

我正在开发一种营销应用程序,它允许用户向其联系人发送消息。发送消息后,将创建一个新的“ processed_message”数据库条目。有一个列表视图,显示所有市场活动以及每个市场活动发送,阻止和失败的消息数。我的问题是,在> 50个包含大量消息的广告系列之后,此列表视图的加载时间太长。

当前,每个广告系列都有3个计算出的属性(messages_sent,messages_failed和messages_blocked),这些属性都在Campaign模型的“附加”数组中。每个属性都会查询给定广告系列的给定类型的处理消息数。

namespace App;

class Campaign
{
    protected $appends = [
        'messages_sent',
        'messages_blocked',
        'messages_failed'
    ];

    /**
     * @relationship
     */
    public function processed_messages()
    {
        return $this->hasMany(ProcessedMessage::class);
    }

    public function getMessagesSentAttribute()
    {
        return $this->processed_messages()->where('status', 'sent')->count();
    }

    public function getMessagesFailedAttribute()
    {
        return $this->processed_messages()->where('status', 'failed')->count();
    }

    public function getMessagesBlockedAttribute()
    {
        return $this->processed_messages()->where('status', 'blocked')->count();
    }
}

我还尝试一次或分块查询所有消息以减少查询数量,但是一次获取所有的处理消息进行一次露营将导致内存溢出,并且分块方法太慢了,因为必须使用偏移量。我考虑过急于将带有处理的消息的广告加载到广告系列中,但这显然会占用过多的内存。

namespace App\Http\Controllers;

class CampaignController extends Controller
{
    public function index()
    {
        $start = now();
        $campaigns = Campaign::where('user_id', Auth::user()->id)->orderBy('updated_at', 'desc')->get();
        $ids = $campaigns->map(function($camp) {
            return $camp->id;
        });

        $statistics = ProcessedMessage::whereIn('campaign_id', $ids)->select(['campaign_id', 'status'])->get();
        foreach($statistics->groupBy('campaign_id') as $group) {
            foreach($group->groupBy('status') as $messages) {
                $status = $messages->first()->status;
                $attr = "messages_$status";
                $campaign = $campaigns->firstWhere('id', $messages->first()->campaign_id);
                $campaign->getStatistics()->$attr = $status;
            }
        }

        return view('campaign.index', [
            'campaigns' => $campaigns
        ]);
    }
}

我的主要目标是大大减少当前页面的加载时间(如果有大量广告系列,加载时间可能从30秒减少到5分钟)。

1 个答案:

答案 0 :(得分:1)

您可以使用withCount方法对所有对象进行计数,而无需加载关系。

参考:

  

如果您想在没有实际加载的情况下计算关系的结果数,则可以使用withCount方法,该方法将在结果模型上放置一个{relation} _count列。

您可以在控制器中执行以下操作:

$count = Campaign::withCount(['processed_messages' => function ($query) {
    $query->where('content', 'sent');
}])->get();

您也可以在同一关系中进行多次计数:

$campaigns = Campaign::withCount([
'processed_messages',
'processed_messages as sent_message_count' => function ($query) {
    $query->where('content', 'sent');
}],
'processed_messages as failed_message_count' => function ($query) {
    $query->where('status', 'failed');
}],
'processed_messages as blocked_message_count' => function ($query) {
    $query->where('status', 'blocked');
}])->get();

您可以通过以下方式访问计数:

echo $campaigns[0]->sent_message_count

Docs