Custom Artisan command in Laravel

Sometimes your application needs to run administrative tasks periodically on the server. Whether you want to send out emails to your users or clean up the database’s temporary tables at the end of the day, you will need a task scheduling mechanism to take care of the tasks. Laravel provides an inbuilt scheduler and its methods for calling it.

We can run those commands with a scheduler to run the Cron job on the server.

In this example, we will create a user model and feed some data into it. then we set up a Cron job that runs at midnight to check birthdays from a user and send birthday wishes using mail functionality.

To create a custom command we have split our tutorial into these parts :

  1. Mail and Database Configuration
  2. Update User Migration
  3. Seeding Dummy Data to User
  4. Create Mailable for Birthday Wishes
  5. Create Artisan Command
  6. Scheduling Artisan Command
  7. Testing Our Application / Functionality

Mail and Database Configuration

First of all, let’s configure our database and mail server configuration into our application. For that open the .env file from the root directory of your project. if the .env file is missing from the project then copy content from the .env.example and create the file. .env file defines many common environment variables.

//Database Configuration
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=database_username
DB_PASSWORD=password

//Mail configuration
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=mail_address
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=admin@example.com
MAIL_FROM_NAME="${APP_NAME}"

Update User Migration

In this example, we will use the default User model you can use another model if required. In this model, we need to add a date of birth column to the user’s table. Update migration as below :

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->date('dob'); //Add this line
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Here we have added a “dob” column with a date data type. We will use this field to check the user’s birthday.

Seeding Dummy Data to User

If you have an existing user base then you don’t need to follow this step. With the fresh application, we don’t have any data in the user table. For that, we will use the Seeding functionality.

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'dob' => $this->faker->date(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
    public function unverified()
    {
        return $this->state(function (array $attributes) {
            return [
                'email_verified_at' => null,
            ];
        });
    }
}

Let’s Create a Seeder to call the factory. Open the terminal and enter the below command.

php artisan make:seeder UserSeeder

This command will create UserSeeder.php. Let’s modify it to create users with a factory class.

<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    public function run()
    {
        User::factory()
            ->count(100)
            ->create();
    }
}

This Logic will create new 100 users in a database. You can change the user count as per your requirements.

Create Mailable for Birthday Wishes

In this step, we will create a mailable class for sending specific mail to users. Open the terminal and enter the below command :

php artisan make:mail HappyBirthday

It will create two files. First, it will create a mailable class with the name HappyBirthday.php in the app\Mail directory and the view file is resources\views\mail\happy-birthday.blade.php. Let’s modify those files one by one

<?php

namespace App\Mail;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class HappyBirthday extends Mailable
{
    use Queueable, SerializesModels;

    protected $user;
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function build()
    {
        return $this->subject('Happy Birthday From Codewolfy' )
                ->markdown('mail.happy-birthday')
                ->with('user', $this->user);
    }
}

We have passed the user model to its construct method and then we set the subject and view for mail. Here we have also passed the user to view using with() method. This User model can be used in our markdown file.

@component('mail::message')
# Happy Birthday {{$user->name}}
It's your special day — get out there and celebrate!

Thanks,

{{ config('app.name') }}
@endcomponent

Create Artisan Command

Now, we are ready to create an artisan command for birthday wishes. Here we will use the make:command to create our custom artisan command like the below :

php artisan make:command BirthdayWish

This command will create BirthdayWish.php. Our logic for checking birthdays and sending mail will be written here. The Artisan command can be divided into Four parts :

  1. Command Signature: This protected variable defines the command signature for a command like birthday:wish. This will set php artisan birthday:wish to run our custom command.
  2. Command Description: This variable holds the command description for user understanding.
  3. Construct() Method: Required logic that we need while calling this command
  4. Handle() Method: Entire logic for our command needs to be added to this function.
<?php

namespace App\Console\Commands;

use App\Mail\HappyBirthday;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;

class BirthdayWish extends Command
{
    protected $signature = 'birthday:wish';

    protected $description = 'This command will run everyday and send birthday wishes to users.';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $today = Carbon::now();
        $birthdays = User::whereMonth('dob', '=', $today->month)
            ->whereDay('dob', '=', $today->day)
            ->get();

        foreach($birthdays as $birthday)
        {
            Mail::to($birthday->email)->queue(new HappyBirthday($birthday));
            $this->info("Birthday Wishes Sent to ".$birthday->name);
        }
        return COMMAND::SUCCESS;
    }
}

Into the handle method first of all we will get birthday users and then we will loop through them. In this loop, we added two statements. The first will add to the mail queue and the second will print a message to the console.

Now our custom command is ready to use. We can manually run it or schedule it as a cron job using Laravel scheduler.

Scheduling Artisan Command

Laravel provides in-built functionality to schedule cron jobs. We can schedule our newly created command to run at a specific time Let’s schedule our command to run at midnight.

<?php

namespace App\Console;

use App\Console\Commands\BirthdayWish; //import
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    protected $commands = [
        BirthdayWish::class,
    ];

    protected function schedule(Schedule $schedule)
    {
        //This sets command to at 12AM
        $schedule->command('birthday:wish')->dailyAt('00:00');
    }

    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

Testing Our Application / Functionality

You can test commands using a terminal or using task scheduling. Here we will test our custom artisan command using a terminal.

php artisan birthday:wish

This will print “Birthday Wishes Sent to USER” for each birthday user into the terminal. It will not print those messages while calling the scheduler.