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

Unit Converter Laravel Package

jordanbrauer/unit-converter

A PHP library for converting between units of measurement—length, mass, volume, temperature, speed, area and more—with a clean fluent API, automatic unit resolution, and support for registering custom units. Framework-agnostic with no dependencies.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a pure-PHP, dependency-free conversion engine — there is no service provider, no facade, no config publish. Integration is one composer require plus a container binding.

composer require jordanbrauer/unit-converter

Bind a single, pre-built converter instance so you are not rebuilding the unit registry on every conversion:

// app/Providers/AppServiceProvider.php
use UnitConverter\UnitConverter;

public function register(): void
{
    $this->app->singleton(UnitConverter::class, fn () => UnitConverter::create());
}

First use case — anything where a human-facing number arrives in one unit system and must be stored or displayed in another:

use UnitConverter\UnitConverter;

$converter = app(UnitConverter::class);

$cm = $converter->convert(12)->from('in')->to('cm')->calculate(); // 30.48

Where to look first, in this order:

  1. vendor/jordanbrauer/unit-converter/src/Unit/ — the directory tree is the catalogue of supported measurements and units. Skim the folder names (Length, Mass, Volume, Time, Pressure, Speed…) before promising a feature.
  2. src/Unit/AbstractUnit.php — how a unit declares its name, symbol and scale factor relative to the family's base unit; this is your extension point.
  3. src/Registry/ — how names/symbols are resolved, and how to add your own.
  4. src/Calculator/ — the arithmetic backend (plain float vs. arbitrary precision). This is the knob you will actually need to touch in production.

In php artisan tinker, dump the registry to see exactly what strings from()/to() accept in your installed version:

app(\UnitConverter\UnitConverter::class)->getRegistry()->getUnits();

Implementation Patterns

Wrap it, don't sprinkle it. The fluent API is pleasant but leaks stringly-typed unit codes into controllers and Blade templates. Put a thin domain service in front of it so unit vocabulary lives in one place:

namespace App\Support;

use UnitConverter\UnitConverter;

class Measurements
{
    public function __construct(private UnitConverter $converter) {}

    public function toMetric(float $value, string $from, string $to): float
    {
        return $this->converter->convert($value)->from($from)->to($to)->calculate();
    }
}

This also gives you one place to add rounding, logging, and a fallback when a unit is unknown.

Normalise at the boundary, store canonically. For imports (supplier CSVs, freight manifests), convert on ingest and persist metric in the database. Keep the original value and unit in side columns for auditability; never re-derive them later from a formatted string.

$weightKg = $measurements->toMetric((float) $row['weight'], $row['unit'] ?? 'lb', 'kg');

Present via an accessor, not in the view. Conversion is a domain concern; Blade should receive a ready number.

public function getWeightImperialAttribute(): string
{
    return number_format(
        $this->converter->convert($this->weight_kg)->from('kg')->to('lb')->calculate(),
        2
    ).' lb';
}

Register custom/company units once, at boot. Industry-specific units (pallet, barrel, board-foot, "case of 24") are common. Extend the abstract unit for the relevant measurement family so the registry knows which family it belongs to, then register it on the shared instance:

namespace App\Support\Units;

use UnitConverter\Unit\AbstractUnit;

class Barrel extends AbstractUnit
{
    protected function setup(): void
    {
        $this->setName('barrel')
             ->setSymbol('bbl')
             ->setUnits(0.158987); // factor to the family's base unit
    }
}

// in AppServiceProvider::boot()
app(UnitConverter::class)->getRegistry()->register(new Barrel);

Keep registrations in boot(), after the singleton is resolved, and make the registration idempotent if your provider can run twice (queue workers, Octane).

Validate unit strings before they reach the converter. A user-facing "unit" select or an imported spreadsheet column will eventually contain "lbs " or "Lbs". Check against the registry and reject with a 422 rather than letting an exception escape as a 500:

$known = array_keys(app(UnitConverter::class)->getRegistry()->getUnits());

$request->validate([
    'unit' => ['required', Rule::in($known)],
]);

Batch conversions (e.g. re-pricing a whole catalogue): resolve the converter once outside the loop. Conversion itself is cheap; repeated UnitConverter::create() calls are not, because each one rebuilds the registry.

Money-adjacent conversions (price per lb → price per kg) should use the arbitrary-precision calculator backend and only round at the very end of the chain — see below.

Gotchas and Tips

The default calculator is float-based. For prices, tariffs, and anything that gets summed across thousands of rows, configure the arbitrary-precision calculator via a configuration object at construction time instead of fighting accumulated error afterwards:

use UnitConverter\Configuration\StandardConfiguration;
use UnitConverter\Calculator\BinaryCalculator;

$converter = UnitConverter::create(
    (new StandardConfiguration)->setCalculator(new BinaryCalculator)
);

Verify the backend is actually available in your runtime (bcmath/gmp extensions); a missing extension is a deploy-time failure, not a runtime one, so check it in your health endpoint or a test.

Round once, at the edge. Chaining kg → lb → oz and rounding at each hop multiplies error. Convert to the final target in one call, then format.

Symbol collisions and case sensitivity. Short symbols are ambiguous by nature (m meter vs. min; t tonne vs. T). Prefer full names in your own APIs and in stored data; reserve symbols for display. Assume lookups are case-sensitive until you've verified otherwise in the registry, and normalise user input with trim()/strtolower() at the boundary.

Affine units need a real test, not an assumption. Temperature (and anything with an offset rather than a pure scale factor) is the classic failure mode in conversion libraries. Write an explicit test asserting 0 °C = 32 °F and 100 °C = 212 °F against the version you pin. If the installed version only multiplies scale factors, you must handle offsets yourself — do not discover this in production.

No compound or derived units. There is no kg/m³ → lb/ft³ or m/s → mph in one call. Compute the numerator and denominator separately and combine the results; wrap that in your own service method so callers don't reimplement the algebra.

No parsing of human strings. "5 ft 3 in", "1,5 kg", "3.2e1 lb" are not understood. Parse and normalise to a numeric value plus a clean unit code before calling the converter. Mixed-unit strings must be summed manually.

Unknown-unit failures are exceptions, not nulls. Wrap conversions coming from untrusted input in a try/catch (or pre-validate, as above) and map to a domain-level "unsupported unit" result. Log the offending string — that log line is how you discover which units your users actually need.

Registry collisions throw. Registering a unit whose name or symbol already exists will blow up on boot. Prefix company-specific symbols (x-pallet) or guard registration with an existence check.

Pin the version and own your test suite. This is a small, low-traffic package — treat it as a dependency you are responsible for. Lock the major version in composer.json, and cover the conversion pairs your business actually depends on with a few dozen data-provider assertions. That test file is your upgrade safety net and documents your unit vocabulary at the same time.

Extension point summary: custom units via AbstractUnit + getRegistry()->register(); custom arithmetic via a CalculatorInterface implementation passed through the configuration object; everything else is composition in your own service layer.

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.
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
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle