I want to solve two problems:
- Share data between existing methods
- Less coupling this methods
What pattern I can use for calculating totals?
/** @var array $orderData */
// Ordered products total price
$orderedProductsTotal = $this->orderedProductsTotalPrice($orderData);
$total = $orderedProductsTotal;
// Shipping cost dependent on ordered products total price
$shippingCost = $this->shippingCost($orderedProductsTotal);
$total += $shippingCost;
// Client allowed credit, depends on ordered products total price
$total -= $this->credit($orderedProductsTotal);
// Available coupon depends on order data (i.e. special products in order, etc)
$total -= $this->coupon($orderData);
// Client personal discount applying on total amount, excluding shipping cost
$total -= $this->personalDiscount($total - $shippingCost);
I thinking about something like this
$calcs = [
'OrderProductsCalc',
'ShippingCalc',
'CreaditCalc',
'CouponCalc',
'PersonalDiscountCalc',
];
$total = 0;
foreach ($calcs as $v) {
// How to share data between calculators?
// Separated calculated total values of each calculator
// Order data
// Something else
/** @var CalculatorInterface $calculator */
$calculator = $this->container->get($v);
$total = $calculator->getTotal($total);
}
This is a simple class dependents on Cart items
class OrderProductsCalc implements CalcInterface
{
private Cart $cart;
public function __construct(Cart $cart)
{
$this->cart = $cart;
}
public function getTotal(float $total): float
{
$subTotal = $this->cart->getTotal();
return $total + $subTotal;
}
}
ShippingCalc
is simple too as it run next to OrderProductsCalc
.
But CreaditCalc::getTotal()
should receive as $total
the $subTotal
inner value from OrderProductsCalc::getTotal()
.
class CreaditCalc implements CalcInterface
{
private $customer;
public function __construct(Customer $customer)
{
$this->customer = $customer;
}
/**
* @var float $total The $subTotal inner value from OrderProductsCalc::getTotal()
*/
public function getTotal(float $total): float
{
$balance = $this->customer->getBalance();
if ($balance) {
$credit = min($balance, $total);
if ($credit > 0) {
$total -= $credit;
}
}
return $total;
}
}