Handling cron jobs in Laravel

Handling cron jobs in Laravel

Cron jobs or scheduled tasks are common features in many web applications, for example to clean up old log files, make a backup or notify the users of your portal. For Laravel applications, setting up cron jobs is easy with its built-in task scheduler. One of the main advantages of this task scheduler is that you only have to set a single cron job on your server. Once this is done, everything will be handled by your code in Laravel. 

In one of our applications, users are able to follow the development of their babies. Every two weeks, they have to fill in the length and weight of their child and those data are compared to what’s expected for a child of their age. When someone forgets to enter new data, we want to send them a reminder. In this post, we describe the steps to set this up. Let’s dive in!

Create a command

The first step is to write the code that has to be executed at regular times, which is done in a command. Next, we schedule the command to handle this reminder. We start by creating a command via the terminal:

php artisan make:command Reminder

A new file ‘Reminder.php’ is then created in the app/Console/Commands directory. Within this Reminder class, you’ll find the protected variable $signature, which is used to call the command. In our example, we set this to reminder:new-measurement. This means we can execute the command manually using php artisan reminder:new-measurement, or run the command at regular times using the task scheduler (see below).

In the protected variable $description you can describe what the command is doing. This is mainly for your own reference.

In the handle method you write the code to be executed. In our example, we retrieve the users that entered their latest weight and length data point more than 2 weeks ago, and send them an email. It’s beyond the scope of this post to describe these steps.

Kernel

With our command ready, we have to schedule the command to be regularly executed. This is done in the app/Console/Kernel.php file, inside the schedule method:

protected function schedule (Schedule $schedule) {
    $schedule->command('reminder:new-measurement')->dailyAt('13:00')->sendOutputTo('storage/cronlog')->emailOutputTo('info@plint-sites.nl');
}

The command to be executed is reminder:new-measurement, which corresponds to the signature set in the command. The frequency at which the command should be run is set to daily at 13.00 in this example, but there are plenty of frequency options for the cron job, e.g. hourly, weekly, everyFiveMinutes etc. A full overview of the possibilities can be found here.

Additionally, the output of the cron job can be stored on the server using the sendOutputTo method, or sent by email using the emailOutputTo method. 

Further customizing the tasks

There are some more handy methods that can be chained to a scheduled task. The onOneServer method can be used if your code is deployed to multiple application servers, but only one server should execute the command. This would be the case in our example, because we only want users to receive one reminder. 

If you’re using Envoyer (or a similar service) you can use a heartbeat to make sure you get notified when the task stops working. How to create a heartbeat in Envoyer is described in this post. It gives you a heartbeat url that can be pinged after the task has run:

$schedule->command(‘reminder:new-measurement’)->thenPing(‘http://heartbeaturl.io’)

Passing arguments

In some cases you may want to send additional arguments to your command. In our example, we send a reminder to our users after two weeks of inactivity, but we can pass the inactivity period as an argument, so we can easily change it in the future. To do this, we start by changing the signature of the command:

protected $signature = 'reminder:new-measurement {inactivityPeriod}';

In the handle method of the command, we can now access this variable via $this->argument(‘inactivityPeriod’). Last, we have to change the call to command in the kernel:

$schedule->command(‘reminder:new-measurement 2’)

It’s possible to make the argument optional. To do so, change the signature to:

protected $signature = 'reminder:new-measurement {inactivityPeriod?}';
protected $signature = 'reminder:new-measurement {inactivityPeriod=2}';

In the last example, inactivityPeriod is an optional argument with a default value of 2.

Configuring cron jobs on your server

The code base is ready and the last step is to make sure the scheduled commands will run on the server. This requires one cron job to be set on your server:

* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1

The path to artisan is often something like /home/forge/domainname/artisan.

Testing cron jobs on a localhost

Before deploying your codebase to production, you obviously want to test your scheduled tasks. The commands themselves can be tested and executed by calling them directly from the command line, as described above. 

Additionally, you can run the php artisan schedule:work command. This will invoke the scheduler every minute (until you terminate the command) and therefore simulates the behaviour of the scheduler on the server.

Summary

In this post, we described how to set up cron jobs for your Laravel application, by creating commands and scheduling them via Laravel’s task scheduler.


Mijn Twitter profiel Mijn Facebook profiel
Leonie Derendorp Webdeveloper and co-owner of PLint-sites in Sittard, The Netherlands. I love to create complex webapplications using Laravel! Latest post
Handling cron jobs in Laravel

4 thoughts on “Handling cron jobs in Laravel

  1. saman

    hi . I did all you have told in this article but i received periodic emails that say ‘Class ‘Illuminate\Console\Command’ not found ‘. i confuse because in cron.php (the file i created by artisan:consol ),
    there are “namespace App\Console\Commands;

    use Illuminate\Console\Command;”
    and cron class extends Command
    where i have made a mistake?
    best regards

    Reply
    1. Leonie Derendorp Post author

      Hi Saman,

      Your code seems fine, but it’s hard to tell what’s going wrong from these few lines of code.

      The Illuminate\Console\Command class apparently cannot be found, so maybe it helps to run a composer dump-autoload to regenerate the class map.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *