Laravel: проверьте, добавлены ли новые элементы в таблицу БД, и запланируйте задание для пользователя по электронной почте.

Я хочу инициировать электронное письмо, когда новые строки добавляются в таблицу в моем приложении Laravel. Однако я хочу добавить своего рода буфер, поэтому, если 5 строк добавляются в быстрой последовательности, отправляется только 1 электронное письмо.

Метод, который я выбрал, заключается в том, чтобы запланировать проверку каждые 15 минут и посмотреть, добавлены ли новые строки. Если есть, то я поставлю в очередь электронное письмо.

В настоящее время я получаю сообщение об ошибке в расписании. Я прогоню свой код ниже:

В Kernel.php, где мы настраиваем расписания, у меня есть:

        $schedule->job(new ProcessActivity)
        ->everyFifteenMinutes()
        ->when(function () {
            return \App\JobItem::whereBetween('created_at', array(Carbon::now()->subMinutes(15), Carbon::now()))->exists();
        })
        ->onSuccess(function () {
            Log::debug(
                'Success'
            );
        })
        ->onFailure(function () {
            Log::debug(
                'Fail'
            );
        });

Который я использую для запуска задания, найденного в: App\Jobs\ProcessActivity.php:

 public function __construct()
{

    $this->jobs = \App\JobItem::whereBetween('created_at', array(Carbon::now()->subMinutes(15), Carbon::now()))->get();
}

/**
 * Execute the job.
 *
 * @return void
 */
public function handle()
{


    Log::debug('Activity Job Run',  ['jobs' => $this->jobs]);

    $this->jobs->each(function ($item, $key) {
        Log::debug('loop');

        // get project
        $project = $item->project;

        // get project email
        $user_id = $project->user_id;
        $email = \App\User::find($user_id)->email;

        // get project UUID
        $projectUuid = $project->public_id;

        // emails
        $subscriberEmails = \App\ProjectSubscription::where('project_id', $project->id)->get();

        // create activity email
        Notification::route('mail', $subscriberEmails)->notify(new Activity($project, $projectUuid));
    });

    return true;
}

Я разместил свой полный код выше, который также показывает связь между моими моделями JobItems и Project. Я не буду подробно останавливаться на этом, поскольку я прокомментировал код.

Проблема

Когда я добавляю новую строку в свою таблицу JobItem, я вижу, что задание запланировано и обрабатывается (используя Laravel Telescope для проверки).

Однако я также вижу в своем журнале, что для каждого задания я получаю два сообщения журнала:

Сначала: «Ошибка», а затем «Выполнение задания активности».

Мое электронное письмо не отправляется, и я не знаю, как определить, почему это не удается.

Итак, кажется, что onFailure срабатывает, и есть проблема с моей ProcessActivity.

Любые подсказки о том, где я ошибаюсь и как определить ошибку, будут высоко оценены.


person raison    schedule 15.07.2019    source источник
comment
Я думаю, что есть также проблема в вашей логике. Вы запускаете расписание каждую минуту. Это означает, что уже обработанная строка будет обработана снова в течение следующих 19 минут.   -  person Sachin Kumar    schedule 15.07.2019
comment
@sachinkumar Ах! Я просто запускаю каждую минуту для целей отладки на данный момент - я войду и исправлю это, чтобы это не путалось!   -  person raison    schedule 15.07.2019
comment
Вы можете понять, какое исключение выброшено?   -  person Kevin Bui    schedule 16.07.2019
comment
Есть идеи, как @kevin - как я могу сбросить исключение в onFailure()?   -  person raison    schedule 16.07.2019
comment
Пожалуйста, поместите попытку catch и зарегистрируйте исключение внутри вашего задания: try { } catch (\Exception $e) { \Log::info($e-›getMessage()); } }   -  person Kevin Bui    schedule 17.07.2019
comment
Спасибо @KevinBui, который фактически решил мою проблему, помог мне определить некоторые проблемы. Однако, несмотря на то, что он работает так, как я хочу, я все еще получаю onFailure() для запуска по расписанию. Не уверен, как это запускается, но, возможно, это не проблема... Не уверен. Опубликую ответ на этот вопрос.   -  person raison    schedule 17.07.2019
comment
Я предлагаю вам опубликовать проблему на github.com/laravel/framework для дальнейшего улучшения.   -  person Kevin Bui    schedule 18.07.2019


