Sunday, December 22, 2019

Laravel: scheduler notification (cron)

I was assigned to a task to send email notification to all registered users when their thread has a new message and they weren’t able to read it. The solution was by sending them an email that has a list of all the messages that are posted not more than 1 hour on their thread.
Automatically it came to my mind to subscribe it to an event, but then since we need to send the email periodically and check for new messages and get only those messages that are posted within an hour in the thread, it would be hard for me to make it work.
Thankfully, laravel has other solution by using schedule. It allows you to fluently and expressively define your command schedule within Laravel itself. When using the scheduler, only a single Cron entry is needed on your server. Your task schedule is defined in the app/Console/Kernel.php file's schedulemethod. To help you get started, a simple example is defined within the method.
Now let start with creating php artisan command: php artisan make:command then go to and open the file in app\Console\Commands\SendUserUnreadMessages.php then change the value of the $signature which is the command name of our artisan command and can also run in the terminal by typing php artisan notification:unreadMessagesand $description a simple sentence to know our command does.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class SendUserUnreadMessages extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'notification:unreadmessages';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send the user an email of their unread messages';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//
}
}
Then, open the app\Console\Kernel.php and then put 'App\Console\Commands\SendUserUnreadMessages' inside the $commands array and $schedule->command(notification:unreadMessages)->hourly(); inside the schedule() function.

<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
'App\Console\Commands\SendUserUnreadMessages'
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
$schedule->command('notification:unreadmessages')->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
} I will not include the logic of getting all the users, threads and its messages as I feel that it is not related to this topic and we also use another package to handle our messenger feature. So, if we look again the SendUserUnreadMessages it now look like this.
?php
namespace App\Console\Commands;
use App\User;
use App\Notifications\UnreadMessagesInThread;
use Illuminate\Console\Command;
class SendUnreadMessagesEmail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'notification:unreadmessages';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send the user an email of their unread messages';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$users = User::all();
$users->map(function($user){
$messages = GetThreadMessages::new($user);
$user->notify (new UnreadMessagesInThread($messages, $user));
});
}
}
As you can see in the handle() function, we get all the users then iterate through them using collection map then the process of checking the user’s thread if it has new messages and get those messages that posted not more than 1 hour. After that we get the messages and send the notification using $user->notify(new UnreadMessagesInTheThread($messages, $user); but still this will not work as it was not yet created so we need to add the Notifiable Trait in our User Model and the create UnreadMessagesInTheThread notification.
Now, open the User model file which is in my case located in app\User.php then put use Illuminate\Notifications\Notifiable in the list of dependencies and inside the User model class use Notifiable;.
And then create a new notification by running this command
php artisan make:notification UnreadMessagesInTheThread --markdown=mails.unread-messages
Open the newly created UnreadMessagesInTheThread and copy the code below.
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class UnreadMessagesInTheThread extends Notification
{
use Queueable;
protected $messages;
protected $user;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($messages, $user)
{
$this->messages = $messages;
$this->user = $user;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
if ($this->messages->count() > 0){
return ['mail', 'database'];
}
return [];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)->markdown('mails.unread-messages',[
'messages' => $this->messages,
'user' => $this->user
]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
As you can see it has two properties $messages and $user that will be pass into the markdown shown below, and use to let the user see all the thread’s messages. And we set a validation inside the via() method so it will not send the notification to a user who’s threads doesn’t have new messages.
@component('mail::message')
#Hello {{$user->first_name}},
@if(count($messages) > 1)
<p>You have new reply in your threads:</p>
@else
<p>You have new reply in your thread:</p>
@foreach ($messages as $message)
<p><strong>{{ $message->thread }}</strong></p>
<p>{{ $message->body }}</p>
<small>Replied <strong>{{ \Carbon\Carbon::parse($message->created_at)->diffForHumans() }}</strong></small>
@endforeach
Thanks,<br>
{{ config('app.name') }}
@endcomponent
https://medium.com/@rafaelogic/use-laravel-notifications-and-schedule-to-send-email-notification-a1a4c652a816