I've got the tasks table, this table has status and deadline columns. How can status be changed to "expired" automatically when current date will become greater than task's deadline date? Is there any realtime event listeners in Laravel?
I guess that's how event listener class should look like, but I'm not sure what to do next.
<?php
namespace App\Events;
use App\Models\Task;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class DeadlineExpired
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* The task instance.
*
* #var \App\Models\Task
*/
public $task;
/**
* Create a new event instance.
*
* #param \App\Models\Task $task
* #return void
*/
public function __construct(Task $task)
{
$this->task = $task;
}
}
Since you're checking only the date. Your cron needs to run only once at midnight. Use Laravel Scheduler to do your Job.
First create a class
class UpdateTasks
{
public function __invoke()
{
// do your task here...e.g.,
Tasks::whereDate('deadline','<',today())->update(['status'=>'expired']);
}
}
Then in your app\Console\Kernel.php, schedule method-
$schedule->call(new UpdateTasks())->daily();
Finally configure a cron job at your server to run the schedule command daily at midnight.
php artisan schedule:run
There are Realtime event listeners but thes require a action to fire. Example are when a model is Created, Updated or Deleted then these events fire.
There is no built in "listener" to ping every model waiting for a field you defined to change.
If there is further logic you would like to fire when the Task becomes expired (like send email) then your best would be to run a check for any new expired Tasks using the scheduler.
The Scheduler runs every minute - set by cron.
Related
I am writing Laravel json api using MySQL. What I am trying to do, is whenever user creates record, I want to perform some operation on this record every 24 hours, until some changes are made by admin on this record. So averagely this job will be repeated 10-15 times. this is my job:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Http\Models\Orders\RoadOrder;
class RoadOrderEprirationHandler implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
protected $order_id;
public function __construct($order_id)
{
$this->order_id = $order_id;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$order = RoadOrder::findOrFail($this->order_id);
//do some tasks and if I need to run this cycle again :
$roadOrderEprirationHandler = new RoadOrderEprirationHandler($order->id);
$roadOrderEprirationHandler->dispatch($order->id)->delay(now()->addHours(24));
}
}
So as you can see, I initiate job after previous job was finished if admin didn't modified record and I need to run cycle again. after several cycles, I won't run job again, so it will be finished.
My question:
Is some problem in this implementation? maybe I am doing something wrong or maybe there may occur some type of memory leak (because I create job inside job again and again). I can write this but I need to be sure that this implementation doesn't leads to some type of bugs or errors.
Why don't create a separate table in your DB for that purpose?
You can keep order_ids with status (checked by admin / not checked). Or just put new order_id there and delete it after admin check. So you will only use your jobs to see is there any unchecked ids. Or some kind of that.
I think this approach is more predictable and less buggy.
I am developing a laravel 5.7 application.
I have created a command that should setup my database:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
class TestSetupCommand extends Command
{
protected $signature = 'test:data';
protected $description = 'Basic Setup for Test Data';
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
Artisan::call('migrate:refresh', ['--seed' => '']);
Artisan::call('basis:cc');
Artisan::call('tick:exchange');
$this->info("DB freshly setup: DONE");
$this->info("Coin: DONE");
$this->info("Exchange Scrapped: DONE");
}
}
My problem is that each command takes several minutes to run through. In total it costs me 25 minutes to fill the whole database with data.
I would like to run the commands only for 1 minutes each and kill them afterwards.
Any suggestions how to accomplish this within my laravel command?
I think the best way to do this is to extract these commands into background job. This artisan command then becomes code to queue up that new job (or jobs).
Why? It's very easy to configure jobs to timeout after x amount of time, by overriding a value like so:
<?php
namespace App\Jobs;
class ProcessPodcast implements ShouldQueue
{
/**
* The number of seconds the job can run before timing out.
*
* #var int
*/
public $timeout = 120;
}
Also, why are you refreshing the database? That seems.... like a crazy idea unless this is purely an analytics platform (no user data at all). It's probably a bad thing if that refresh command times out - you may look into job chaining so that the refresh command is guaranteed to succeed, then the other commands (new jobs now) have set timeouts.
Now I need to send FCM push notifications to all my users on every record add, but it's take so much time to loop through them all, how to make the loop work in the background?
I'm using brozot/Laravel-FCM package
You might be triggering an event for push notification. so, while triggering an event for push notification you can push the notifications to queue. This queue is basically implemented in class declared in Listeners folder. example:
namespace App\Listeners;
use App\Events\EventName;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\FcmAdapter;
class NotificationClassName implements ShouldQueue
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param EventName $event
* #return void
*/
public function handle(EventName $event)
{
// code for sending FCM notification.
}
}
implements ShouldQueue is the way to implement queue.
I'm trying to schedule an email to remind users who have to-do tasks due tomorrow. I made a custom command email:reminder. Here is my code in custom command:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Todo;
use Illuminate\Support\Facades\Mail;
class SendReminderEmail extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'email:reminder';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Remind users of items due to complete next day';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
//
/*
* Send mail dynamically
*/
/*
* hardcoded email
*/
Mail::queue('emails.reminder', [], function ($mail) {
$mail->to('example#email.com')
->from('todoreminder#gmail.com', 'To-do Reminder')
->subject('Due tomorrow on your To-do list!');
}
);
$this->info('Reminder email sent successfully!');
}
}
I hardcoded the email for now to test it, but when I ran php artisan email:reminder, I got an exception of
[InvalidArgumentException]
Only mailables may be queued.
I then checked Laravel's documentation, but Task Scheduling and Email Queue are 2 separate topic.
How can I achieve sending email queue with task scheduling in Laravel
5.6 please?
Also how can I pass data, i.e. to-do items in database into my email
view please?
Any help is greatly appreciated!
Using the console kernel to schedule queued jobs is easy to do. Laravel offers several wrapper methods that make the cron integration trivial. Here's a basic example:
$schedule->job(new SendTodoReminders())->dailyAt('9:00');
You should create a command which does exactly as you described, but without the scheduling. You can use the crontab for scheduling or some other task scheduler.
Have you followed Laravel's documentation about mailing? https://laravel.com/docs/5.6/mail
Once you get to the Sending Mail section, you should not create a controller but a command instead.
When that command works, add it to the task scheduler (eg. crontab) to run on a daily basis.
Mail::queue('emails.reminder', [], function ($mail) {
$mail->to('example#email.com')
->from('todoreminder#gmail.com', 'To-do Reminder')
->subject('Due tomorrow on your To-do list!');
}
);
is deprecated since Laravel 5.3. Only the Mailables can Queued and it should implement the ShouldQueue interface.
For running jobs you have to configure the queue driver and run php artisan queue:work
I am building web app in PHP which is used to schedule tasks in the future. In the background I am supposed to Poll the database every 2 min to see if there is any upcoming task. How do I implement polling. Javascript solution is not helpful as the user can be on any of the pages on the webapp, but the system should keep pooling the DB.
Cron is one way but still I have to poll the database to create a cron job. What is the best way to implement it?
Thanks
Create a cron job that is executed once every X minutes and make that job check the DB. If there is a new upcoming task, just make the job launch it.
Create an 'execute:tasks' artisan command, so you can poll your database whenever you need to execute your tasks. You should be able to run it this way:
php artisan execute:tasks
Your command will call some controller action (or a class method) to poll the database and find if there are tasks available to be executed.
Then you just need to create a cron job that will execute that artisan command every 2 minutes:
*/2 * * * * php artisan execute:tasks
This is an example of Artisan command:
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ExecuteTasksCommand extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'execute:tasks';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Find and execute available tasks.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
(new ExecuteTasksClass)->findAndExecute();
}
}
You just have to name this file something like app/commands/ExecuteTasksCommand.php
And add this to app\start\artisan.php:
Artisan::add(new ExecuteTasksCommand);