Ответы (1)


У меня есть исправление, но сначала, вот некоторые вещи, которые я узнал, которые мешали моему прогрессу:

Я использовал эту ремесленную команду для обработки моих запланированных заданий:

php artisan queue:work

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

Таким образом, вы можете либо Command + C, чтобы вернуться в консоль и использовать это каждый раз, когда происходит изменение кода:

php artisan queue:restart
php artisan queue:work

Или вы можете просто использовать это, и это позволит изменять код:

php artisan queue:listen

Как вы можете себе представить, не зная этого, у вас будет медленный процесс отладки!

В результате этого и добавления исключения в мою работу я добился некоторого прогресса. Я вставлю код ниже, чтобы сравнить с исходным кодом:

    public function __construct()
{
}

/**
 * Execute the job.
 *
 * @return void
 */
public function handle()
{
    try {

        $jobs = \App\JobItem::whereBetween('created_at', array(Carbon::now()->subMinutes(20), Carbon::now()))->get();

        Log::debug('Activity Job',  ['jobs' => $jobs]);

        // collection start
        $collection = collect();

        // loop jobs to get emails
        foreach ($jobs as $key => $value) {

            // get project UUID
            $project = $value->project;
            $projectUuid = $project->public_id;

            // get emails subscribed to projects
            $subscriberEmails = \App\ProjectSubscription::where('project_id', $project->id)->get();

            // merge into a single collection via the loop
            if ($key != 0) {
                $merge = $collection->merge($subscriberEmails);
                $collection = collect($merge);
            } else {
                $collection = $subscriberEmails;
            }

            // Log::debug('emails_project in loop', ['emails' => $subscriberEmails]);
        };

        // clean object with uniques only
        $subscriberEmailsCleaned = $collection->unique();

        // debug
        Log::debug('Project Emails to Notify', ['emails' => $subscriberEmailsCleaned]);

        // create activity email
        Notification::route('mail', $subscriberEmailsCleaned)->notify(new Activity($project, $projectUuid));
    } catch (\Exception $e) {
        \Log::info($e->getMessage());
    }
}

Первое, что нужно отметить, это то, что __construct() запускается изначально и сериализуется. Затем при обработке задания вызывается метод handle. Поэтому мне пришлось переместить мой красноречивый запрос в метод дескриптора.

Я также использовал foreach, а не .each для создания новой коллекции электронных писем. Возможно, есть более элегантный способ, но мне нужно было создать коллекцию электронных писем, и этот способ позволил мне переместить переменные в цикле наружу для использования в методе.

Вы можете видеть, как я объединяю их в нижней части цикла.

Я также добавил несколько элементов Log::, полезных для отладки.

Не исправлено на 100 % С помощью этого кода я теперь могу автоматически планировать отправку электронной почты каждые x минут при добавлении новых элементов. Тем не менее, я все еще получаю журнал Fail от onFailure() из моего файла Kernal.php:

        ->onFailure(function () {
        Log::debug(
            'Fail'
        );

Я все еще не понимаю, на что это указывает и как я могу получить больше информации о том, как это не удалось и что это значит. Тем не менее, это работает, поэтому я буду осторожно двигаться вперед (с одним глазом на комментарии, на случай, если у кого-то есть идея, которая может помочь!)

person raison    schedule 17.07.2019
comment
Вы запустили Планировщик? Если планировщик запущен, то он также не отражает изменений. - person Sachin Kumar; 18.07.2019