Introduction
Shopware 6.7 introduces significant enhancements to its commerce platform, particularly in the area of shipping method customization and rate calculation. As e-commerce businesses continue to grow in complexity, the ability to implement custom shipping logic becomes crucial for maintaining competitive advantage. This technical deep dive explores how to create a custom shipping method with sophisticated rate calculation capabilities in Shopware 6.7.
Understanding Shopware 6.7 Shipping Architecture
Before diving into implementation details, it's essential to understand the core architecture of Shopware 6.7's shipping system. The platform utilizes a robust event-driven architecture where shipping methods are registered as services and can be extended through various hooks and events.
The shipping calculation process in Shopware 6.7 follows these key steps:
- Cart validation and preparation
- Shipping method selection
- Rate calculation based on configured rules
- Tax calculation integration
- Final shipping cost application
Setting Up the Custom Shipping Method
Service Registration
To implement a custom shipping method, we first need to register our service in the dependency injection container. Create a new service class that extends the core shipping functionality:
<?php declare(strict_types=1);
namespace MyCompany\CustomShipping\Service;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryInformation;
use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\PriceCollection;
use Shopware\Core\Checkout\Shipping\Aggregate\ShippingMethodTranslation\ShippingMethodTranslationDefinition;
use Shopware\Core\Checkout\Shipping\ShippingMethodEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
class CustomShippingService
{
public function calculateCustomRate(
Cart $cart,
ShippingMethodEntity $shippingMethod,
SalesChannelContext $context
): ?CalculatedPrice {
// Implementation details will follow
}
}
Plugin Structure
Create the necessary plugin structure in your custom plugin directory:
# config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/platform/master/src/Core/System/SystemConfig/Schema/config.xsd">
<elements>
<element name="customShippingBaseFee" type="number">
<label>Base Fee</label>
<helpText>Base shipping fee for all orders</helpText>
</element>
<element name="customShippingWeightRate" type="number">
<label>Weight Rate</label>
<helpText>Additional fee per kilogram</helpText>
</element>
</elements>
</config>
Advanced Rate Calculation Logic
Weight-Based Pricing Implementation
The core of our custom shipping method lies in the rate calculation logic. Here's how we implement sophisticated weight-based pricing:
public function calculateCustomRate(
Cart $cart,
ShippingMethodEntity $shippingMethod,
SalesChannelContext $context
): ?CalculatedPrice {
$cartItems = $cart->getLineItems();
$totalWeight = 0;
// Calculate total weight from cart items
foreach ($cartItems as $item) {
if ($item->getPayloadValue('weight')) {
$totalWeight += $item->getQuantity() * $item->getPayloadValue('weight');
}
}
// Retrieve configuration values
$baseFee = (float)$this->systemConfigService->get('MyCompanyCustomShipping.config.customShippingBaseFee');
$weightRate = (float)$this->systemConfigService->get('MyCompanyCustomShipping.config.customShippingWeightRate');
// Apply tiered pricing based on weight
$shippingCost = $baseFee;
if ($totalWeight > 0) {
$shippingCost += $totalWeight * $weightRate;
}
// Apply dynamic adjustments based on delivery zone
$deliveryZone = $this->getDeliveryZone($context);
$shippingCost = $this->applyZoneAdjustments($shippingCost, $deliveryZone);
// Apply tax calculation
$taxRate = $context->getTaxRules()->getTaxRateForPriceIncl($shippingMethod->getTaxId());
return new CalculatedPrice(
$shippingCost,
$shippingCost,
new PriceCollection(),
$this->calculateTax($shippingCost, $taxRate),
1
);
}
Complex Pricing Rules
Shopware 6.7 supports advanced rule-based pricing through its RuleBuilder system. We can implement complex pricing logic that considers multiple factors:
private function applyAdvancedPricingRules(
float $basePrice,
Cart $cart,
SalesChannelContext $context
): float {
$rules = [
'customer_group' => $context->getCustomer()->getGroupId(),
'order_amount' => $cart->getPrice()->getTotalPrice(),
'shipping_country' => $context->getShippingLocation()->getCountry()->getId(),
'product_categories' => $this->extractProductCategories($cart),
];
// Evaluate rules and apply discounts
foreach ($this->pricingRules as $rule) {
if ($this->evaluateRule($rule, $rules)) {
$basePrice = $this->applyRuleDiscount($basePrice, $rule);
}
}
return $basePrice;
}
Integration with Cart Events
Event Subscriber Implementation
To ensure our custom shipping method integrates seamlessly with the checkout process, we need to implement event subscribers:
<?php declare(strict_types=1);
namespace MyCompany\CustomShipping\Event;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\Event\BeforeCalculatePriceEvent;
use Shopware\Core\Framework\Context;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ShippingCalculationSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
BeforeCalculatePriceEvent::class => 'onBeforeCalculatePrice',
];
}
public function onBeforeCalculatePrice(BeforeCalculatePriceEvent $event): void
{
$cart = $event->getCart();
$context = $event->getSalesChannelContext();
// Custom logic to validate shipping eligibility
if ($this->shouldApplyCustomShipping($cart, $context)) {
$this->applyCustomShippingLogic($cart, $context);
}
}
private function shouldApplyCustomShipping(Cart $cart, Context $context): bool
{
// Logic to determine when custom shipping should be applied
return true;
}
}
Database Integration and Configuration
Shipping Method Definition
To register our custom shipping method with Shopware's database:
// In your plugin's migration
public function update(Connection $connection): void
{
$connection->insert('shipping_method', [
'id' => Uuid::randomHex(),
'name' => 'Custom Express Shipping',
'description' => 'Advanced custom shipping with rate calculation',
'active' => true,
'position' => 10,
'tax_id' => $this->getTaxId($connection),
'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_FORMAT),
]);
}
Configuration Management
Shopware 6.7 provides robust configuration management through the SystemConfigService:
public function getShippingConfiguration(
SalesChannelContext $context
): array {
return [
'base_fee' => $this->systemConfigService->get('MyCompanyCustomShipping.config.customShippingBaseFee', $context->getSalesChannelId()),
'weight_rate' => $this->systemConfigService->get('MyCompanyCustomShipping.config.customShippingWeightRate', $context->getSalesChannelId()),
'minimum_order_value' => $this->systemConfigService->get('MyCompanyCustomShipping.config.minimumOrderValue', $context->getSalesChannelId()),
'maximum_weight' => $this->systemConfigService->get('MyCompanyCustomShipping.config.maximumWeight', $context->getSalesChannelId()),
];
}
Performance Optimization
Caching Strategies
Given that shipping calculations occur frequently during checkout, performance optimization is crucial:
private function calculateWithCaching(
Cart $cart,
ShippingMethodEntity $shippingMethod,
SalesChannelContext $context
): ?CalculatedPrice {
$cacheKey = $this->generateCacheKey($cart, $shippingMethod, $context);
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$result = $this->calculateRate($cart, $shippingMethod, $context);
// Cache for 1 hour
$this->cache->set($cacheKey, $result, 3600);
return $result;
}
Asynchronous Processing
For complex calculations, consider implementing asynchronous processing:
public function calculateAsync(
Cart $cart,
ShippingMethodEntity $shippingMethod,
SalesChannelContext $context
): PromiseInterface {
return $this->asyncExecutor->execute(function() use ($cart, $shippingMethod, $context) {
return $this->calculateRate($cart, $shippingMethod, $context);
});
}
Testing and Validation
Unit Testing
Comprehensive testing ensures our custom shipping method works correctly:
public function testWeightBasedCalculation(): void
{
$cart = $this->createMockCart();
$shippingMethod = $this->createMockShippingMethod();
$context = $this->createMockContext();
// Test with 5kg weight
$result = $this->customShippingService->calculateCustomRate($cart, $shippingMethod, $context);
$this->assertInstanceOf(CalculatedPrice::class, $result);
$this->assertEquals(15.0, $result->getTotalPrice());
}
Integration Testing
Testing within the full Shopware environment ensures compatibility:
public function testShippingMethodIntegration(): void
{
$this->client->request('POST', '/api/v3/checkout/cart/line-item', [
'data' => [
'type' => 'line_item',
'attributes' => [
'referencedId' => $productId,
'quantity' => 2,
'payload' => ['weight' => 1.5]
]
]
]);
$response = $this->client->request('GET', '/api/v3/checkout/cart');
$cart = json_decode($response->getContent(), true);
$this->assertArrayHasKey('deliveries', $cart['data']);
}
Conclusion
Implementing a custom shipping method with rate calculation in Shopware 6.7 requires understanding of the platform's architecture, event system, and configuration management. By leveraging the enhanced features introduced in version 6.7, developers can create sophisticated shipping solutions that adapt to business requirements while maintaining optimal performance.
The key advantages of this approach include:
- Flexible configuration through the admin panel
- Integration with existing Shopware rules and tax systems
- Performance optimization through caching and asynchronous processing
- Comprehensive testing capabilities for reliability
As e-commerce continues to evolve, custom shipping methods will remain essential for businesses seeking to optimize their logistics operations and provide competitive delivery options to customers. Shopware 6.7's enhanced capabilities make it easier than ever to implement these sophisticated solutions while maintaining the platform's robustness and scalability.