Weight-Based Rates
Weight Rate Calculator
namespace Vendor\Shipping\Rate\Calculation;
class WeightRateCalculator
{
private array $tiers = [
['min' => 0, 'max' => 1, 'rate' => 5.00],
['min' => 1, 'max' => 5, 'rate' => 10.00],
['min' => 5, 'max' => 10, 'rate' => 15.00],
['min' => 10, 'max' => 20, 'rate' => 25.00],
['min' => 20, 'max' => 999, 'rate' => 35.00],
];
public function calculate(float $weight): float
{
foreach ($this->tiers as $tier) {
if ($weight >= $tier['min'] && $weight < $tier['max']) {
return $tier['rate'];
}
}
return end($this->tiers)['rate'];
}
}
Per-Unit Weight Rate
namespace Vendor\Shipping\Rate\Calculation;
class PerUnitWeightRate
{
private float $ratePerLb = 2.50;
private float $minimumCharge = 5.00;
public function calculate(array $items): float
{
$totalWeight = 0;
foreach ($items as $item) {
$totalWeight += $item->getWeight() * $item->getQty();
}
return max($this->minimumCharge, $totalWeight * $this->ratePerLb);
}
}
Weight-Based with Zones
namespace Vendor\Shipping\Rate\Calculation;
class WeightZoneRate
{
private array $zones = [
'local' => [
'countries' => ['US'],
'tiers' => [
['min' => 0, 'max' => 5, 'rate' => 5.00],
['min' => 5, 'max' => 20, 'rate' => 10.00],
],
],
'domestic' => [
'countries' => ['CA', 'MX'],
'tiers' => [
['min' => 0, 'max' => 5, 'rate' => 15.00],
['min' => 5, 'max' => 20, 'rate' => 25.00],
],
],
'international' => [
'countries' => ['*'],
'tiers' => [
['min' => 0, 'max' => 5, 'rate' => 25.00],
['min' => 5, 'max' => 20, 'rate' => 45.00],
],
],
];
public function calculate(float $weight, string $country): float
{
$zone = $this->getZone($country);
foreach ($this->zones[$zone]['tiers'] as $tier) {
if ($weight >= $tier['min'] && $weight < $tier['max']) {
return $tier['rate'];
}
}
return 0;
}
private function getZone(string $country): string
{
foreach ($this->zones as $zone => $config) {
if (in_array($country, $config['countries']) || in_array('*', $config['countries'])) {
return $zone;
}
}
return 'international';
}
}
Price-Based Rates
Price Rate Calculator
namespace Vendor\Shipping\Rate\Calculation;
class PriceRateCalculator
{
private array $tiers = [
['min' => 0, 'max' => 50, 'rate' => 5.99],
['min' => 50, 'max' => 100, 'rate' => 9.99],
['min' => 100, 'max' => 200, 'rate' => 14.99],
['min' => 200, 'max' => 500, 'rate' => 19.99],
['min' => 500, 'max' => PHP_FLOAT_MAX, 'rate' => 0.00],
];
public function calculate(float $cartTotal): float
{
foreach ($this->tiers as $tier) {
if ($cartTotal >= $tier['min'] && $cartTotal < $tier['max']) {
return $tier['rate'];
}
}
return 0;
}
}
Percentage-Based Rate
namespace Vendor\Shipping\Rate\Calculation;
class PercentageRate
{
private float $percentage = 10.0; // 10% of cart total
private float $minimumCharge = 5.00;
private float $maximumCharge = 50.00;
public function calculate(float $cartTotal): float
{
$rate = $cartTotal * ($this->percentage / 100);
return max($this->minimumCharge, min($this->maximumCharge, $rate));
}
}
Free Shipping Threshold
namespace Vendor\Shipping\Rate\Calculation;
class FreeShippingThreshold
{
private float $threshold = 50.00;
public function calculate(
float $cartTotal,
float $baseRate
): float {
if ($cartTotal >= $this->threshold) {
return 0.00;
}
return $baseRate;
}
}
Destination-Based Rates
Zone-Based Rate Calculator
namespace Vendor\Shipping\Rate\Calculation;
class DestinationZoneRate
{
private array $zones = [
'zone_1' => [
'countries' => ['US'],
'rate' => 5.00,
],
'zone_2' => [
'countries' => ['CA', 'MX'],
'rate' => 15.00,
],
'zone_3' => [
'countries' => ['GB', 'DE', 'FR'],
'rate' => 25.00,
],
'zone_4' => [
'countries' => ['*'],
'rate' => 35.00,
],
];
public function calculate(string $country): float
{
foreach ($this->zones as $zone) {
if (in_array($country, $zone['countries'])) {
return $zone['rate'];
}
}
return 35.00; // Default international
}
}
Region-Based Rate
namespace Vendor\Shipping\Rate\Calculation;
class RegionRate
{
private array $regionRates = [
'US-CA' => 10.00,
'US-NY' => 12.00,
'US-TX' => 11.00,
'CA-ON' => 15.00,
'CA-BC' => 18.00,
];
public function calculate(string $countryId, string $regionCode): float
{
$key = $countryId . '-' . $regionCode;
return $this->regionRates[$key] ?? 25.00;
}
}
Zip Code Range Rate
namespace Vendor\Shipping\Rate\Calculation;
class ZipCodeRangeRate
{
private array $ranges = [
['from' => '00000', 'to' => '29999', 'rate' => 5.00],
['from' => '30000', 'to' => '59999', 'rate' => 8.00],
['from' => '60000', 'to' => '89999', 'rate' => 10.00],
['from' => '90000', 'to' => '99999', 'rate' => 12.00],
];
public function calculate(string $zipCode): float
{
foreach ($this->ranges as $range) {
if ($zipCode >= $range['from'] && $zipCode <= $range['to']) {
return $range['rate'];
}
}
return 15.00;
}
}
Table Rates
Table Rate Implementation
namespace Vendor\Shipping\Rate\TableRate;
class TableRateCalculator
{
private array $tableRates = [];
public function loadFromCsv(string $csvPath): void
{
$handle = fopen($csvPath, 'r');
while (($data = fgetcsv($handle)) !== false) {
$this->tableRates[] = [
'country' => $data[0],
'region' => $data[1],
'zip' => $data[2],
'weight' => (float) $data[3],
'price' => (float) $data[4],
'rate' => (float) $data[5],
];
}
fclose($handle);
}
public function calculate(
string $country,
string $region,
string $zip,
float $weight,
float $price
): float {
foreach ($this->tableRates as $row) {
if ($this->matches($row, $country, $region, $zip, $weight, $price)) {
return $row['rate'];
}
}
return 0;
}
private function matches(
array $row,
string $country,
string $region,
string $zip,
float $weight,
float $price
): bool {
if ($row['country'] !== '*' && $row['country'] !== $country) {
return false;
}
if ($row['region'] !== '*' && $row['region'] !== $region) {
return false;
}
if ($row['zip'] !== '*' && strpos($zip, $row['zip']) !== 0) {
return false;
}
if ($row['weight'] > 0 && $weight > $row['weight']) {
return false;
}
if ($row['price'] > 0 && $price > $row['price']) {
return false;
}
return true;
}
}
CSV Format
country,region,zip,weight,price,rate
US,*,*,10,0,5.00
US,*,*,50,0,10.00
US,CA,*,0,100,8.00
CA,*,*,20,0,15.00
*,*,*,0,0,25.00
Magento Table Rate Export
namespace Vendor\Shipping\Rate\TableRate\Import;
class TableRateImport
{
public function importFromMagento(array $rates): void
{
foreach ($rates as $rate) {
$this->tableRateCalculator->addRow([
'country' => $rate->getCountryId(),
'region' => $rate->getRegionId() ?: '*',
'zip' => $rate->getZip() ?: '*',
'weight' => $rate->getWeight() ?: 0,
'price' => $rate->getPrice() ?: 0,
'rate' => $rate->getCost(),
]);
}
}
}
Quiz
1. What is the advantage of table rates?
2. How do you implement free shipping above a threshold?
3. What does a zone define in shipping?
Flashcards
Question
What are the main rate calculation types?
Click to reveal answer
Answer
Weight-based, price-based, destination-based, table rates
Question
How do table rates work?
Click to reveal answer
Answer
CSV with country/region/zip/weight/price → rate mappings
Question
What is a shipping zone?
Click to reveal answer
Answer
Geographic area with specific rate rules
Question
How to implement free shipping?
Click to reveal answer
Answer
Check threshold and return rate of 0
Revision Notes
Key Takeaways
- 1. Weight-based rates use product/package weight for calculation
- 2. Price-based rates use cart subtotal for rating
- 3. Destination zones group countries/regions with same rates
- 4. Table rates provide maximum flexibility via CSV import
- 5. Free shipping thresholds encourage higher order values
Interview Tips
- • Explain weight vs price rate calculation trade-offs
- • Discuss how to design shipping zones
- • Describe table rate implementation approach
- • Talk about combining multiple rate factors
Cheat Sheet
Rate Types:
Weight → rate × weight
Price → rate × % of total
Destination → zone-based
Table → CSV mapping
Zones:
Local → same country
Domestic → neighboring countries
International → rest of world
Table Rate CSV:
country,region,zip,weight,price,rate
* = wildcard (any value)