NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Requests for Laravel

Cancel & expire

Requests::cancel($request);   // status = Cancelled
Requests::expire($request);   // status = Expired

cancel() fires RequestStatusChanged and RequestCancelled; expire() fires RequestStatusChanged and RequestExpired.

Both also close the request’s open approval round, so a late decision can’t resolve it and pull the request back: through Requests it throws RequestAlreadyResolved, straight through the approvals engine ($alice->approve($request)) the engine’s ClosedApprovalRequestException. The round is closed as cancelled or expired and announced with the engine’s ApprovalRequestResolved event. A cancelled request is final; an expired one can only be reopened.

Deadlines

Stamp an expiry on creation — per request with expiresAt(), or globally with default_ttl. default_ttl is a positive whole number of minutes (a numeric string from env() works too); not set (null or blank) means no default expiry, and anything else throws InvalidConfigurationException:

// Per request:
Requests::make()->title('Leave request')->expiresAt(now()->addDays(7))->create();

// Globally — config/requests.php, in minutes (integer):
'default_ttl' => 60 * 24 * 14,   // 14 days unless expiresAt() is given

A deadline alone doesn’t change the status. Until requests:expire or expire() runs, a past-due request stays New, and isExpired() returns true:

$request->isExpired();   // true when still New and past expires_at

Expiring from code

Requests::expireDue() expires every open, past-due request and returns the count:

use RoundlyConsulting\Requests\Facades\Requests;

Requests::expireDue();                // int — how many requests were expired
Requests::expireDue(dryRun: true);    // int — how many are due; nothing changes
Requests::expireDue(chunk: 1000);     // rows processed per batch

Expiring on a schedule

The requests:expire command is a thin wrapper over Requests::expireDue() (see Artisan commands). Schedule it:

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('requests:expire')->hourly();

Only open (New) requests past their expires_at are expired, and their approval rounds are closed with them; approved and rejected requests are untouched. The command also lapses the approval decisions and rounds on requests whose own expiry has passed — scoped to the request model, so the rest of your app’s approvals are left to their own sweep. Requests::expireDue() expires requests only, so make that scoped call yourself when you sweep from code:

use RoundlyConsulting\Approvals\Facades\Approvals;
use RoundlyConsulting\Requests\Facades\Requests;
use RoundlyConsulting\Requests\Models\Request;

Requests::expireDue();                            // expire past-due requests
Approvals::expire(subjectType: Request::class);   // lapse their decisions and rounds — requests only

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.