За последние 24 часа нас посетили 17844 программиста и 1566 роботов. Сейчас ищут 1311 программистов ...

[РЕШЕНО] Laravel, Schedule, Mail notification lists

Тема в разделе "Laravel", создана пользователем artoodetoo, 27 окт 2017.

  1. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    Запрос на элегантное решение ;)

    Собираюсь переделать существующие регулярные задачи с crontab на ларавелевский Task Scheduling — чтобы всё было под контролем git. Почитал, попробовал, в целом нравится. Хочу посоветоваться насчет рассылки результатов.

    В кротаб это выглядит примерно так:
    Код (Text):
    1.  
    2. MAILTO="sasha@example.com,petya@example.com"
    3. */10 * * * * php /path/to/task_a.php
    4. */20 * * * * php /path/to/task_b.php
    5.  
    6. MAILTO="petya@example.com"
    7. 0 17 * * * php /path/to/task_c.php
    8.  
    9. MAILTO="olga@example.com"
    10. 0 20 * * * php /path/to/task_d.php
    То есть sasha получает сообщения про a и b. petya - a, b и c. olga - d.

    Хочу повторить подобное для Kernel::schedule(). Есть нюанс: сами задачи и расписание надо держать под version control, а вот списки извещения настраивать независимо, то есть через dotenv. Почту не хардкодить!
    Сходу получется что-то вроде этого, корявенько:

    PHP:
    1. $schedule->command('foo:bar')
    2.          ->everyTenMinutes()
    3.          ->emailOutputTo(env('LIST_FOO_BAR'));
    В моём понимании элегантно было бы вообще избавиться от явного упоминания emailOutputTo(), а использовать какие-то события за кадром и в .env типа

    Код (Text):
    1.  
    2. LIST_FOO_BAR="sasha@example.com,petya@example.com"
    3. LIST_BAR_BAZ="olga@example.com"
    И так как же мне повесить событие на command и сделать магию с "foo:bar" => "LIST_FOO_BAR" ?
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Найдёшь, отпишись плиз. Буду следить за этой темой, но сейчас специально в исходниках покопался, ничего похожего на глобальные события после выполнения команды в исходнике не нашёл. И \Illuminate\Console\Scheduling\Event создаётся не через DI-контейнер, так что его не отдекорировать...
     
  3. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Так, давай по порядку ) Почему бы не оформить таски как консольные команды и не передавать данные опциями / аргументами?

    Ну или как-то так к примеру:
    Код (Text):
    1. $ FOO=bar php -r "echo getenv('FOO').PHP_EOL;"
    2. bar
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    @romach извини не понял. команды и так подразумеваются. что дальше?
     
  5. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    Кто хочет узнать больше о ларавелевском шедулинге, вот есть по русски: https://laravel.ru/docs/v5/scheduling
    Почитайте, потом просветлённые возвращайтесь к вопросу этой темы.
     
  6. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Канонично:
    Создавая класс команды, ты можешь свойством описать сигнатуру:
    PHP:
    1. protected $signature = 'email:send {user} {--queue=}';
    И потом обратиться к переданным параметрам
    PHP:
    1. $queueName = $this->option('queue'); $user = $this->getArgument('user');
    Ну и вызвать их с нужными данными
    PHP:
    1. $schedule->command('emails:send username --force=value')->daily();
    Второй вариант, если тебе принципиально надо передавать через переменные окружения, то ты можешь их передать вместе с вызовом скрипта
    Код (Text):
    1. FOO=bar /path/to/php script.php
    В результате скрипт её увидит. Конкретно твой пример будет выглядеть так:
    PHP:
    1. $schedule->exec('MAILTO="olga@example.com" php /path/to/task_d.php')->cron('0 20 * * *');
    p.s. ну или это я не понял вопроса ))
     
  7. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    Да, походу ты не понял вопроса. Кажется ты не в курсе как кронтаб шлёт письма. Он сам шлёт письма, а не выполняемая из него команда. Такое вот разделение обязанностей :D

    Рассказываю анекдот снова: возьмём за основу это
    PHP:
    1. class Kernel extends ConsoleKernel
    2. {
    3.     protected function schedule(Schedule $schedule)
    4.     {
    5.         $schedule->command('foo:bar')
    6.            ->everyTenMinutes()
    7.            ->emailOutputTo(env('LIST_FOO_BAR'));
    8.         $schedule->command('bar:baz')
    9.            ->dailyAt('18:00')
    10.            ->emailOutputTo(env('LIST_BAR_BAZ'));
    11.         // Таких записей будет десяток-другой
    12.         // . . .
    13.     }
    14. }
    А в настоящем кроне будет ежеминутно стартовать
    php /path/to/artisan schedule:run

    То, что выше уже должно работать. Но…

    Давай будем считать, что вывод каждой команды в расписании должен отсылается на почту. Конкретные емайлы вбивать в код нельзя. Хочется чтобы привязка команд к адресам прописывалась отдельно, скорее всего в .env. Потому как это вещь изменчивая и server dependent.

    Попытаемся добавить сахарку: избавиться от явного упоминания env() и явного упоминания emailOutputTo(). Ибо когда строк много, это выглядит как мусор.

    Рабочая идея такая: использовать свой унаследованный класс Schedule, который бы декорировал command(). Но рассмотрю иные варианты. То что ты @romach выше писал это просто мимо.
     
    #7 artoodetoo, 27 окт 2017
    Последнее редактирование: 30 окт 2017
  8. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    Отчитываюсь.

    Завел такое правило для адресов рассылки: в .env описывается адрес по-умолчанию и опционально адреса, соответствующие командам:
    Код (Text):
    1. SCHEDULE_MAILTO=webmaster@example.com
    2. # for "dummy:output" command
    3. SCHEDULE_DUMMY_OUTPUT_MAILTO=sasha@example.com,petya@example.com
    4. # for "slack:send"
    5. SCHEDULE_SLACK_SEND_MAILTO=petya@example.com
    В Kernel::schedule() добавил замыкание, которое вычисляет адрес и даёт задание на отправку почты:
    PHP:
    1. protected function schedule(Schedule $schedule)
    2. {
    3.     $defaultEmail = env('SCHEDULE_MAILTO');
    4.  
    5.     $run = function($command) use($schedule, $defaultEmail) {
    6.         // Initialize event then, if email address is set, send report
    7.         $event = $schedule->command($command)->description($command);
    8.         $name = str_replace(':', '_', strtoupper(explode(' ', $command)[0]));
    9.         return ($email = env("SCHEDULE_{$name}_MAILTO", $defaultEmail))
    10.             ? $event->emailOutputTo(explode(',', $email), true)
    11.             : $event;
    12.     };
    13.  
    14.     $run('dummy:output')->everyFiveMinutes();
    15.     $run('env')->hourly();
    16.     // ... $run ... $run ... $run
    17. }
    в этом примере для команды "dummy:output" будет найден специфичный адрес, а для "env" будет использован адрес по умолчанию. если адрес по умолчанию также не задан, то рассылки не будет :)

    пустые письма также не шлём, emailOutputTo($addresses, true) — второй аргумент говорит о том, что почту надо отправлять только если вывод команды непустой (как в кроне)

    Вроде получилось как хотел. Предложения приветствуются.
     
    #8 artoodetoo, 29 окт 2017
    Последнее редактирование: 30 окт 2017
    mkramer и mahmuzar нравится это.
  9. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Симпатично.
     
  10. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    @artoodetoo херасе ты от двух строчек избавился )) Дык вот, если говорить о каноне и элегантности, то сообщения шлются по средством Notifications. Т.е. создается сущность, которая принимает некоторый набор данных и исходя из него шлет что надо и кому надо в нужном виде. Само же событие, как следует из названия должно пройти через Events.

    Кратко: команда что-то делает, собирает и генерирует событие, по результату которого уже и стартует Notification с переданными по цепочке данными и отправляющий всё это на нужные каналы.

    Бонусом ты получаешь возможность гибко управлять логикой всего этого: на определенную почту, конкретному юзеру, в слак или широковещательно всем онлайн пользователям разом. Ну и не нагружаешь крон логикой, которая явно к нему не относится )
     
  11. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    @romach кто против, так это не я. покажи как можно "гибко управлять логикой". повторю условия:
    есть консольные команды. мы можем вызвать любую из них с командной строки как `php artisan command:name parameters`
    при этом команда что-то выводит в консоль чтобы я видел как оно проистекает.
    если эту же команду выполняем по расписанию, то конечно вывод я не увижу, если только я не пропишу свой адрес в список оповещения (не в исходниках).

    вот вся задача: в одном случае не надо писем, в другом надо. и желательно сделать просто и без дублирования.