Laravel integration for the Yarunoka schedule definition DSL.
Yarunoka is a small JSON DSL — Yrnk — that states calendar rules like "payday is the 25th, moved up to the previous business day" as data, plus a pure engine that answers questions about them. The DSL and the engine live in yarunoka/core; the language-independent specification lives in the spec repository.
This package binds that engine into Laravel:
config/yarunoka.php and binds
YrnkEvaluator / YrnkParser into the container — scoped per request,
and yielding to any binding the application makes itself.Schedule wrapper with
the firing decision (isDue), a schedules-part column as Schedules
(a list of Schedule composed with any), and a whole-document column
as a bare Yrnk.ValidYrnk, ValidYrnkSchedule,
ValidYrnkSchedules) reject a bad request with the engine's own
message on the validation error.composer require yarunoka/laravel
Requires PHP 8.4 or newer and Laravel 13. The service provider is registered by package auto-discovery. To publish the config:
php artisan vendor:publish --tag=yarunoka-config
Name the wall-clock timezone and the calendar once, in the config:
// config/yarunoka.php
return [
'timezone' => 'Asia/Tokyo',
'calendar' => [
'holidays' => 'yasumi-Japan', // resolved automatically when azuyalabs/yasumi is installed
'business_holidays' => ['2026-08-14'],
'business_days' => [],
],
'resolvers' => [],
];
The bindings are scoped — resolved once per request or queue job — and
the config is read at first use within a scope. Where one scope lives
long (tinker, a test), a config change made after that first use
waits for the next scope; app()->forgetScopedInstances() renews
within the current one, though it resets every scoped service, not
only Yarunoka's.
Cast a JSON column to one schedule by naming the wrapper in casts()
(Schedules::class casts a column of many the same way):
use Yarunoka\Laravel\Schedule;
class Routine extends Model
{
protected function casts(): array
{
return ['schedule' => Schedule::class];
}
}
A column can also hold a whole Yrnk document — its own timezone and
calendar per row. Yrnk is the core's class, so there is no wrapper to
name: a whole-document column names the cast class itself, in the
model's casts():
use Yarunoka\Laravel\Casts\AsYrnk;
protected function casts(): array
{
return ['document' => AsYrnk::class];
}
Validate a request and store the schedule as it was spelled — an invalid schedule never reaches the database:
use Yarunoka\Laravel\Rules\ValidYrnkSchedule;
$validated = $request->validate([
'schedule' => ['required', new ValidYrnkSchedule()],
]);
$routine = Routine::create(['schedule' => $validated['schedule']]);
Ask the firing question from a poller — was there a scheduled point since the last run?
if ($routine->schedule->isDue(now(), since: $routine->last_run_at)) {
// fire, then advance last_run_at
}
How can I help you explore Laravel packages today?