Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Visual Cron Builder Laravel Package

interwal-net/filament-visual-cron-builder

A Filament plugin that adds a visual cron expression builder for Laravel scheduling, letting you create and preview cron schedules through an intuitive UI instead of writing expressions by hand.

View on GitHub
Deep Wiki
Context7

Getting Started

Requirements: Filament v4 (not v3), plus whatever Laravel/PHP floor the composer.json declares. This is a v1.0.0 — read composer.json and CHANGELOG.md before adopting; treat the public API as provisional until a 1.1/2.0 lands.

Install and look at these first:

composer require <vendor>/<package>
  1. README.md — exact namespace and field options.
  2. src/ — the three moving parts: the CronBuilder field, CronExpressionBuilder (compose / parse / humanReadable), and the ValidCronExpression rule. These are the whole surface area.
  3. CHANGELOG.md — v1.0.0 baseline; watch it for option renames in point releases.

Data model: store the expression as a plain string column. Nothing exotic is required.

Schema::table('reports', function (Blueprint $table) {
    $table->string('cron')->nullable(); // e.g. "*/15 9-17 * * 1-5"
});

First use case — swap a text input in a Resource form:

use <Vendor>\<Package>\Fields\CronBuilder; // confirm namespace in README

public static function form(Form $form): Form
{
    return $form->schema([
        TextInput::make('name')->required(),

        CronBuilder::make('cron')
            ->label('Schedule')
            ->required(),
    ]);
}

If you already have a TextInput::make('cron') for schedules, this is a genuine drop-in: same state path, same string value, no migration. Ship it behind a feature flag or on one low-risk resource first to see how the 5-column UI behaves with your real expressions.

Implementation Patterns

Form field as the primary integration. The field binds to a string column and renders five columns (minute, hour, day-of-month, month, day-of-week) with Every / Specific / Range / Step modes, plus a live human-readable preview and next-run time. That preview is the reason to use this over a text input — non-technical admins can build 0 9 * * 1-5 without help.

Display side — humanize in tables and infolists. Keep the raw string in the DB and render it read-only:

TextColumn::make('cron')
    ->formatStateUsing(fn (string $state) => CronExpressionBuilder::humanReadable($state))
    ->tooltip(fn ($record) => $record->cron),

Validate outside the form too. The rule is standalone, so FormRequests, imports, and API endpoints get the same guarantees:

'cron' => ['required', 'string', new ValidCronExpression()],

Compose expressions programmatically for seeders, defaults, or "schedule presets" in an admin action:

$cron = CronExpressionBuilder::compose([
    'minute' => '*/15',
    'hour'   => '9-17',
    // ...
]);

Round-trip a stored expression back into builder state for edit forms — parse() gives you the parts, which is useful if you ever want presets, bulk-copy, or a custom summary panel outside Filament.

Wire it to the scheduler. The output is a standard cron string, so use it directly where you'd otherwise hardcode:

$schedule->command('reports:run')
    ->cron($report->cron)
    ->withoutOverlapping();

Anything dynamic like this needs the schedule to be rebuilt whenever the record changes — Schedule::command(...) in a service provider reading from the DB is the usual pattern.

Pair with a "next run" column for operator confidence. If the package exposes a next-run computation on CronExpressionBuilder, cache it; if not, compute it in your own accessor with cron/cron-expression (which the package almost certainly depends on).

Testing. Assert the field accepts and rejects values at the rule level, and use Filament's form testing for the field:

livewire(CreateReport::class)
    ->fillForm(['cron' => '0 9 * * 1-5'])
    ->call('create')
    ->assertHasNoFormErrors();

livewire(CreateReport::class)
    ->fillForm(['cron' => 'not a cron'])
    ->call('create')
    ->assertHasFormErrors(['cron']);

Gotchas and Tips

  • Filament v4 only. There is no v3 compatibility path noted; if you have panels on v3 mid-upgrade, this package waits for the panel, not the other way round.
  • Five fields only. Standard 5-column cron — no seconds field, and no guarantee about macros (@daily, @weekly). Don't assume a scheduler-style alias survives the field's parser. If you accept macros from elsewhere (API, config), normalize them to 5-field form before they hit the builder.
  • Nullable columns are the classic break. A null cron on an edit form often surfaces as an empty or half-filled preview. Decide the contract early: either ->required() or a ->default('0 0 * * *'), and guard the human-readable rendering with a null check.
  • Preview computation runs on every keystroke. The live preview is nice but it's reactive work; on large forms or slow hosts, check whether the field exposes a toggle to disable the next-run calculation, or scope it to a single field per form.
  • Canonicalize before comparing. 0 9 * * 1-5 and 0 09 * * 1-5 and different whitespace are the same schedule. If you dedupe, diff, or match schedules, compare parsed parts, not raw strings.
  • Next-run is timezone-sensitive. It's computed relative to something — app timezone, panel/user timezone, or UTC. Confirm which before you show it to operators, especially for daily jobs near midnight.
  • Don't store the human-readable string. It's a display artefact and will drift as the package's phrasing changes across versions. The cron string is the source of truth.
  • Pin the version. v1.0.0 with no prior releases means option names (->showPreview(), ->showNextRun(), etc.) can move in a minor. "^1.0" with a lockfile is fine; floating dev-main is not.
  • Extension points are thin by design: subclass CronBuilder and override the view/render hooks for branding or to trim columns; wrap CronExpressionBuilder in your own service if you need caching or custom phrasing, so a package upgrade doesn't ripple through your codebase.
  • Debugging tips: dump the field state on submit ($livewire->form->getState()) to catch dehydration surprises; if an expression is valid but rejected, test the same string against ValidCronExpression in isolation to tell a rule problem from a field-state problem.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib