How to add a custom shipping calculator?
Sylius comes with several built-in shipping fee calculators like flat rate or per unit, but in real-world projects, you often need more domain-specific logic.
This guide walks you through building a custom shipping calculator that multiplies the total shipment weight by a per-channel shipping rate.
Use Case
We want to define shipping prices that scale with the total weight of the shipment. For example, if:
A product weighs 10 (unit-less),
The per-weight unit rate is $10.00 for a channel,
Then the shipment cost =
10 × $10 = $100.
1. Create the Calculator Class
<?php
// src/Shipping/Calculator/WeightBasedRateCalculator.php
namespace App\Shipping\Calculator;
use Sylius\Component\Core\Exception\MissingChannelConfigurationException;
use Sylius\Component\Shipping\Calculator\CalculatorInterface;
use Sylius\Component\Shipping\Model\ShipmentInterface;
use Webmozart\Assert\Assert;
final class WeightBasedRateCalculator implements CalculatorInterface
{
public function calculate(ShipmentInterface $subject, array $configuration): int
{
Assert::isInstanceOf($subject, \Sylius\Component\Core\Model\ShipmentInterface::class);
$channelCode = $subject->getOrder()->getChannel()->getCode();
if (!isset($configuration[$channelCode])) {
throw new MissingChannelConfigurationException(sprintf(
'Channel %s has no amount defined for shipping method %s',
$subject->getOrder()->getChannel()->getName(),
$subject->getMethod()->getName(),
));
}
$rate = (int) $configuration[$channelCode]['amount'];
$totalWeight = array_sum(array_map(
fn($unit) => $unit->getShippable()->getWeight(),
iterator_to_array($subject->getUnits())
));
return $rate * $totalWeight;
}
public function getType(): string
{
return 'weight_based_rate';
}
}2. Register the Calculator as a Service
3. Create the Configuration Form Type
4. Register the Form Type
5. Add translations
Example Setup
Product "Adventurous Aurora Cap" has a weight of 10.

Shipping method "UPS" uses the new
Rate per weight unitcalculator. Admin sets $10.00 per weight unit for the channel.

At checkout, the shipping cost is calculated as
10 × $10 = $100.

Last updated
Was this helpful?
