Usage
The bridge adds three surfaces on top of the engine: container services built from the config, Eloquent casts for storing schedules in columns, and validation rules for rejecting a bad request. Everything the engine itself does — parsing, building, and the three questions — is documented by yarunoka/core.
The container services
Section titled “The container services”YrnkEvaluator and YrnkParser come out of the container carrying the
configured environment:
use Yarunoka\YrnkEvaluator;use Yarunoka\YrnkParser;
app(YrnkEvaluator::class)->matches($schedule, now());app(YrnkParser::class)->parse($json); // knows the config's resolver namesStoring a schedule column
Section titled “Storing a schedule column”A column holding one schedule — a JSON object judged in the
application’s calendar — is cast by naming the wrapper in casts():
use Yarunoka\Laravel\Schedule;
class Routine extends Model{ protected function casts(): array { return ['schedule' => Schedule::class]; }}Writing accepts the spellings a schedule arrives in — a PHP array, a
JSON string, a YrnkSchedule, or the wrapper itself — and validates
on both paths: structure, values, and that every name resolves in the
configured environment. An invalid schedule stops on an exception and
never reaches the database; a broken column value surfaces on read as
InvalidYrnkColumnException, naming the model and the column.
$routine = Routine::create([ 'schedule' => ['days' => [25], 'shift' => ['prev', 'or_same', 'business_day'], 'times' => ['10:00']],]);Reading gives the wrapper back. It is readonly — a change is a
reassignment of the whole attribute — and the model’s toArray() /
toJson() show the stored spelling of the DSL as it is. The core’s
YrnkSchedule stays exposed as $routine->schedule->yrnkSchedule, so
everything the engine can do with a schedule stays reachable.
Storing a schedules column
Section titled “Storing a schedules column”A column may instead hold a schedules part — a JSON list of
schedules, like a document’s schedules — by naming Schedules:
use Yarunoka\Laravel\Schedules;
protected function casts(): array{ return ['schedules' => Schedules::class];}The wrapper holds a list of Schedule (each carrying its
YrnkSchedule), and writing additionally accepts a list of Schedule
or YrnkSchedule. Two things differ from a document: the calendar and
timezone come from the config rather than travelling with the data, and
the document-level “no duplicate schedules” rule does not apply —
identical schedules are legal in a column and fire as one.
Deciding when to fire
Section titled “Deciding when to fire”Both wrappers carry the firing decision — Schedules composes its
schedules with any:
if ($routine->schedule->isDue(now(), since: $routine->last_run_at)) { run(); $routine->last_run_at = now();}isDue asks whether the schedule has a scheduled point after
since, through at — the same half-open question as the engine’s
hasMatchIn, so each question’s “now” becomes the next one’s start and
every point is seen exactly once across a series of polls. Catch-up,
grace, and throttling are the caller’s decisions, made by how the period
is cut; the engine documentation covers the patterns.
Storing a whole document
Section titled “Storing a whole document”A column may instead hold a whole Yrnk document — its own timezone and calendar per row, for rows whose rules do not share the application’s environment:
use Yarunoka\Laravel\Casts\AsYrnk;
protected function casts(): array{ return ['document' => AsYrnk::class];}Reading gives a bare Yrnk. A whole-document column stores such a
document and grows no judgment conveniences; whoever wants to judge
builds an evaluator from the document itself:
use Yarunoka\YrnkEvaluator;
$evaluator = YrnkEvaluator::fromYrnk($routine->document);Writing accepts a Yrnk, an array, or a JSON string, and always passes
construction validation before the database.
Validating a request
Section titled “Validating a request”The rules try the construction and put the engine’s message on the validation error as it is:
use Yarunoka\Laravel\Rules\ValidYrnk;use Yarunoka\Laravel\Rules\ValidYrnkSchedule;use Yarunoka\Laravel\Rules\ValidYrnkSchedules;
$validated = $request->validate([ 'schedule' => ['required', new ValidYrnkSchedule()], 'schedules' => ['sometimes', new ValidYrnkSchedules()], 'document' => ['sometimes', new ValidYrnk()],]);ValidYrnkSchedule checks one schedule, ValidYrnkSchedules a
schedules part, ValidYrnk a whole document — structure, values, and
references against the configured environment. What passed validation is
exactly what the cast will accept, so validate-then-store never fails in
the second step.
Dirtiness and serialization
Section titled “Dirtiness and serialization”JSON varies in key order and whitespace as a string, so the casts
compare decoded values: reassigning a column with the same content in a
different spelling is not dirty, and no pointless UPDATE runs.
Changed content is dirty as usual.
Handling failures
Section titled “Handling failures”Everything the bridge and the engine throw implements
Yarunoka\Exceptions\ExceptionInterface. The bridge adds one exception
of its own: InvalidYrnkColumnException, raised when a stored column
cannot be read back — it names the model and the column, because a
cast’s failure surfaces in the middle of model retrieval where the cause
is otherwise hard to see. Validation failures never reach an exception:
the rules turn them into validation errors instead.