Laravel отправляет запланированные электронные письма с неправильно подсчитанными строками сообщений, которые были получены за последние 24 часа.

Я создал приложение Laravel, в котором я использую команду, которая отправляет электронные письма каждому своему пользователю с подсчетом сообщений, полученных пользователем в его чате, уведомлениями от приложения и подписчиками, которые следили за ним за последние 24 часа (1 день)

Я пытаюсь подсчитать сообщения, уведомления и подписчиков, которые пользователь получил за последние 24 часа.

Когда я это проверяю - работает нормально. (Когда я проверяю это, я устанавливаю дату и время для планировщика на 2-3 минуты вперед и жду результата или просто запускаю команду, которая дает мне результаты непосредственно через консоль)

Проблема возникает, когда я пытаюсь получить все результаты за последние 24 часа. Заметил - вообще не работает, всегда показывает "0" в счетчиках.

Я предполагаю, что проблема где-то в форматировании даты, но не вижу в этом никаких ошибок.

Когда я тестирую, это работает, но когда я жду 24 часа - это не работает, и я должен исправить это быстрее, а не ждать днями, чтобы поймать ошибку.

Вот код планировщика:

protected function schedule(Schedule $schedule) {
     $schedule->command('command:send_user_general_info_to_email')
              ->daily()->at('13:00')->timezone('Europe/London');
}

Вот код для подсчета сообщений, уведомлений и подписчиков за последние 24 часа:

public function handle() {

    $new_messages_amount = 0;
    $new_followers_amount = 0;
    $new_notifications_amount = 0;

    // chunk all the user in the app when send emails
    User::chunk(300, function( $users ) {

        foreach ($users as $user) {

            $new_messages_amount = Chat::where('user_id', $user->id )
                ->whereDate('created_at', '>', Carbon::now()->subHours(24)->toDateTimeString() )
                ->where('type', 1)
                ->count();

            $new_followers_amount = DB::table('followers')
                ->where('following_id', $user->id)
                ->whereDate('created_at', '>', Carbon::now()->subHours(24)->toDateTimeString() )
                ->count();

            $new_notifications_amount = $user->unreadNotifications()
                ->whereDate('created_at', '>',  Carbon::now()->subHours(24)->toDateTimeString() )
                ->count();

            if ( $user->email ) { // if user has email not NULL

                $user_info = [
                    'username' => $user->first_name,
                    'new_messages_amount' => $new_messages_amount,
                    'new_followers_amount' => $new_followers_amount,
                    'new_notifications_amount' => $new_notifications_amount,
                ];

                Mail::to( $user->email )->queue( new DailyOverviewMail($user_info) );
            }
        }

        // make a delay for 1 minute
        sleep(60);
    });

}

Ребята, заранее спасибо за любую помощь, которая может помочь мне решить проблему!


person Taras Chernata    schedule 04.09.2019    source источник
comment
Похоже, все должно быть в порядке, но вы можете включить ведение журнала mysql и посмотреть журнал запросов, чтобы увидеть, что именно передается.   -  person aynber    schedule 04.09.2019
comment
Вы тестировали его на сервере или в своей системе? В вашем планировщике зарегистрируйте запрос и запустите тот же запрос на своем сервере mysql и проверьте результат.   -  person Mayank Pandeyz    schedule 04.09.2019
comment
Не уверен, что вам нужно использовать whereDate, если вы сравниваете всю строку даты и времени. Я не играл с этими функциями, но похоже, что они предназначены для сравнения определенных частей даты, а не всего. Попытка просто использовать where.   -  person Matt K    schedule 04.09.2019


Ответы (1)


Проблема в том, что вы используете whereDate, который сравнивает только даты, а не полную метку времени. В сочетании с > (вместо >=) это вернет только записи текущего дня (в лучшем случае). Просто замените его на where, и все в порядке.


Ваш код можно значительно улучшить, заменив дополнительные запросы подзапросами. Таким образом, будет только один запрос на блок, а не 1 + 3*300 = 901.

public function handle()
{
    $users = User::query()
        ->select('*')
        ->selectSub(
            Chat::query()
                ->whereColumn('users.id', 'chats.user_id')
                ->where('created_at', '>', now()->subHours(24))
                ->where('type', 1)
                ->count(),
            'new_messages'
        )
        ->selectSub(
            DB::table('followers')
                ->whereColumn('users.id', 'followers.following_id')
                ->where('created_at', '>', now()->subHours(24))
                ->count(),
            'new_followers'
        )
        ->selectSub(
            DatabaseNotification::query()
                ->whereColumn('users.id', 'notifications.user_id')
                ->whereNull('read_at')
                ->where('created_at', '>', now()->subHours(24))
                ->count(),
            'new_unread_notifications'
        )
        ->chunkById(300, function ($users) {
            foreach ($users as $user) {
                if ($user->email) {
                    $user_info = [
                        'username' => $user->first_name,
                        'new_messages_amount' => $user->new_messages,
                        'new_followers_amount' => $user->new_followers,
                        'new_notifications_amount' => $user->new_unread_notifications,
                    ];

                    Mail::to($user->email)->queue(new DailyOverviewMail($user_info));
                }
            }
        });
}

Примечание: select('*') (или select()) требуется, чтобы selectSub() не переопределяло список выбора.

Поскольку вы ставите почту в очередь, на мой взгляд, также нет необходимости sleep(60)...

person Namoshek    schedule 04.09.2019
comment
Мужик, спасибо! Это так полезно! Я попробую это сейчас и посмотрю, работает ли это, сообщу вам здесь :) Еще раз спасибо! - person Taras Chernata; 04.09.2019
comment
О да, вам, вероятно, следует использовать chunkById(), потому что в запросе нет orderBy. Но я думаю, вы уже поняли это, потому что обычно он довольно быстро выдает ошибку. - person Namoshek; 04.09.2019