feat(actions): add ScheduleServiceProvider — wires DB-backed actions into scheduler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d1598882bb
commit
633fbeb559
3 changed files with 151 additions and 0 deletions
73
src/Core/Actions/ScheduleServiceProvider.php
Normal file
73
src/Core/Actions/ScheduleServiceProvider.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Core PHP Framework
|
||||
*
|
||||
* Licensed under the European Union Public Licence (EUPL) v1.2.
|
||||
* See LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Actions;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
/**
|
||||
* Reads scheduled_actions table and wires enabled actions into Laravel's scheduler.
|
||||
*
|
||||
* This provider runs in console context only. It queries the database for enabled
|
||||
* scheduled actions and registers them with the Laravel Schedule facade.
|
||||
*
|
||||
* The scheduled_actions table is populated by the `schedule:sync` command,
|
||||
* which discovers #[Scheduled] attributes on Action classes.
|
||||
*/
|
||||
class ScheduleServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
if (! $this->app->runningInConsole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard against table not existing (pre-migration)
|
||||
if (! Schema::hasTable('scheduled_actions')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->app->booted(function () {
|
||||
$schedule = $this->app->make(Schedule::class);
|
||||
|
||||
$actions = ScheduledAction::enabled()->get();
|
||||
|
||||
foreach ($actions as $action) {
|
||||
$class = $action->action_class;
|
||||
|
||||
if (! class_exists($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$event = $schedule->call(function () use ($class, $action) {
|
||||
$class::run();
|
||||
$action->markRun();
|
||||
})->name($class);
|
||||
|
||||
// Apply frequency
|
||||
$method = $action->frequencyMethod();
|
||||
$args = $action->frequencyArgs();
|
||||
$event->{$method}(...$args);
|
||||
|
||||
// Apply options
|
||||
if ($action->without_overlapping) {
|
||||
$event->withoutOverlapping();
|
||||
}
|
||||
|
||||
if ($action->timezone) {
|
||||
$event->timezone($action->timezone);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,8 @@ class Boot extends ServiceProvider
|
|||
return;
|
||||
}
|
||||
|
||||
$this->app->register(\Core\Actions\ScheduleServiceProvider::class);
|
||||
|
||||
$this->fireConsoleBooting();
|
||||
}
|
||||
|
||||
|
|
|
|||
76
tests/Feature/ScheduleServiceProviderTest.php
Normal file
76
tests/Feature/ScheduleServiceProviderTest.php
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tests\Feature;
|
||||
|
||||
use Core\Actions\ScheduledAction;
|
||||
use Core\Actions\ScheduleServiceProvider;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Orchestra\Testbench\TestCase;
|
||||
|
||||
class ScheduleServiceProviderTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function defineDatabaseMigrations(): void
|
||||
{
|
||||
$this->loadMigrationsFrom(__DIR__.'/../../database/migrations');
|
||||
}
|
||||
|
||||
protected function defineEnvironment($app): void
|
||||
{
|
||||
$app['config']->set('database.default', 'testing');
|
||||
$app['config']->set('database.connections.testing', [
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app): array
|
||||
{
|
||||
return [
|
||||
ScheduleServiceProvider::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function test_provider_registers_enabled_actions_with_scheduler(): void
|
||||
{
|
||||
ScheduledAction::create([
|
||||
'action_class' => 'Core\\Tests\\Fixtures\\Mod\\Scheduled\\Actions\\EveryMinuteAction',
|
||||
'frequency' => 'everyMinute',
|
||||
'is_enabled' => true,
|
||||
]);
|
||||
|
||||
ScheduledAction::create([
|
||||
'action_class' => 'Core\\Tests\\Fixtures\\Mod\\Scheduled\\Actions\\DailyAction',
|
||||
'frequency' => 'dailyAt:09:00',
|
||||
'timezone' => 'Europe/London',
|
||||
'is_enabled' => false,
|
||||
]);
|
||||
|
||||
// Re-boot the provider to pick up the new rows
|
||||
$provider = new ScheduleServiceProvider($this->app);
|
||||
$provider->boot();
|
||||
|
||||
$schedule = $this->app->make(Schedule::class);
|
||||
$events = $schedule->events();
|
||||
|
||||
// Should have at least the enabled action
|
||||
$this->assertNotEmpty($events);
|
||||
}
|
||||
|
||||
public function test_provider_skips_when_table_does_not_exist(): void
|
||||
{
|
||||
// Drop the table
|
||||
Schema::dropIfExists('scheduled_actions');
|
||||
|
||||
// Should not throw
|
||||
$provider = new ScheduleServiceProvider($this->app);
|
||||
$provider->boot();
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue