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 unit calculator. Admin sets $10.00 per weight unit for the channel.

circle-info

💡 The amount is defined in the smallest currency unit (e.g., cents for USD/EUR). If you configure $10.00, Sylius stores it as 1000.

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


circle-check

🧠 Notes

Last updated

Was this helpful?