Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Laravel custom PHP attribute example

An example project that shows how to build a custom PHP attribute in Laravel: a #[Schedule] attribute you place on an Artisan command class so the command declares its own cron frequency, instead of declaring it in a central scheduling file or bootstrap/app.php.

Requirements

  • PHP 8.5+
  • Composer
  • Docker (for Laravel Sail)
  • Node.js and npm (used by the setup script for asset compilation)

The commands

Five dummy Artisan commands live in app/Console/Commands/Scheduler/. They only print a line — what matters is how they get scheduled.

Command Cadence Options
shop:cancel-unpaid-orders daily without overlapping, on one server
shop:expire-coupons hourly at minute 5 on one server
shop:refresh-catalog-cache every 30 minutes without overlapping, in background
shop:report-low-stock daily at 08:00 Europe/Madrid on one server
shop:archive-delivered-orders cron 0 2 * * 1 without overlapping, on one server, production only

Approach 1: bootstrap/app.php (this branch, main)

The framework default. Every command is registered in a central withSchedule() closure:

->withSchedule(function (Schedule $schedule): void {
    $schedule->command('shop:cancel-unpaid-orders')
        ->daily()
        ->withoutOverlapping()
        ->onOneServer();

    $schedule->command('shop:archive-delivered-orders')
        ->cron('0 2 * * 1')
        ->withoutOverlapping()
        ->onOneServer()
        ->when(fn (): bool => app(ArchiveDeliveredOrdersCommand::class)->shouldArchive());

    // ...
})

It works, but:

  • The cadence lives far from the command it belongs to — you read the class and still don't know when it runs.
  • The file grows with every new command and becomes a merge-conflict magnet.
  • Deleting a command leaves an orphan registration behind.
  • The signature is repeated as a string, so a rename silently breaks the schedule.

Approach 2: the #[Schedule] attribute (branch feature/schedule-attribute)

The command declares its own cadence, and App\Providers\ScheduleAttributeServiceProvider discovers it by reflecting over Artisan::all():

#[Signature('shop:cancel-unpaid-orders')]
#[Schedule(
    frequency: 'daily',
    withoutOverlapping: true,
    onOneServer: true,
)]
final class CancelUnpaidOrdersCommand extends Command { /* ... */ }

App\Attributes\Schedule accepts a frequency method name (daily, dailyAt, …) with args, or a raw cron expression — anything containing a space is treated as cron. withoutOverlapping, onOneServer, runInBackground, timezone and when (a method name on the command) map to the matching Event calls. The attribute is repeatable, so a command can run on several cadences.

php artisan schedule:list prints the same five entries on both branches — that equality is the point of the example.

License

This project is open-sourced software licensed under the MIT license.

About

An example about creating a custom PHP attribute in a Laravel project.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages