Extending Ibexa Commerce - Shipping cost calculator

How to dynamically calculate shipping costs?
Ibexa Commerce Transport API

21 Aug 2024

How to dynamically calculate shipping costs?

The official documentation that explains how to create shipping methods in Ibexa Commerce omits a crucial explanation: how to calculate the fees for the method in question?

So I'm going to fill this gap and explain how to create a fee calculator for a shipping method. I'll take as an example a simple case where we have products with free shipping:

  • if all the products in the order have free shipping, the shipping cost of the order will be 0;
  • otherwise the shipping cost will be the amount configured in the shipping method.

Like many other things in Ibexa or Symfony in general, this calculator will be a tagged service.

Here is the class src/Commerce/Attribute/String/StringStorageDefinition.php :

   

<?php 
declare(strict_types=1); 
namespace App\Commerce\Shipping; 
use Ibexa\Contracts\Cart\Value\CartInterface;
use Ibexa\Contracts\ProductCatalog\CurrencyServiceInterface;
use Ibexa\Contracts\ProductCatalog\Values\AttributeInterface;
use Ibexa\Contracts\Shipping\ShippingMethod\CostCalculatorInterface;
use Ibexa\Contracts\Shipping\Value\ShippingMethod\ShippingMethodInterface;
use Ibexa\ProductCatalog\Money\DecimalMoneyFactory;
use Money\Currency;
use Money\Money; 
class CostCalculator implements CostCalculatorInterface
{
   public function __construct(
       private readonly DecimalMoneyFactory $decimalMoneyFactory,
       private readonly CurrencyServiceInterface $currencyService
   ) {
   } 
   public function calculate(ShippingMethodInterface $method, CartInterface $cart): Money
   {
       $isFree = true; 
       // Check for each item in the cart if it has free shipping
       foreach ($cart->getEntries() as $entry) {
           /** @var array<AttributeInterface> $attributes */
           $attributes = iterator_to_array($entry->getProduct()->getAttributes());
           if (!$attributes['free_shipping']->getValue()) {
               $isFree = false;
               break;
           }
       } 
       // Get shipping method configuration
       $price = $method->getOptions()->get('price');
       $currencyId = $method->getOptions()->get('currency'); 
       $currency = $this->currencyService->getCurrency($currencyId); 
       // All items have free shipping, return a amount of 0
       if ($isFree) {
           return new Money('0', new Currency($currency->getCode()));
       } 
       return $this->decimalMoneyFactory->getMoneyParser()->parse(
           $price,
           new Currency($currency->getCode())
       );
   }
} 
   

All that remains is to tag our service in configuration file config/services.yaml :

   

    App\Commerce\Shipping\CostCalculator:
       tags:
           - name: ibexa.shipping.shipping.cost_calculator
             method: custom_method # custom_method à remplacer par l'identifiant de la méthode de livraison