Skip to main content

FromAndToMustSameTypeException

Thrown when attempting to convert a value between units of different types.

Namespace

JobMetric\UnitConverter\Exceptions\FromAndToMustSameTypeException

HTTP Status Code

400 Bad Request

Constructor

public function __construct(
int $code = 400,
?Throwable $previous = null
)

Parameters

ParameterTypeDescription
$codeintHTTP status code (default: 400)
$previousThrowable|nullPrevious exception for chaining

When Is It Thrown?

This exception is thrown when you try to convert a value between units that belong to different measurement types. Unit conversion only makes sense within the same type family.

Understanding Unit Types

Each unit belongs to a specific type:

TypeExample Units
weightgram, kilogram, pound
lengthmeter, centimeter, inch
volumeliter, milliliter, gallon
temperatureCelsius, Fahrenheit, Kelvin

Invalid Conversion Examples

use JobMetric\UnitConverter\Facades\UnitConverter;

// Cannot convert weight to length
UnitConverter::convert($kilogramId, $meterId, 5);
// Throws: FromAndToMustSameTypeException

// Cannot convert volume to temperature
UnitConverter::convert($literId, $celsiusId, 100);
// Throws: FromAndToMustSameTypeException

// Cannot convert currency to data storage
UnitConverter::convert($usdId, $gigabyteId, 50);
// Throws: FromAndToMustSameTypeException

Valid Conversion Examples

use JobMetric\UnitConverter\Facades\UnitConverter;

// Weight to weight: OK
UnitConverter::convert($kilogramId, $gramId, 5);
// Returns: 5000

// Length to length: OK
UnitConverter::convert($meterId, $centimeterId, 2);
// Returns: 200

// Currency to currency: OK
UnitConverter::convert($usdId, $eurId, 100);
// Returns: ~92 (depending on exchange rate)

Handling the Exception

In Controllers

use JobMetric\UnitConverter\Exceptions\FromAndToMustSameTypeException;
use JobMetric\UnitConverter\Facades\UnitConverter;

class ConversionController extends Controller
{
public function convert(Request $request)
{
try {
$result = UnitConverter::convert(
$request->from_unit_id,
$request->to_unit_id,
$request->value
);

return response()->json([
'success' => true,
'result' => $result,
]);
} catch (FromAndToMustSameTypeException $e) {
return response()->json([
'error' => 'incompatible_units',
'message' => 'Cannot convert between different unit types',
], 400);
}
}
}

In API Resources

use JobMetric\UnitConverter\Exceptions\FromAndToMustSameTypeException;

class ProductResource extends JsonResource
{
public function toArray($request): array
{
$weightInPounds = null;

try {
if ($this->hasUnit('weight')) {
$weightInPounds = $this->getUnit('weight', $this->poundUnitId());
}
} catch (FromAndToMustSameTypeException $e) {
// Log the error, unit type mismatch
Log::error('Weight conversion failed', [
'product_id' => $this->id,
'error' => $e->getMessage(),
]);
}

return [
'id' => $this->id,
'name' => $this->name,
'weight_lbs' => $weightInPounds['value'] ?? null,
];
}
}

Global Exception Handler

use JobMetric\UnitConverter\Exceptions\FromAndToMustSameTypeException;

public function register(): void
{
$this->renderable(function (FromAndToMustSameTypeException $e, $request) {
if ($request->expectsJson()) {
return response()->json([
'error' => 'incompatible_units',
'message' => 'Units must be of the same type for conversion',
'hint' => 'Ensure both units belong to the same measurement type (e.g., both weight, both length)',
], 400);
}

return redirect()->back()->withErrors([
'conversion' => 'Cannot convert between different unit types',
]);
});
}

Prevention

1. Validate Unit Types Before Conversion

use JobMetric\UnitConverter\Models\Unit;
use JobMetric\UnitConverter\Facades\UnitConverter;

$fromUnit = Unit::find($fromUnitId);
$toUnit = Unit::find($toUnitId);

if ($fromUnit && $toUnit && $fromUnit->type === $toUnit->type) {
$result = UnitConverter::convert($fromUnitId, $toUnitId, $value);
} else {
// Handle: incompatible types
}

2. Use Form Request Validation

use JobMetric\UnitConverter\Models\Unit;

class ConvertRequest extends FormRequest
{
public function rules(): array
{
return [
'from_unit_id' => 'required|integer|exists:units,id',
'to_unit_id' => 'required|integer|exists:units,id',
'value' => 'required|numeric',
];
}

public function withValidator($validator): void
{
$validator->after(function ($validator) {
$fromUnit = Unit::find($this->from_unit_id);
$toUnit = Unit::find($this->to_unit_id);

if ($fromUnit && $toUnit && $fromUnit->type !== $toUnit->type) {
$validator->errors()->add(
'to_unit_id',
"Cannot convert from {$fromUnit->type} to {$toUnit->type}"
);
}
});
}
}

3. Filter Units by Type in UI

When building a conversion UI, filter the target units by the source unit's type:

// Controller
public function getCompatibleUnits(int $unitId)
{
$unit = Unit::find($unitId);

if (!$unit) {
return response()->json([]);
}

$compatibleUnits = Unit::where('type', $unit->type)
->where('id', '!=', $unitId)
->where('status', true)
->with('translations')
->get();

return response()->json($compatibleUnits);
}

4. Use Model's getUnit with Type Safety

The getUnit() method on models with HasUnit trait validates type compatibility:

class Product extends Model
{
use HasUnit;

protected array $unitables = [
'weight' => 'weight', // Only accepts weight type units
];
}

$product = Product::find(1);

// This validates that $targetUnitId is a weight unit
$converted = $product->getUnit('weight', $targetUnitId);

The Conversion Formula

Understanding why types must match:

result = value × (from_unit.value / to_unit.value)

This formula only makes mathematical sense when both units measure the same physical quantity. For example:

  • Valid: 5 kg → grams: 5 × (1000 / 1) = 5000 g
  • Invalid: 5 kg → meters: Makes no physical sense