starring-jane/nova-unit-field
A Laravel Nova field for displaying and editing numeric values with units such as kg, %, km, or currency. Configure the unit, precision, and formatting to keep Nova forms clear, readable, and consistent across your resources.
composer require <vendor>/nova-unit-field. This is a Nova field package, so it belongs in your app's require block, not require-dev.php artisan vendor:publish --tag=nova-unit-field-assets (or nova:publish depending on the package's tags) and commit the published files, or add the publish step to your deploy script. Without this the field renders as an empty cell.kg, discount in %, duration in min):use Vendor\NovaUnitField\UnitField;
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
UnitField::make('Weight', 'weight')
->unit('kg')
->precision(2)
->rules('required', 'numeric', 'min:0'),
];
}
->unit(), ->precision(), ->min(), ->max(), ->step() methods (these define the whole surface area), the published config file if one exists, and the dist/ bundle to confirm the asset version matches the installed package version. Then open the README's screenshot to see how the value renders on index vs. detail.v1.0.0 is an initial release, so treat the public API as provisional — pin the exact version in composer.json until you've validated it across your resources.
Repeatable field definition. If a unit type appears on many resources, wrap it:
// app/Nova/Fields/WeightField.php
class WeightField extends UnitField
{
public function __construct($name, $attribute = null, $resolveCallback = null)
{
parent::__construct($name, $attribute, $resolveCallback);
$this->unit('kg')->precision(2)->step(0.01)->rules('nullable', 'numeric', 'min:0');
}
}
This keeps unit, precision, and validation consistent and gives you one place to change when the display format changes.
Server-side validation is authoritative. The field's JS formatting (decimal separator, thousands separator, masking) is presentation only. Always attach ->rules('numeric') or a dedicated Rule object; a client that strips the mask can still post "12,5" or "abc". Consider a prepareForValidation/FormRequest cast to normalize comma decimals to dots before validation.
Display on index/detail. Numeric columns sort and filter on the raw DB value, so ->sortable() keeps working. If the package exposes ->displayUsing() or a format meta, use it for read-only views so users see 1,250.00 kg on detail while the form still edits a clean number.
Pair with nullable/default states. ->nullable() plus ->default(0) avoids the empty-string vs null ambiguity in create forms. Nova sends null for a cleared field — make sure your column and rules both accept it.
Use in actions, lenses, and inline creation. Because it's a standard Nova field, it drops into ActionFields, Lenses, and NovaResource inline-create modals without extra work — but verify the asset is loaded on the lens screens too, since some Nova versions lazy-load bundles per route.
Config-driven defaults. If the package publishes a config, set the default precision and decimal separator there rather than repeating ->precision(2) on every field, so localization is a single-file change.
composer.json constraints against your laravel/nova version before upgrading Nova — a field built for Nova 4 may silently fail to render on 5.public/vendor/<package> exists after deploy and that your build pipeline runs the publish command. Clear php artisan nova:publish caches after upgrading the package.whereBetween filters. Keep the unit in code/config or a separate column., as the decimal mark, PHP's numeric rule will reject it. Normalize in a FormRequest or prepareForValidation rather than loosening the rule to string.->precision(2) displays two decimals but does not round on save. Pair it with a decimal:2 cast or DB column type that matches, otherwise detail views and reports disagree.9.5 look like it sorts after 10.0 if you display without consistent precision. Keep precision uniform across the column.Nova::script() output in the rendered page and confirm the package's service provider is auto-discovered (php artisan package:discover).->withMeta([...]) to pass extra options to the Vue component, and override the field's component via ->component() only if you need behavior the package doesn't expose — subclassing keeps you upgradable.How can I help you explore Laravel packages today?