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

Duration Fieldtype Laravel Package

builtnoble/duration-fieldtype

A Statamic fieldtype from BuiltNoble for entering and editing duration values in the control panel, making it easy to store consistent time spans in your content and render them in your templates.

View on GitHub
Deep Wiki
Context7

Getting Started

builtnoble/duration-fieldtype is a Statamic fieldtype addon for capturing and rendering durations (e.g. 1h 30m, 90, 01:30:00) as a first-class blueprint field rather than a free-text string.

Minimal setup:

composer require builtnoble/duration-fieldtype

The service provider is auto-discovered — there is no install command and, in v1.0.x, no mandatory publish step. Add the field to a blueprint and you are running:

# resources/blueprints/collections/recipes.yaml
fields:
  -
    handle: cook_time
    field:
      type: duration
      display: 'Cook Time'

First use case to prove it out: a "time tracking" or "media runtime" field — one entry per task/episode with a duration, then render it in an Antlers listing. That exercises input, storage, augmentation and output in a single pass, so it surfaces storage-format questions before you commit a schema.

Where to look first, in order:

  1. DOCUMENTATION.md — trimmed for verbosity in v1.0.1, so it is now a fast read rather than a scroll.
  2. src/Fieldtypes/Duration.php — the actual source of truth for config options and the save/augment contract. The docs are thin enough that reading the class takes five minutes and removes all ambiguity.
  3. config/duration-fieldtype.php (publish with php artisan vendor:publish --tag=duration-fieldtype-config if present) — any global defaults such as output format.

Note the v1.0.1 dependency bump: orchestra/testbench moved from ^10.8 to ^11.2. That is the test-harness version aligned with Laravel 12, so the package's CI now targets a Laravel 12 / Statamic 5 matrix. If you are still on Laravel 11, pin the addon to 1.0.0 or verify the constraint in composer.json before composer update sweeps it in.

Implementation Patterns

Pick a canonical storage representation up front. The fieldtype's job is input and formatting; your job is deciding what the underlying value means. Store the smallest unit (total seconds/minutes) if you will ever sum, sort, or chart the value; store the formatted string only if it is purely display metadata. Retrofitting this later means a migration over every entry.

Blueprint variants. The common shapes:

# Free-form, human-typed duration
type: duration

# Constrained output for display-only fields
type: duration
format: 'H:i:s'

Because config keys live on the fieldtype class, confirm exact key names against src/Fieldtypes/Duration.php rather than guessing from other addons — this is a small package and the docs intentionally do not restate the source.

Antlers output. Render through augmentation, not the raw value:

{{ cook_time }}                      {{# augmented / formatted #}}
{{ if cook_time }}{{ cook_time }}{{ /if }}

For arithmetic, use the raw augmented value and format in the template or a computed property:

{{ cook_time:value ?? cook_time }}

PHP-side augmentation. In a controller, computed field, or API resource, prefer the augmented value so formatting config is honoured consistently:

$formatted = $entry->augmentedValue('cook_time')->value();
$raw       = $entry->get('cook_time');   // storage-level value

Use get() for persistence logic and augmentedValue() for anything a human or API consumer will see — mixing the two is the most common source of "the format changed in one place only" bugs.

Aggregation pattern. If you need totals (e.g. total runtime across a collection), normalise at save time into a second numeric field or a computed value, then ->sum() on that field. Do not parse formatted strings at query time.

Validation. Treat the field as user input: add a rule in the blueprint's validate block, and if you accept free-form text, add a custom Rule object rather than a loose regex inline. Reject negative values and cap the maximum if the UI has a practical ceiling.

API / GraphQL exposure. Durations serialise as whatever you stored. Decide whether the REST/JSON consumer gets seconds (machine-friendly) or the formatted string, and document it — clients written against the formatted shape break when someone changes the field's format config.

Gotchas and Tips

Empty is not zero. An unset duration typically augments to null, not 0 or 00:00. Any sum, average, or "fastest recipe" query must filter nulls first, otherwise zero-valued entries pollute the results and skew averages downward.

Do not parse durations with Carbon::parse(). Carbon::parse('02:30') is half past two in the morning, not two and a half hours. Use CarbonInterval, or do the arithmetic on integers. This is the single most common bug in duration-handling code and it fails silently — the value looks plausible.

Durations are not times, and they are timezone-free. Never apply a timezone conversion, and never compute a stored duration via Carbon::now()->diff($other) at render time. Compute it once at write time and store the result; otherwise a DST boundary or a server timezone change silently rewrites historical data.

Values over 24 hours wrap. Any format-based rendering (H:i:s, HH:mm) rolls over at a day. If marathon runtimes, long playlists, or accumulated time entries are in scope, store seconds and format manually, or verify the fieldtype's behaviour with a value like 90000 seconds before shipping.

Sorting and filtering are lexical on strings. A collection sorted by a formatted duration string sorts 10:00 before 9:00. If ordering matters, sort on the numeric field, not the fieldtype's stored value.

Upgrading to v1.0.1 is low risk but not contentless. The release is maintenance, docs, and CI only — no fieldtype behaviour changes, no deprecations, no schema changes. The one thing that can bite is the transitive dev-dependency bump to orchestra/testbench ^11.2: if you fork the package or extend its test suite in your own app, your Testbench version must satisfy the new constraint. For consumers simply requiring the addon, composer update builtnoble/duration-fieldtype is safe.

A small package means your own tests carry the weight. There is no large community surface to lean on. Wrap your duration field in a feature test that asserts the round trip — set a value in PHP, save, re-fetch, assert both the raw and augmented forms. That test is the guardrail for every future upgrade.

Extension points. The two useful seams are the fieldtype view (override the Vue component if you need a different input affordance — hours/minutes spinners instead of a text box) and field-level config in the blueprint. Reach for a custom Vue component before you reach for a forked copy of the addon; forking means re-merging the CI and dependency churn that v1.0.1 represents.

Watch the changelog cadence. v1.0.1 was almost entirely housekeeping (reusable CI workflows, docs trims, dependency bumps). That is a healthy signal for a v1 addon, but it also means feature velocity is low — if you need behaviour the fieldtype does not provide, budget for building it yourself rather than waiting on a release.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin