Как повысить производительность запросов с несколькими счетчиками в представлении Laravel - PullRequest
0 голосов
/ 01 апреля 2019

Я работаю над маркетинговым приложением, которое позволяет пользователям отправлять сообщения своим контактам. Когда сообщение отправлено, создается новая запись в базе данных «processing_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 Ответ

1 голос
/ 01 апреля 2019

Вы можете использовать метод withCount для подсчета всех объектов без загрузки отношения.

Ссылка:

Если вы хотите подсчитать количество результатов из отношенияфактически не загружая их, вы можете использовать метод withCount, который поместит столбец {отношение} _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

Документы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...