feat(actions): add #[Scheduled] attribute for Action classes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
cb6d206c76
commit
8d0b2b64ec
2 changed files with 108 additions and 0 deletions
46
src/Core/Actions/Scheduled.php
Normal file
46
src/Core/Actions/Scheduled.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?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 Attribute;
|
||||
|
||||
/**
|
||||
* Mark an Action class for scheduled execution.
|
||||
*
|
||||
* The frequency string maps to Laravel Schedule methods:
|
||||
* - 'everyMinute' → ->everyMinute()
|
||||
* - 'dailyAt:09:00' → ->dailyAt('09:00')
|
||||
* - 'weeklyOn:1,09:00' → ->weeklyOn(1, '09:00')
|
||||
* - 'hourly' → ->hourly()
|
||||
* - 'monthlyOn:1,00:00' → ->monthlyOn(1, '00:00')
|
||||
*
|
||||
* Usage:
|
||||
* #[Scheduled(frequency: 'dailyAt:09:00', timezone: 'Europe/London')]
|
||||
* class PublishDigest
|
||||
* {
|
||||
* use Action;
|
||||
* public function handle(): void { ... }
|
||||
* }
|
||||
*
|
||||
* Discovered by ScheduledActionScanner, persisted to scheduled_actions table
|
||||
* via `php artisan schedule:sync`, and executed by ScheduleServiceProvider.
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
class Scheduled
|
||||
{
|
||||
public function __construct(
|
||||
public string $frequency,
|
||||
public ?string $timezone = null,
|
||||
public bool $withoutOverlapping = true,
|
||||
public bool $runInBackground = true,
|
||||
) {}
|
||||
}
|
||||
62
tests/Feature/ScheduledAttributeTest.php
Normal file
62
tests/Feature/ScheduledAttributeTest.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tests\Feature;
|
||||
|
||||
use Core\Actions\Scheduled;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
|
||||
class ScheduledAttributeTest extends TestCase
|
||||
{
|
||||
public function test_attribute_can_be_instantiated_with_frequency(): void
|
||||
{
|
||||
$attr = new Scheduled(frequency: 'dailyAt:09:00');
|
||||
|
||||
$this->assertSame('dailyAt:09:00', $attr->frequency);
|
||||
$this->assertNull($attr->timezone);
|
||||
$this->assertTrue($attr->withoutOverlapping);
|
||||
$this->assertTrue($attr->runInBackground);
|
||||
}
|
||||
|
||||
public function test_attribute_accepts_all_parameters(): void
|
||||
{
|
||||
$attr = new Scheduled(
|
||||
frequency: 'weeklyOn:1,09:00',
|
||||
timezone: 'Europe/London',
|
||||
withoutOverlapping: false,
|
||||
runInBackground: false,
|
||||
);
|
||||
|
||||
$this->assertSame('weeklyOn:1,09:00', $attr->frequency);
|
||||
$this->assertSame('Europe/London', $attr->timezone);
|
||||
$this->assertFalse($attr->withoutOverlapping);
|
||||
$this->assertFalse($attr->runInBackground);
|
||||
}
|
||||
|
||||
public function test_attribute_targets_class_only(): void
|
||||
{
|
||||
$ref = new ReflectionClass(Scheduled::class);
|
||||
$attrs = $ref->getAttributes(\Attribute::class);
|
||||
|
||||
$this->assertNotEmpty($attrs);
|
||||
$instance = $attrs[0]->newInstance();
|
||||
$this->assertSame(\Attribute::TARGET_CLASS, $instance->flags);
|
||||
}
|
||||
|
||||
public function test_attribute_can_be_read_from_class(): void
|
||||
{
|
||||
$ref = new ReflectionClass(ScheduledAttributeTest_Stub::class);
|
||||
$attrs = $ref->getAttributes(Scheduled::class);
|
||||
|
||||
$this->assertCount(1, $attrs);
|
||||
$instance = $attrs[0]->newInstance();
|
||||
$this->assertSame('everyMinute', $instance->frequency);
|
||||
}
|
||||
}
|
||||
|
||||
#[Scheduled(frequency: 'everyMinute')]
|
||||
class ScheduledAttributeTest_Stub
|
||||
{
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue