laraning/nova-time-field
A Laravel Nova custom field that adds a clean time picker to your resources, letting you store and edit time-only values with ease. Simple to install and configure, it integrates seamlessly with Nova forms, detail, and index views.
Architecture fit
Laravel\Nova\Fields\Field plus a compiled Vue 2 component wrapping flatpickr. There is no service layer, no schema, no config file, and no migration surface — it is a presentation-layer dependency that plugs into the existing Nova resource contract.Integration feasibility
composer require laraning/nova-time-field:^1.2, then use Laraning\NovaTimeField\Time; in a resource's fields() method. Package discovery handles asset registration if the provider calls Nova::script() / Nova::style().src/Time.php" is a legitimate discovery step, not a code audit. A TPM can fund an hour of engineering review and get a complete behavioural picture: format methods, nullable handling, seconds toggle, component override.Technical risk
| Risk | Severity | Notes |
|---|---|---|
| Nova 4/5 incompatibility | High | Prebuilt dist/ bundle is a 2020-era Vue 2 artifact with no reproducible build path on our toolchain. Symptom: field registers but renders blank, or 404 on the compiled JS. |
| Stale release cadence | High | Last release 2020-11-29. No security-patching SLA, no maintenance commitment, no upstream responsiveness expectation. |
handleChange signature deviation |
Medium | v1.2 overrode Nova's handleChange to fix a JS exception, with the maintainer explicitly noting it does not match the Nova method signature and would be "optimised later." Anything riding Nova's change pipeline (dependsOn, reactive hiding, readonly toggles) is suspect. |
| Timezone drift | Medium | The picker emits a wall-clock string with no timezone. Carbon-casting the attribute makes Laravel interpret it in app.timezone, producing off-by-one-hour bugs. |
| Seconds precision mismatch | Medium | Display and storage precision are configured independently; TIME(0) with a seconds picker truncates silently, TIME(6) with H:i yields 10:15:00.000000 in queries. |
Empty string vs. null |
Low | A cleared picker may submit '', which fails date_format:H:i and can break sortable() ordering. |
| Zero dependents / no support corpus | Medium | 61 stars, 0 dependents. Issue searches come up empty; every failure is an investigation, not a lookup. |
Key questions to resolve before approval
Stack fit
composer.json and compare its laravel/nova constraint against the installed Nova version. Let Composer be the first gate — a blocked install is a clean, cheap signal.Migration path
laraning/nova-time-field:^1.2. Do not target dev-master on a package with a multi-year release gap.app/Nova/Fields/ or nova-components/, rebuild against our own toolchain, and restore a standard handleChange. A few hours of engineering; it eliminates the largest ongoing risk and is the recommended end state for anything with a Nova upgrade on the roadmap.Compatibility
dist/ assets belong under public/vendor/, run php artisan nova:publish as part of the documented install steps — missing compiled assets are far more common than logic defects.dependsOn / reactive visibility explicitly if used. The v1.2 override touches exactly that path.Sequencing
| Phase | Deliverable | Exit criteria |
|---|---|---|
| 0 — Discovery | Nova version confirmed, src/Time.php read, method names verified against the installed tag |
Written note of the actual public API; no reliance on the README |
| 1 — Spike | Field on one throwaway resource, create/edit/detail/index all exercised | Round-trip test passes; raw DB value asserted |
| 2 — Pilot | One real internal feature (shift planning, appointment slots, operating hours, time logging) | Product acceptance criteria for nullability, precision, and validation signed off |
| 3 — Standardize or vendor | Either a documented usage pattern, or a vendored copy owned by the team | Ownership assigned; upgrade runbook written |
Ship the pilot to one workflow, not to every time-based field at once. The dependency is cheap to evaluate and cheap to remove; sequencing it as a per-field convenience keeps both options open.
Minimum usage pattern
Time::make('Opens At', 'opens_at')
->rules('nullable', 'date_format:H:i')
->sortable(),
Plus one assertion that pays for itself on every Nova upgrade:
$this->assertSame('09:30:00', $slot->fresh()->getRawOriginal('opens_at'));
Maintenance
handleChange workaround is technical debt inherited at install time. Do not carry it forward into a fork; replace it with the correct signature.config/, no migrations, no publishable settings). All behaviour is per-field in the resource. If anyone starts looking for a config file, the answer is no — stop.Support
nova:publish → composer show laraning/nova-time-field to confirm installed version → raw database value → validation output.Scaling
sortable() works because the underlying value is a plain string sorted by the database — this scales fine but makes column type and precision decisions load-bearing for index-view ordering.Failure modes and triage
| Symptom | Likely cause | First action |
|---|---|---|
| Field renders blank | Nova 4/5 vs. Vue 2 bundle mismatch | Console + network tab; treat as vendor signal |
| 404 on field JS | Asset not published | php artisan nova:publish |
| Time off by one hour | Carbon cast interpreting wall-clock in app.timezone |
Store as string/TIME; convert only at the boundary |
| Seconds silently dropped | TIME(0) column with seconds picker |
Align precision across field, rules, migration |
| Save fails on cleared field | '' submitted to NOT NULL column |
nullable in rules; normalize '' to null |
dependsOn misbehaves |
v1.2 handleChange signature deviation |
Re-test on every Nova upgrade; fix when vendoring |
| Sort order looks wrong | Empty strings vs. nulls in the column | Normalize nulls; verify raw values |
Ramp-up
How can I help you explore Laravel packages today?