Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
Ранее, возможно, вам приходилось создавать запись конфигурации cron для каждой задачи, которую вы хотели запланировать на своем сервере. Однако это может быстро стать проблемой, потому что ваше расписание задач больше не находится под управлением системы контроля версий, и вам приходится подключаться к своему серверу по SSH, чтобы просмотреть существующие записи cron или добавить новые записи.
Планировщик команд Laravel предлагает новый подход к управлению запланированными задачами на вашем сервере. Планировщик позволяет вам легко и выразительно определять расписание ваших команд непосредственно в вашем приложении Laravel. При использовании планировщика на сервере требуется всего одна запись cron. Ваше расписание задач определено в методе schedule
файла app/Console/Kernel.php
. Для того чтобы помочь вам начать, приведен простой пример внутри этого метода.
Вы можете определить все свои запланированные задачи в методе schedule
класса App\Console\Kernel
вашего приложения. Для начала давайте рассмотрим пример. В этом примере мы будем запускать замыкание каждый день в полночь. Внутри замыкания мы выполним запрос к базе данных для очистки таблицы:
<?php namespace App\Console; use Illuminate\Console\Scheduling\Schedule;use Illuminate\Foundation\Console\Kernel as ConsoleKernel;use Illuminate\Support\Facades\DB; class Kernel extends ConsoleKernel{ /** * Определение расписания команд приложения. */ protected function schedule(Schedule $schedule): void { $schedule->call(function () { DB::table('recent_users')->delete(); })->daily(); }}
Помимо планирования с использованием замыканий, вы также можете планировать вызываемые объекты. Вызываемые объекты - это простые классы PHP, содержащие метод __invoke
:
$schedule->call(new DeleteRecentUsers)->daily();
Если вы хотите просмотреть обзор ваших запланированных задач и время следующего их запуска, вы можете использовать команду Artisan schedule:list
:
php artisan schedule:list
Помимо планирования замыканий, вы также можете планировать команды Artisan и системные команды. Например, вы можете использовать метод command
для планирования команды Artisan, указав имя или класс команды.
При планировании команд Artisan с использованием класса команды вы можете передать массив дополнительных аргументов командной строки, которые должны быть предоставлены команде при ее вызове:
use App\Console\Commands\SendEmailsCommand; $schedule->command('emails:send Taylor --force')->daily(); $schedule->command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
Метод job
может использоваться для планирования очередной задачи. Этот метод предоставляет удобный способ планировать задачи в очереди без использования метода call
для определения замыканий для помещения задачи в очередь:
use App\Jobs\Heartbeat; $schedule->job(new Heartbeat)->everyFiveMinutes();
Необязательные второй и третий аргументы могут быть предоставлены методу job
, который указывает имя очереди и подключение к очереди, которые должны использоваться для помещения задачи в очередь:
use App\Jobs\Heartbeat; // Отправка задания в очередь "heartbeats" на подключении "sqs"...$schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
Метод exec
может использоваться для выполнения команды в операционной системе:
$schedule->exec('node /home/forge/script.js')->daily();
Мы уже видели несколько примеров того, как можно настроить выполнение задачи с учетом определенных интервалов. Однако существует еще много частот выполнения задач, которые можно присвоить:
Метод | Описание |
---|---|
->cron('* * * * *'); |
Запуск задачи по пользовательскому расписанию cron |
->everySecond(); |
Запуск задачи каждую секунду |
->everyTwoSeconds(); |
Запуск задачи каждые две секунды |
->everyFiveSeconds(); |
Запуск задачи каждые пять секунд |
->everyTenSeconds(); |
Запуск задачи каждые десять секунд |
->everyFifteenSeconds(); |
Запуск задачи каждые пятнадцать секунд |
->everyTwentySeconds(); |
Запуск задачи каждые двадцать секунд |
->everyThirtySeconds(); |
Запуск задачи каждые тридцать секунд |
->everyMinute(); |
Запуск задачи каждую минуту |
->everyTwoMinutes(); |
Запуск задачи каждые две минуты |
->everyThreeMinutes(); |
Запуск задачи каждые три минуты |
->everyFourMinutes(); |
Запуск задачи каждые четыре минуты |
->everyFiveMinutes(); |
Запуск задачи каждые пять минут |
->everyTenMinutes(); |
Запуск задачи каждые десять минут |
->everyFifteenMinutes(); |
Запуск задачи каждые пятнадцать минут |
->everyThirtyMinutes(); |
Запуск задачи каждые тридцать минут |
->hourly(); |
Запуск задачи каждый час |
->hourlyAt(17); |
Запуск задачи каждый час в 17 минут прошедших часа |
->everyOddHour($minutes = 0); |
Запуск задачи каждый нечетный час |
->everyTwoHours($minutes = 0); |
Запуск задачи каждые два часа |
->everyThreeHours($minutes = 0); |
Запуск задачи каждые три часа |
->everyFourHours($minutes = 0); |
Запуск задачи каждые четыре часа |
->everySixHours($minutes = 0); |
Запуск задачи каждые шесть часов |
->daily(); |
Запуск задачи каждый день в полночь |
->dailyAt('13:00'); |
Запуск задачи каждый день в 13:00 |
->twiceDaily(1, 13); |
Запуск задачи дважды в день в 1:00 и 13:00 |
->twiceDailyAt(1, 13, 15); |
Запуск задачи дважды в день в 1:15 и 13:15 |
->weekly(); |
Запуск задачи каждое воскресенье в 00:00 |
->weeklyOn(1, '8:00'); |
Запуск задачи каждую неделю в понедельник в 8:00 |
->monthly(); |
Запуск задачи в первый день каждого месяца в 00:00 |
->monthlyOn(4, '15:00'); |
Запуск задачи ежемесячно 4-го числа в 15:00 |
->twiceMonthly(1, 16, '13:00'); |
Запуск задачи дважды в месяц, 1-го и 16-го числа, в 13:00 |
->lastDayOfMonth('15:00'); |
Запуск задачи в последний день месяца в 15:00 |
->quarterly(); |
Запуск задачи в первый день каждого квартала в 00:00 |
->quarterlyOn(4, '14:00'); |
Запуск задачи ежеквартально 4-го числа в 14:00 |
->yearly(); |
Запуск задачи в первый день каждого года в 00:00 |
->yearlyOn(6, 1, '17:00'); |
Запуск задачи ежегодно 1-го июня в 17:00 |
->timezone('America/New_York'); |
Установка временной зоны для задачи |
Эти методы могут быть объединены с дополнительными ограничениями для создания более тонко настроенных расписаний, которые выполняются только в определенные дни недели. Например, можно запланировать выполнение команды еженедельно по понедельникам:
// Запускать раз в неделю по понедельникам в 13:00...$schedule->call(function () { // ...})->weekly()->mondays()->at('13:00'); // Запускать каждый час с 8:00 до 17:00 по будням...$schedule->command('foo') ->weekdays() ->hourly() ->timezone('America/Chicago') ->between('8:00', '17:00');
Ниже приведен список дополнительных ограничений расписания:
Метод | Описание |
---|---|
->weekdays(); |
Ограничение выполнения задачи по будням |
->weekends(); |
Ограничение выполнения задачи по выходным |
->sundays(); |
Ограничение выполнения задачи по воскресеньям |
->mondays(); |
Ограничение выполнения задачи по понедельникам |
->tuesdays(); |
Ограничение выполнения задачи по вторникам |
->wednesdays(); |
Ограничение выполнения задачи по средам |
->thursdays(); |
Ограничение выполнения задачи по четвергам |
->fridays(); |
Ограничение выполнения задачи по пятницам |
->saturdays(); |
Ограничение выполнения задачи по субботам |
->days(array|mixed); |
Ограничение выполнения задачи определенными днями |
->between($startTime, $endTime); |
Ограничение выполнения задачи между начальным и конечным временем |
->unlessBetween($startTime, $endTime); |
Ограничение выполнения задачи вне начального и конечного времени |
->when(Closure); |
Ограничение выполнения задачи на основе проверки истинности |
->environments($env); |
Ограничение выполнения задачи для определенных сред выполнения |
Метод days
можно использовать для ограничения выполнения задачи только определенные дни недели. Например, можно запланировать выполнение команды каждый час по воскресеньям и средам:
$schedule->command('emails:send') ->hourly() ->days([0, 3]);
Также можно использовать константы, доступные в классе Illuminate\Console\Scheduling\Schedule
, при определении дней, в которые должна выполняться задача:
use Illuminate\Console\Scheduling\Schedule; $schedule->command('emails:send') ->hourly() ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
Метод between
можно использовать для ограничения выполнения задачи в зависимости от времени суток:
$schedule->command('emails:send') ->hourly() ->between('7:00', '22:00');
Аналогично метод unlessBetween
можно использовать для исключения выполнения задачи в определенный период времени:
$schedule->command('emails:send') ->hourly() ->unlessBetween('23:00', '4:00');
Метод when
можно использовать для ограничения выполнения задачи в зависимости от результата заданного теста истинности. Другими словами, если заданное замыкание возвращает true
, задача будет выполнена, при условии, что никакие другие ограничения не препятствуют выполнению задачи:
$schedule->command('emails:send')->daily()->when(function () { return true;});
Метод skip
можно рассматривать как обратное when
. Если метод skip
возвращает true
, запланированная задача не будет выполнена:
$schedule->command('emails:send')->daily()->skip(function () { return true;});
При использовании цепочки методов when
, запланированная команда будет выполняться только в том случае, если все условия when
возвращают true
.
Метод environments
можно использовать для выполнения задач только в указанных средах выполнения (как определено переменной среды APP_ENV
environment variable):
$schedule->command('emails:send') ->daily() ->environments(['staging', 'production']);
Используя метод timezone
, вы можете указать, что время запланированной задачи должно интерпретироваться в пределах определенного часового пояса:
$schedule->command('report:generate') ->timezone('America/New_York') ->at('2:00')
Если вы постоянно присваиваете один и тот же часовой пояс всем запланированным задачам, вам, возможно, захочется определить метод scheduleTimezone
в вашем классе App\Console\Kernel
. Этот метод должен возвращать часовой пояс по умолчанию, который следует присвоить всем запланированным задачам:
use DateTimeZone; /** * Получить временную зону, которая должна использоваться по умолчанию для запланированных событий. */protected function scheduleTimezone(): DateTimeZone|string|null{ return 'modify_10x/scheduling.return_text_262';}
Внимание Помните, что некоторые часовые пояса используют переход на летнее/зимнее время. При изменении времени перехода на летнее/зимнее время ваша запланированная задача может выполняться дважды или даже вообще не выполняться. По этой причине рекомендуем избегать планирования задач по часовым поясам, когда это возможно.
По умолчанию запланированные задачи будут выполняться даже в том случае, если предыдущий экземпляр задачи все еще выполняется. Чтобы предотвратить это, вы можете использовать метод withoutOverlapping
:
$schedule->command('emails:send')->withoutOverlapping();
В этом примере команда emails:send
команды Artisan будет выполняться каждую минуту, если она еще не выполняется. Метод withoutOverlapping
особенно полезен, если у вас есть задачи, сильно различающиеся по времени выполнения, что мешает вам точно предсказать, сколько времени займет выполнение задачи.
При необходимости вы можете указать, сколько минут должно пройти, прежде чем истечет блокировка "без перекрытия". По умолчанию блокировка истекает через 24 часа:
$schedule->command('emails:send')->withoutOverlapping(10);
За кулисами метод withoutOverlapping
использует кэш вашего приложения cache, чтобы получить блокировки. При необходимости вы можете очистить эти блокировки кэша с помощью команды Artisan schedule:clear-cache
. Это обычно требуется только в том случае, если задача застревает из-за непредвиденной проблемы с сервером.
Внимание Для использования этой функции ваше приложение должно использовать драйвер кэша
database
,memcached
,dynamodb
илиredis
в качестве драйвера кэша по умолчанию. Кроме того, все серверы должны взаимодействовать с одним и тем же центральным сервером кэширования.
Если планировщик вашего приложения работает на нескольких серверах, вы можете ограничить выполнение запланированной задачи только на одном сервере. Допустим, у вас есть запланированная задача, которая генерирует новый отчет каждую пятницу вечером. Если планировщик задач работает на трех рабочих серверах, запланированная задача будет выполняться на всех трех серверах и генерировать отчет трижды. Не очень хорошо!
Чтобы указать, что задача должна выполняться только на одном сервере, используйте метод onOneServer
при определении запланированной задачи. Первый сервер, который получит задачу, закрепит атомарную блокировку на работу с заданием, чтобы предотвратить запуск той же задачи на других серверах в то же время:
$schedule->command('report:generate') ->fridays() ->at('17:00') ->onOneServer();
Иногда вам может потребоваться запланировать одну и ту же задачу для отправки с разными параметрами, сохраняя при этом инструкцию Laravel о запуске каждой перестановки задачи на одном сервере. Для этого вы можете присвоить каждому определению расписания уникальное имя с помощью метода name
:
$schedule->job(new CheckUptime('https://laravel-docs.com')) ->name('check_uptime:laravel-docs.com') ->everyFiveMinutes() ->onOneServer(); $schedule->job(new CheckUptime('https://vapor.laravel.com')) ->name('check_uptime:vapor.laravel.com') ->everyFiveMinutes() ->onOneServer();
Точно так же запланированным замыканиям необходимо присваивать имя, если предполагается, что они будут выполняться на одном сервере:
$schedule->call(fn () => User::resetApiRequestCount()) ->name('reset-api-request-count') ->daily() ->onOneServer();
По умолчанию несколько задач, запланированных на одно и то же время, будут выполняться последовательно в порядке, в котором они определены в методе schedule
. Если у вас есть долго выполняющиеся задачи, это может вызвать запуск последующих задач намного позже, чем предполагалось. Если вы хотите выполнять задачи в фоновом режиме, чтобы все они могли выполняться одновременно, вы можете использовать метод runInBackground
:
$schedule->command('analytics:report') ->daily() ->runInBackground();
Внимание Метод
runInBackground
может использоваться только при планировании задач с помощью методовcommand
иexec
.
Запланированные задачи вашего приложения не будут выполняться, когда приложение находится в режиме обслуживания, поскольку мы не хотим, чтобы ваши задачи вмешивались в незавершенное обслуживание, которое вы можете выполнять на своем сервере. Однако, если вы хотите принудительно запустить задачу даже в режиме обслуживания, вы можете вызвать метод evenInMaintenanceMode
при определении задачи:
$schedule->command('emails:send')->evenInMaintenanceMode();
Теперь, когда мы узнали, как определить запланированные задачи, давайте обсудим, как их фактически запускать на нашем сервере. Команда Artisan schedule:run
будет анализировать все ваши запланированные задачи и определит, нужно ли их выполнять в зависимости от текущего времени сервера.
Итак, при использовании планировщика Laravel нам нужно добавить всего одну запись конфигурации cron на наш сервер, которая будет запускать команду schedule:run
каждую минуту. Если вы не знаете, как добавлять cron-записи на свой сервер, рассмотрите возможность использования сервиса вроде Laravel Forge, который может управлять вашими cron-записями за вас:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
В большинстве операционных систем cron-задачи ограничены выполнением максимум один раз в минуту. Однако планировщик Laravel позволяет планировать задачи на более частые интервалы, даже до одного раза в секунду:
$schedule->call(function () { DB::table('recent_users')->delete();})->everySecond();
Когда в вашем приложении определены задачи с субминутными интервалами, команда schedule:run
будет продолжать выполнение до конца текущей минуты, а не выходить из нее немедленно. Это позволяет команде вызывать все необходимые субминутные задачи в течение минуты.
Поскольку субминутные задачи, выполняющиеся дольше, чем ожидалось, могут задержать выполнение последующих субминутных задач, рекомендуется, чтобы все субминутные задачи отправляли в очередь задания или выполнялись в фоновом режиме для обработки фактической задачи:
use App\Jobs\DeleteRecentUsers; $schedule->job(new DeleteRecentUsers)->everyTenSeconds(); $schedule->command('users:delete')->everyTenSeconds()->runInBackground();
Поскольку команда schedule:run
выполняется в течение всей минуты при вызове субминутных задач, вам иногда может потребоваться прервать выполнение команды при развертывании вашего приложения. В противном случае экземпляр команды schedule:run
, который уже выполняется, будет продолжать использовать код вашего приложения, развернутый ранее, до завершения текущей минуты.
Чтобы прервать выполнение уже запущенных вызовов schedule:run
, вы можете добавить команду schedule:interrupt
в скрипт развертывания вашего приложения. Эту команду следует вызывать после завершения развертывания вашего приложения:
php artisan schedule:interrupt
Обычно вы не добавляли бы запись cron для планировщика на свой локальный рабочий компьютер. Вместо этого вы можете использовать команду Artisan schedule:work
. Эта команда будет выполняться на переднем плане и вызывать планировщик каждую минуту, пока вы не прервёте выполнение команды:
php artisan schedule:work
Планировщик Laravel предоставляет несколько удобных методов для работы с выводом, созданным запланированными задачами. Во-первых, с помощью метода sendOutputTo
вы можете отправлять вывод в файл для последующего анализа:
$schedule->command('emails:send') ->daily() ->sendOutputTo($filePath);
Если вы хотите добавить вывод в конец определенного файла, вы можете использовать метод appendOutputTo
:
$schedule->command('emails:send') ->daily() ->appendOutputTo($filePath);
Используя метод emailOutputTo
, вы можете отправить вывод на указанный адрес электронной почты. Прежде чем отправлять вывод задачи по электронной почте, вы должны настроить сервис электронной почты Laravel:
$schedule->command('report:generate') ->daily() ->sendOutputTo($filePath)
Если вы хотите отправлять вывод только в случае, если запланированная команда Artisan или системная команда завершается с ненулевым кодом завершения, используйте метод emailOutputOnFailure
:
$schedule->command('report:generate') ->daily()
Внимание Методы
emailOutputTo
,emailOutputOnFailure
,sendOutputTo
иappendOutputTo
доступны только для методовcommand
иexec
.
Используя методы before
и after
, вы можете указать код, который будет выполняться перед запуском и после запуска запланированной задачи:
$schedule->command('emails:send') ->daily() ->before(function () { // Задача собирается выполниться... }) ->after(function () { // Задача выполнена... });
Методы onSuccess
и onFailure
позволяют указать код, который будет выполняться в случае успеха или неудачи запланированной задачи. Ошибка указывает на то, что запланированная команда Artisan или системная команда завершилась с ненулевым кодом завершения:
$schedule->command('emails:send') ->daily() ->onSuccess(function () { // Задача выполнена успешно... }) ->onFailure(function () { // Задача завершилась с ошибкой... });
Если вывод доступен из вашей команды, вы можете получить к нему доступ в ваших хуках after
, onSuccess
или onFailure
, указав типизированный аргумент $output
вашего замыкания:
use Illuminate\Support\Stringable; $schedule->command('emails:send') ->daily() ->onSuccess(function (Stringable $output) { // Задача выполнена успешно... }) ->onFailure(function (Stringable $output) { // Задача завершилась с ошибкой... });
С использованием методов pingBefore
и thenPing
планировщик может автоматически отправлять запрос ping на указанный URL перед или после выполнения задачи. Этот метод полезен для уведомления внешнего сервиса, такого как Envoyer, о том, что ваша запланированная задача начинает выполнение или завершает выполнение:
$schedule->command('emails:send') ->daily() ->pingBefore($url) ->thenPing($url);
Методы pingBeforeIf
и thenPingIf
можно использовать для отправки запроса ping на указанный URL только в том случае, если выполняется заданное условие:
$schedule->command('emails:send') ->daily() ->pingBeforeIf($condition, $url) ->thenPingIf($condition, $url);
Методы pingOnSuccess
и pingOnFailure
могут использоваться для отправки запроса ping на указанный URL только в случае успеха или неудачи задачи. Ошибка указывает на то, что запланированная команда Artisan или системная команда завершилась с ненулевым кодом завершения:
$schedule->command('emails:send') ->daily() ->pingOnSuccess($successUrl) ->pingOnFailure($failureUrl);
Для использования всех методов ping требуется библиотека Guzzle HTTP. Обычно Guzzle устанавливается по умолчанию во всех новых проектах Laravel, но вы можете вручную установить Guzzle в свой проект с использованием менеджера пакетов Composer, если он был случайно удален:
composer require guzzlehttp/guzzle
При необходимости вы можете слушать события, вызываемые планировщиком. Обычно сопоставления слушателей событий будут определены в классе App\Providers\EventServiceProvider
вашего приложения:
/** * Сопоставления слушателей событий для приложения. * * @var array */protected $listen = [ 'Illuminate\Console\Events\ScheduledTaskStarting' => [ 'App\Listeners\LogScheduledTaskStarting', ], 'Illuminate\Console\Events\ScheduledTaskFinished' => [ 'App\Listeners\LogScheduledTaskFinished', ], 'Illuminate\Console\Events\ScheduledBackgroundTaskFinished' => [ 'App\Listeners\LogScheduledBackgroundTaskFinished', ], 'Illuminate\Console\Events\ScheduledTaskSkipped' => [ 'App\Listeners\LogScheduledTaskSkipped', ], 'Illuminate\Console\Events\ScheduledTaskFailed' => [ 'App\Listeners\LogScheduledTaskFailed', ],];