devsrv / laravel-scheduled-action
处理 Laravel 的定时模型操作
v3.1.0
2023-03-27 12:07 UTC
Requires
- php: ^8.1|^8.2
- illuminate/database: ^7.0|^8.0|^9.0|^10.0
- illuminate/support: ^7.0|^8.0|^9.0|^10.0
- nesbot/carbon: ^2.0
Requires (Dev)
- orchestra/testbench: ^6.0|^7.0|^8.0
- pestphp/pest: ^2.2
README
处理与 Eloquent 模型关联的定时任务。
对于任何定时任务,我们都可以直接使用 Laravel 的 队列,但如果该任务需要在执行前进行某些修改怎么办呢?
此包存储所有需要在将来日期和时间运行的定时任务,并在任务计划运行的当天执行每个任务,这样我们就有机会在任务执行前修改它。
它使用 Laravel 的定时任务 调度 来确定和处理需要当天在指定时间运行的定时任务,并将任务负载发送到你的应用程序的 接收类(可配置的)。所以如何执行任务完全取决于你。
💡 工作原理
- 此包创建一个名为
model_actions的表 - 每个任务有 4 种状态
PENDINGFINISHEDCANCELLEDDISPATCHED scheduledaction:poll艺术家命令轮询当天需要执行的PENDING任务,并将任务负载传递给你的接收类。- 设置轮询的频率和传递给您的接收器的任务数量(上面的 示例 显示每小时 10 个)
PENDING任务在指定日期和时间运行,请根据处理方式将任务标记为FINISHED或CANCELLED(检查示例)。- 很可能是您会使用队列在指定时间运行任务,所以将任务调度到队列工作后,您可能想要将状态设置为
DISPATCHED。
安装
composer 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 特性
use 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
示例
想象一个工作门户网站,申请在申请后的 3 天内自动批准,除非它被机器人的一些资格检查自动拒绝,在此期间,管理员可以覆盖申请状态或防止自动发送邮件或修改邮件内容。
步骤 - 1
发生了某些事件,并创建了一个在未来日期和时间执行的任务
ModelAction::for($application) ->actWith('MAIL') ->actAt($inThreeDays) ->setExtraProperties([ 'mailable' => ApproveApplication::class, 'template' => $template ]) ->createSchedule();
步骤 - 2
管理员决定修改任务
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'); }
步骤 - 3
接收类获取任务负载,并根据任务操作将任务传递到类(对于此示例发送电子邮件)
<?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') }; } } }
步骤 - 4
通过之前的接收类接收发送电子邮件的任务负载,并发送邮件
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]; } }
更新日志
有关最近更改的更多信息,请参阅 更新日志。
许可
MIT 许可协议。请参阅许可文件以获取更多信息。
👋🏼 打个招呼!
如果你觉得这个包很有用,请留下一个⭐,别忘了在Twitter上告诉我