Handle Scheduled Model Action For Laravel
devsrv/laravel-scheduled-action is a Laravel package for handle scheduled model action for laravel.
It currently has 18 GitHub stars and 195 downloads on Packagist (latest version v3.1.0).
Install it with composer require devsrv/laravel-scheduled-action.
Discover more Laravel packages by devsrv
or browse all Laravel packages to compare alternatives.
Last updated
Handle scheduled tasks associated with Eloquent models.
For any scheduled task we can directly use Laravel's queue but what if that task needs to be modified in some way before it gets executed?
This package stores all the tasks that needs to run on a future date & time and executes each only on the day when it is scheduled to run so that we get the chance to modify the task before it gets executed.
It uses Laravel's task scheduling to figure out & handle the tasks that needs to be run for the current day at the specified time for that task, and sends the task payload to a receiver class of your app (configurable). So how to perform the task is totally up to you.
model_actionsPENDING FINISHED CANCELLED DISPATCHEDscheduledaction:poll artisan command polls PENDING tasks for the present day and passes the tasks payload to your receiver class.PENDING tasks gets run at specified date & time, remember to mark the task as FINISHED or CANCELLED based on how it was handled check example.DISPATCHEDcomposer require devsrv/laravel-scheduled-action
php artisan vendor:publish --provider="Devsrv\ScheduledAction\ScheduledActionServiceProvider" --tag="migrations"
php artisan migrate
php artisan vendor:publish --provider="Devsrv\ScheduledAction\ScheduledActionServiceProvider" --tag="config"
// the class that takes care of how to perform the task, the payload will be passed to this invokable class
return [
'receiver' => \App\Http\AutoAction\ScheduledActionReceiver::class, π needs to be invokable
];
app/Console/Kernel.php$schedule->command('scheduledaction:poll --tasks=10')->hourly(); // poll pending tasks (10 tasks every hour & sends payload to your receiver, customize as per your app)
HasScheduledAction trait in your modelsuse Devsrv\ScheduledAction\Traits\HasScheduledAction;
class Candidate extends Model
{
use HasFactory, HasScheduledAction;
...
}
use Devsrv\ScheduledAction\Models\ModelAction;
use Devsrv\ScheduledAction\Facades\Action;
$model = \App\Models\Candidate::find(10);
$model->scheduledActions()->get();
$model->scheduledActions()->finished()->get();
$model->scheduledActions()->cancelled()->first();
$model->scheduledActions()->pending()->get();
$model->scheduledActions()->dispatched()->paginate();
$model->scheduledActions()->pending()->toActBetweenTime(Carbon::createFromTimeString('14:00:00'), Carbon::createFromTimeString('16:30:00'))->get();
$model->scheduledActions()->pending()->whereExtraProperty('prefers', 'mail')->get();
$model->scheduledActions()->whereExtraProperty('channel', 'slack')->get();
$model->scheduledActions()->whereExtraProperties(['prefers' => 'mail', 'applicant' => 27])->get();
$model->scheduledActions()->wherePropertyContains('languages', ['en', 'de'])->get();
$model->scheduledActions()->first()->isPending();
$model->scheduledActions()->first()->getExtraProperty('customProperty');
$task = ModelAction::find(1);
$task->getExtraProperty('customProperty');
$task->act_time;
$task->action;
$task->actionable; // associated model
$task->isPending; // bool
$task->isFinished; // bool
$task->isDispatched; // bool
$task->isCancelled; // bool
$task->isRecurring; // bool
ModelAction::finished()->get();
ModelAction::forModel($modlel)->pending()->get();
ModelAction::forClass(Candidate::class)->get();
ModelAction::whereAction('EMAIL')->get();
ModelAction::forClass(Candidate::class)->modelId(10)->get();
ModelAction::modelIdIn([10, 11, 12])->get();
ModelAction::forModel($modlel)->whereProperty('mailable', \App\Mail\RejectMail::class)->get();
ModelAction::forModel($modlel)->whereProperties(['type' => 'info', 'applicant' => 27])->get();
ModelAction::wherePropertyContains('languages', ['en', 'de'])->get();
ModelAction::where('properties->type', 'success')->get();
\Devsrv\ScheduledAction\Facades\Action::needsToRunOn(now()->tomorrow(), 10, 'asc');
\Devsrv\ScheduledAction\Facades\Action::needsToRunToday(3);
$action = $model->scheduledActions()->create([
'action' => 'EMAIL',
'properties' => ['color' => 'silver'],
'status' => \Devsrv\ScheduledAction\Enums\Status::PENDING,
'act_date' => now(),
'act_time' => now()->setHour(11),
]);
$model->scheduledActions()->createMany([]);
ModelAction::for(Candidate::find(10))
->actWith('EMAIL')
->actAt(Carbon::tomorrow()->setHour(11)) // date + time will be set together
->setExtraProperties(['foo' => 'bar'])
->createSchedule();
ModelAction::for($model)
->actWith('EMAIL')
->actDate($actDate)
->actTime(Carbon::createFromTimeString('20:00:00'))
->setExtraProperties(['foo' => 'bar'])
->createSchedule();
ModelAction::for($model)->actWith('EMAIL')->actTime($carbon)->asDispatched()->createSchedule();
$action->setPending()->save();
$action->setCancelled()->save();
$action->setDispatched()->save();
$action->setFinished()->save(); // default sets finished_at as now()
$action->setFinished($carbon)->save(); // set a finished_at time
$action->mergeExtraProperties(['key' => 'val'])->save(); // merges extra properties
$action->withExtraProperties([])->save(); // overwrites existing
$action->setPending()->setActAt($actAt)->save();
$action->setActAt($carbon)->save(); // date and time will be extracted
$action->setActDate($carbon)->save(); // only date will be used
$action->setActTime($carbon)->save(); // only time will be used
imagine a job portal where an application is auto approved after 3 days of apply unless it was auto rejected by some qualifier checks by bot, during that period the admin may override the application status or prevent the auto mail sending or modify the mail content
ModelAction::for($application)
->actWith('MAIL')
->actAt($inThreeDays)
->setExtraProperties([
'mailable' => ApproveApplication::class,
'template' => $template
])
->createSchedule();
public function modifyScheduledTask() {
$this->validate();
$this->task
->setActDate(Carbon::createFromFormat('m/d/Y', $this->act_date))
->setActTime(Carbon::createFromTimeString($this->act_time))
->mergeExtraProperties([
'template' => $this->templateid,
'extra_data' => $this->role
])
->save();
$this->info('schedule updated');
}
public function cancelSchedule() {
$this->task->setCancelled()->save();
$this->info('schedule cancelled');
}
<?php
namespace App\Http\AutoAction;
use Facades\App\Http\Services\AutoAction\Mailer;
class ScheduledActionReceiver
{
public function __invoke($tasks)
{
foreach ($tasks as $task) {
match($task->action) {
'MAIL' => MailTaskHandler::handle($task),
...
default => activity()->log('auto action task unhandled')
};
}
}
}
class MailTaskHandler
{
public function handle($task) {
[$user, $mailable, $extras, $time] = $this->extractData($task);
Mail::to($user)
->later(
Carbon::now()->setTimeFromTimeString($time),
new $mailable($user, $extras)
);
$task->setFinished()->save(); // π marking the task finished here though it is actually not, because for mail there is no easy way to execute this code after that mail is actually sent
}
private function extractData($task) {
$model = $task->actionable;
$user = null;
$mailable = $task->getExtraProperty('mailable');
$template = $task->getExtraProperty('template');
$extras = [];
if($mailable === ApproveApplication::class) {
$extras['role'] = $task->getExtraProperty('role');
$user = $model->applicant;
}
$time = $task->act_time;
return [$user, $mailable, $template, $extras, $time];
}
}
Please see CHANGELOG for more information on what has changed recently.
The MIT License (MIT). Please see License File for more information.
Leave a β if you find this package useful ππΌ, don't forget to let me know in Twitter